Move ImGui setup to common, and enable fullscreen UI in Qt

This commit is contained in:
Connor McLaughlin 2021-02-22 02:38:16 +10:00
parent d0f6ff03a5
commit 8318cdb3c1
16 changed files with 319 additions and 216 deletions

View File

@ -354,7 +354,7 @@ void AndroidHostInterface::EmulationThreadEntryPoint(JNIEnv* env, jobject emulat
m_emulation_activity_object = emulation_activity;
m_emulation_thread_id = std::this_thread::get_id();
}
CreateImGuiContext();
ApplySettings(true);
// Boot system.
@ -400,8 +400,6 @@ void AndroidHostInterface::EmulationThreadEntryPoint(JNIEnv* env, jobject emulat
}
env->CallVoidMethod(emulation_activity, s_EmulationActivity_method_onEmulationStopped);
DestroyImGuiContext();
}
void AndroidHostInterface::EmulationThreadLoop(JNIEnv* env)
@ -486,6 +484,9 @@ bool AndroidHostInterface::AcquireHostDisplay()
wi.surface_width = ANativeWindow_getWidth(m_surface);
wi.surface_height = ANativeWindow_getHeight(m_surface);
// TODO: Really need a better way of determining this.
wi.surface_scale = 2.0f;
switch (g_settings.gpu_renderer)
{
case GPURenderer::HardwareVulkan:
@ -500,20 +501,20 @@ bool AndroidHostInterface::AcquireHostDisplay()
if (!m_display->CreateRenderDevice(wi, {}, g_settings.gpu_use_debug_device, g_settings.gpu_threaded_presentation) ||
!m_display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device,
g_settings.gpu_threaded_presentation) ||
!m_display->CreateImGuiContext())
g_settings.gpu_threaded_presentation))
{
m_display->DestroyRenderDevice();
m_display.reset();
return false;
}
// The alignement was set prior to booting.
// The alignment was set prior to booting.
m_display->SetDisplayAlignment(m_display_alignment);
if (!m_display->UpdateImGuiFontTexture() || !CreateHostDisplayResources())
if (!CreateHostDisplayResources())
{
ReportError("Failed to create host display resources");
ReleaseHostDisplayResources();
ReleaseHostDisplay();
return false;
}
@ -524,9 +525,11 @@ bool AndroidHostInterface::AcquireHostDisplay()
void AndroidHostInterface::ReleaseHostDisplay()
{
ReleaseHostDisplayResources();
m_display->DestroyImGuiContext();
m_display->DestroyRenderDevice();
m_display.reset();
if (m_display)
{
m_display->DestroyRenderDevice();
m_display.reset();
}
}
std::unique_ptr<AudioStream> AndroidHostInterface::CreateAudioStream(AudioBackend backend)
@ -611,6 +614,7 @@ void AndroidHostInterface::SurfaceChanged(ANativeWindow* surface, int format, in
wi.window_handle = surface;
wi.surface_width = width;
wi.surface_height = height;
wi.surface_scale = m_display->GetWindowScale();
m_display->ChangeRenderWindow(wi);
@ -628,27 +632,6 @@ void AndroidHostInterface::SetDisplayAlignment(HostDisplay::Alignment alignment)
m_display->SetDisplayAlignment(alignment);
}
void AndroidHostInterface::CreateImGuiContext()
{
ImGui::CreateContext();
const float framebuffer_scale = 2.0f;
auto& io = ImGui::GetIO();
io.IniFilename = nullptr;
io.DisplayFramebufferScale.x = framebuffer_scale;
io.DisplayFramebufferScale.y = framebuffer_scale;
ImGui::GetStyle().ScaleAllSizes(framebuffer_scale);
ImGui::StyleColorsDarker();
ImGui::AddRobotoRegularFont(15.0f * framebuffer_scale);
}
void AndroidHostInterface::DestroyImGuiContext()
{
ImGui::DestroyContext();
}
void AndroidHostInterface::SetControllerType(u32 index, std::string_view type_name)
{
ControllerType type =

View File

@ -82,9 +82,6 @@ protected:
private:
void EmulationThreadLoop(JNIEnv* env);
void CreateImGuiContext();
void DestroyImGuiContext();
void LoadSettings(SettingsInterface& si) override;
void SetVibration(bool enabled);
void UpdateVibration();

View File

@ -63,6 +63,7 @@ public:
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 float GetWindowScale() const { return m_window_info.surface_scale; }
// Position is relative to the top-left corner of the window.
ALWAYS_INLINE s32 GetMousePositionX() const { return m_mouse_position_x; }

View File

@ -11,7 +11,6 @@
#include "frontend-common/controller_interface.h"
#include "frontend-common/fullscreen_ui.h"
#include "frontend-common/icon.h"
#include "frontend-common/imgui_fullscreen.h"
#include "frontend-common/imgui_styles.h"
#include "frontend-common/ini_settings_interface.h"
#include "frontend-common/opengl_host_display.h"
@ -42,18 +41,15 @@ bool NoGUIHostInterface::Initialize()
m_settings_interface = std::make_unique<INISettingsInterface>(GetSettingsFileName());
// TODO: Make command line.
m_fullscreen_ui_enabled = true;
m_flags.force_fullscreen_ui = true;
if (!CommonHostInterface::Initialize())
return false;
CreateImGuiContext();
const bool start_fullscreen = m_command_line_flags.start_fullscreen || g_settings.start_fullscreen;
const bool start_fullscreen = m_flags.start_fullscreen || g_settings.start_fullscreen;
if (!CreatePlatformWindow(start_fullscreen))
{
Log_ErrorPrintf("Failed to create platform window");
ImGui::DestroyContext();
return false;
}
@ -61,10 +57,12 @@ bool NoGUIHostInterface::Initialize()
{
Log_ErrorPrintf("Failed to create host display");
DestroyPlatformWindow();
ImGui::DestroyContext();
return false;
}
if (m_fullscreen_ui_enabled)
FullscreenUI::QueueGameListRefresh();
// process events to pick up controllers before updating input map
PollAndUpdate();
UpdateInputMap();
@ -73,45 +71,10 @@ bool NoGUIHostInterface::Initialize()
void NoGUIHostInterface::Shutdown()
{
CommonHostInterface::Shutdown();
if (m_display)
{
DestroyDisplay();
ImGui::DestroyContext();
}
DestroyDisplay();
DestroyPlatformWindow();
}
void NoGUIHostInterface::CreateImGuiContext()
{
ImGui::CreateContext();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
}
void NoGUIHostInterface::OnPlatformWindowResized(u32 new_width, u32 new_height, float new_scale)
{
if (new_scale != ImGui::GetIO().DisplayFramebufferScale.x)
{
ImGui::GetIO().DisplayFramebufferScale = ImVec2(new_scale, new_scale);
ImGui::GetStyle() = ImGuiStyle();
ImGui::StyleColorsDarker();
ImGui::GetStyle().ScaleAllSizes(new_scale);
}
if (ImGuiFullscreen::UpdateLayoutScale())
{
if (ImGuiFullscreen::UpdateFonts())
{
if (!m_display->UpdateImGuiFontTexture())
Panic("Failed to update font texture");
}
}
if (!System::IsShutdown())
g_gpu->UpdateResolutionScale();
CommonHostInterface::Shutdown();
}
bool NoGUIHostInterface::CreateDisplay()
@ -163,33 +126,19 @@ bool NoGUIHostInterface::CreateDisplay()
return false;
}
if (!m_display->CreateImGuiContext() ||
(m_fullscreen_ui_enabled && !FullscreenUI::Initialize(this)) ||
!m_display->UpdateImGuiFontTexture())
{
if (m_fullscreen_ui_enabled)
FullscreenUI::Shutdown();
m_display->DestroyImGuiContext();
m_display->DestroyRenderDevice();
m_display.reset();
ReportError("Failed to initialize imgui/fonts/fullscreen UI");
return false;
}
if (!CreateHostDisplayResources())
Log_WarningPrint("Failed to create host display resources");
return true;
}
void NoGUIHostInterface::DestroyDisplay()
{
if (m_fullscreen_ui_enabled)
FullscreenUI::Shutdown();
ReleaseHostDisplayResources();
if (m_display)
{
m_display->DestroyImGuiContext();
m_display->DestroyRenderDevice();
}
m_display.reset();
}
@ -235,42 +184,15 @@ bool NoGUIHostInterface::AcquireHostDisplay()
Panic("Failed to recreate display on GPU renderer switch");
}
if (!CreateHostDisplayResources())
return false;
return true;
}
void NoGUIHostInterface::ReleaseHostDisplay()
{
ReleaseHostDisplayResources();
// restore vsync, since we don't want to burn cycles at the menu
m_display->SetVSync(true);
}
void NoGUIHostInterface::OnSystemCreated()
{
CommonHostInterface::OnSystemCreated();
if (m_fullscreen_ui_enabled)
FullscreenUI::SystemCreated();
}
void NoGUIHostInterface::OnSystemPaused(bool paused)
{
CommonHostInterface::OnSystemPaused(paused);
if (m_fullscreen_ui_enabled)
FullscreenUI::SystemPaused(paused);
}
void NoGUIHostInterface::OnSystemDestroyed()
{
CommonHostInterface::OnSystemDestroyed();
ReportFormattedMessage("System shut down.");
if (m_fullscreen_ui_enabled)
FullscreenUI::SystemDestroyed();
}
void NoGUIHostInterface::OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code,
const std::string& game_title)
{

View File

@ -41,9 +41,6 @@ protected:
bool AcquireHostDisplay() override;
void ReleaseHostDisplay() override;
void OnSystemCreated() override;
void OnSystemPaused(bool paused) override;
void OnSystemDestroyed() override;
void OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code,
const std::string& game_title) override;
@ -53,11 +50,9 @@ protected:
virtual bool CreatePlatformWindow(bool fullscreen) = 0;
virtual void DestroyPlatformWindow() = 0;
virtual std::optional<WindowInfo> GetPlatformWindowInfo() = 0;
void OnPlatformWindowResized(u32 new_width, u32 new_height, float new_scale);
bool CreateDisplay();
void DestroyDisplay();
void CreateImGuiContext();
void RunCallbacks();
std::deque<std::function<void()>> m_queued_callbacks;

View File

@ -98,7 +98,7 @@ bool SDLHostInterface::SetFullscreen(bool enabled)
int window_width, window_height;
SDL_GetWindowSize(m_window, &window_width, &window_height);
m_display->ResizeRenderWindow(window_width, window_height);
OnPlatformWindowResized(window_width, window_height, GetDPIScaleFactor(m_window));
OnHostDisplayResized(window_width, window_height, GetDPIScaleFactor(m_window));
m_fullscreen = enabled;
return true;
@ -289,7 +289,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
if (event->window.event == SDL_WINDOWEVENT_RESIZED)
{
m_display->ResizeRenderWindow(event->window.data1, event->window.data2);
OnPlatformWindowResized(event->window.data1, event->window.data2, GetDPIScaleFactor(m_window));
OnHostDisplayResized(event->window.data1, event->window.data2, GetDPIScaleFactor(m_window));
}
else if (event->window.event == SDL_WINDOWEVENT_MOVED)
{

View File

@ -33,6 +33,8 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.applyGameSettings, "Main", "ApplyGameSettings",
true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.autoLoadCheats, "Main", "AutoLoadCheats", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.enableFullscreenUI, "Main", "EnableFullscreenUI",
false);
SettingWidgetBinder::BindWidgetToEnumSetting(
m_host_interface, m_ui.controllerBackend, "Main", "ControllerBackend", &ControllerInterface::ParseBackendName,
@ -74,9 +76,12 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
ControllerInterface::GetDefaultBackend())),
tr("Determines the backend which is used for controller input. Windows users may prefer "
"to use XInput over SDL2 for compatibility."));
dialog->registerWidgetHelp(
m_ui.enableFullscreenUI, tr("Enable Fullscreen UI"), tr("Unchecked"),
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.
int current_col = 0;
int current_col = 1;
int current_row = m_ui.formLayout_4->rowCount() - current_col;
#ifdef WITH_DISCORD_PRESENCE
{

View File

@ -102,6 +102,13 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="enableFullscreenUI">
<property name="text">
<string>Enable Fullscreen UI</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -10,6 +10,7 @@
#include "core/gpu.h"
#include "core/system.h"
#include "frontend-common/game_list.h"
#include "frontend-common/imgui_fullscreen.h"
#include "frontend-common/imgui_styles.h"
#include "frontend-common/ini_settings_interface.h"
#include "frontend-common/opengl_host_display.h"
@ -97,6 +98,10 @@ bool QtHostInterface::initializeOnThread()
if (!CommonHostInterface::Initialize())
return false;
// imgui setup
setImGuiFont();
setImGuiKeyMap();
// make sure the controllers have been detected
if (m_controller_interface)
m_controller_interface->PollEvents();
@ -294,7 +299,7 @@ void QtHostInterface::ApplySettings(bool display_osd_messages)
m_is_rendering_to_main = render_to_main;
updateDisplayState();
}
else
else if (!m_fullscreen_ui_enabled)
{
renderDisplay();
}
@ -367,6 +372,11 @@ void QtHostInterface::resumeSystemFromMostRecentState()
void QtHostInterface::onDisplayWindowKeyEvent(int key, bool pressed)
{
DebugAssert(isOnWorkerThread());
const u32 masked_key = static_cast<u32>(key) & IMGUI_KEY_MASK;
if (masked_key < countof(ImGuiIO::KeysDown))
ImGui::GetIO().KeysDown[masked_key] = pressed;
HandleHostKeyEvent(key, pressed);
}
@ -377,14 +387,11 @@ void QtHostInterface::onDisplayWindowMouseMoveEvent(int x, int y)
if (!m_display)
return;
m_display->SetMousePosition(x, y);
ImGuiIO& io = ImGui::GetIO();
io.MousePos[0] = static_cast<float>(x);
io.MousePos[1] = static_cast<float>(y);
if (ImGui::GetCurrentContext())
{
ImGuiIO& io = ImGui::GetIO();
io.MousePos[0] = static_cast<float>(x);
io.MousePos[1] = static_cast<float>(y);
}
m_display->SetMousePosition(x, y);
}
void QtHostInterface::onDisplayWindowMouseButtonEvent(int button, bool pressed)
@ -413,6 +420,7 @@ void QtHostInterface::onHostDisplayWindowResized(int width, int height)
return;
m_display->ResizeRenderWindow(width, height);
OnHostDisplayResized(width, height, m_display->GetWindowScale());
// re-render the display, since otherwise it will be out of date and stretched if paused
if (!System::IsShutdown())
@ -426,8 +434,9 @@ void QtHostInterface::onHostDisplayWindowResized(int width, int height)
updateDisplayState();
}
g_gpu->UpdateResolutionScale();
renderDisplay();
// force redraw if we're paused
if (!m_fullscreen_ui_enabled)
renderDisplay();
}
}
@ -470,16 +479,12 @@ bool QtHostInterface::AcquireHostDisplay()
return false;
}
createImGuiContext(display_widget->devicePixelRatioFromScreen());
if (!m_display->MakeRenderContextCurrent() ||
!m_display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device,
g_settings.gpu_threaded_presentation) ||
!m_display->CreateImGuiContext() || !m_display->UpdateImGuiFontTexture() || !CreateHostDisplayResources())
!CreateHostDisplayResources())
{
ReleaseHostDisplayResources();
m_display->DestroyImGuiContext();
destroyImGuiContext();
m_display->DestroyRenderDevice();
emit destroyDisplayRequested();
m_display.reset();
@ -545,12 +550,16 @@ void QtHostInterface::updateDisplayState()
connectDisplaySignals(display_widget);
m_is_exclusive_fullscreen = m_display->IsFullscreen();
OnHostDisplayResized(m_display->GetWindowWidth(), m_display->GetWindowHeight(), m_display->GetWindowScale());
if (!System::IsShutdown())
{
g_gpu->UpdateResolutionScale();
UpdateSoftwareCursor();
redrawDisplayWindow();
if (!m_fullscreen_ui_enabled)
redrawDisplayWindow();
}
UpdateSpeedLimiterState();
}
@ -559,9 +568,7 @@ void QtHostInterface::ReleaseHostDisplay()
Assert(m_display);
ReleaseHostDisplayResources();
m_display->DestroyImGuiContext();
m_display->DestroyRenderDevice();
destroyImGuiContext();
emit destroyDisplayRequested();
m_display.reset();
m_is_fullscreen = false;
@ -1370,32 +1377,40 @@ void QtHostInterface::threadEntryPoint()
// TODO: Event which flags the thread as ready
while (!m_shutdown_flag.load())
{
if (!System::IsRunning())
if (System::IsRunning())
{
// wait until we have a system before running
m_worker_thread_event_loop->exec();
continue;
}
if (m_display_all_frames)
System::RunFrame();
else
System::RunFrames();
if (m_display_all_frames)
System::RunFrame();
UpdateControllerRumble();
if (m_frame_step_request)
{
m_frame_step_request = false;
PauseSystem(true);
}
renderDisplay();
System::UpdatePerformanceCounters();
if (m_throttler_enabled)
System::Throttle();
}
else
System::RunFrames();
UpdateControllerRumble();
if (m_frame_step_request)
{
m_frame_step_request = false;
PauseSystem(true);
// we want to keep rendering the UI when paused and fullscreen UI is enabled
if (!m_fullscreen_ui_enabled || !System::IsValid())
{
// wait until we have a system before running
m_worker_thread_event_loop->exec();
continue;
}
renderDisplay();
}
renderDisplay();
System::UpdatePerformanceCounters();
if (m_throttler_enabled)
System::Throttle();
m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents);
PollAndUpdate();
}
@ -1447,8 +1462,10 @@ static std::string GetFontPath(const char* name)
#endif
}
static bool AddImGuiFont(const std::string& language, float size, float framebuffer_scale)
void QtHostInterface::setImGuiFont()
{
std::string language(GetStringSettingValue("Main", "Language", ""));
std::string path;
const ImWchar* range = nullptr;
#ifdef WIN32
@ -1465,34 +1482,35 @@ static bool AddImGuiFont(const std::string& language, float size, float framebuf
#endif
if (!path.empty())
{
return (ImGui::GetIO().Fonts->AddFontFromFileTTF(path.c_str(), size * framebuffer_scale, nullptr, range) !=
nullptr);
}
return false;
ImGuiFullscreen::SetFontFilename(std::move(path));
if (range)
ImGuiFullscreen::SetFontGlyphRanges(range);
}
void QtHostInterface::createImGuiContext(float framebuffer_scale)
void QtHostInterface::setImGuiKeyMap()
{
ImGui::CreateContext();
auto& io = ImGui::GetIO();
io.IniFilename = nullptr;
io.DisplayFramebufferScale.x = framebuffer_scale;
io.DisplayFramebufferScale.y = framebuffer_scale;
ImGui::GetStyle().ScaleAllSizes(framebuffer_scale);
ImGui::StyleColorsDarker();
std::string language = GetStringSettingValue("Main", "Language", "");
if (!AddImGuiFont(language, 15.0f, framebuffer_scale))
ImGui::AddRobotoRegularFont(15.0f * framebuffer_scale);
}
void QtHostInterface::destroyImGuiContext()
{
ImGui::DestroyContext();
ImGuiIO& io = ImGui::GetIO();
io.KeyMap[ImGuiKey_Tab] = Qt::Key_Tab & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_LeftArrow] = Qt::Key_Left & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_RightArrow] = Qt::Key_Right & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_UpArrow] = Qt::Key_Up & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_DownArrow] = Qt::Key_Down & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_PageUp] = Qt::Key_PageUp & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_PageDown] = Qt::Key_PageDown & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_Home] = Qt::Key_Home & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_End] = Qt::Key_End & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_Insert] = Qt::Key_Insert & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_Delete] = Qt::Key_Delete & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_Backspace] = Qt::Key_Backspace & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_Space] = Qt::Key_Space & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_Enter] = Qt::Key_Enter & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_Escape] = Qt::Key_Escape & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_A] = Qt::Key_A & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_C] = Qt::Key_C & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_V] = Qt::Key_V & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_X] = Qt::Key_X & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_Y] = Qt::Key_Y & IMGUI_KEY_MASK;
io.KeyMap[ImGuiKey_Z] = Qt::Key_Z & IMGUI_KEY_MASK;
}
TinyString QtHostInterface::TranslateString(const char* context, const char* str) const

View File

@ -213,7 +213,10 @@ private:
BACKGROUND_CONTROLLER_POLLING_INTERVAL =
100, /// Interval at which the controllers are polled when the system is not active.
SETTINGS_SAVE_DELAY = 1000
SETTINGS_SAVE_DELAY = 1000,
/// Crappy solution to the Qt indices being massive.
IMGUI_KEY_MASK = 511,
};
using InputButtonHandler = std::function<void(bool)>;
@ -242,8 +245,8 @@ private:
void startBackgroundControllerPollTimer();
void stopBackgroundControllerPollTimer();
void createImGuiContext(float framebuffer_scale);
void destroyImGuiContext();
void setImGuiFont();
void setImGuiKeyMap();
void createThread();
void stopThread();

View File

@ -25,6 +25,8 @@
#include "game_list.h"
#include "icon.h"
#include "imgui.h"
#include "imgui_fullscreen.h"
#include "imgui_styles.h"
#include "ini_settings_interface.h"
#include "save_state_selector_ui.h"
#include "scmversion/scmversion.h"
@ -84,6 +86,9 @@ bool CommonHostInterface::Initialize()
RegisterHotkeys();
UpdateControllerInterface();
CreateImGuiContext();
return true;
}
@ -91,6 +96,8 @@ void CommonHostInterface::Shutdown()
{
HostInterface::Shutdown();
ImGui::DestroyContext();
#ifdef WITH_DISCORD_PRESENCE
ShutdownDiscordPresence();
#endif
@ -139,7 +146,7 @@ void CommonHostInterface::InitializeUserDirectory()
bool CommonHostInterface::BootSystem(const SystemBootParameters& parameters)
{
// If the fullscreen UI is enabled, make sure it's finished loading the game list so we don't race it.
if (m_fullscreen_ui_enabled)
if (m_display && m_fullscreen_ui_enabled)
FullscreenUI::EnsureGameListLoaded();
if (!HostInterface::BootSystem(parameters))
@ -268,7 +275,7 @@ bool CommonHostInterface::ParseCommandLineParameters(int argc, char* argv[],
else if (CHECK_ARG("-batch"))
{
Log_InfoPrintf("Enabling batch mode.");
m_command_line_flags.batch_mode = true;
m_flags.batch_mode = true;
continue;
}
else if (CHECK_ARG("-fastboot"))
@ -286,7 +293,7 @@ bool CommonHostInterface::ParseCommandLineParameters(int argc, char* argv[],
else if (CHECK_ARG("-nocontroller"))
{
Log_InfoPrintf("Disabling controller support.");
m_command_line_flags.disable_controller_interface = true;
m_flags.disable_controller_interface = true;
continue;
}
else if (CHECK_ARG("-resume"))
@ -307,7 +314,7 @@ bool CommonHostInterface::ParseCommandLineParameters(int argc, char* argv[],
else if (CHECK_ARG("-fullscreen"))
{
Log_InfoPrintf("Going fullscreen after booting.");
m_command_line_flags.start_fullscreen = true;
m_flags.start_fullscreen = true;
force_fullscreen = true;
continue;
}
@ -444,6 +451,16 @@ bool CommonHostInterface::SetFullscreen(bool enabled)
return false;
}
void CommonHostInterface::CreateImGuiContext()
{
ImGui::CreateContext();
ImGui::GetIO().IniFilename = nullptr;
#ifndef __ANDROID__
// Android has no keyboard, nor are we using ImGui for any actual user-interactable windows.
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
#endif
}
bool CommonHostInterface::CreateHostDisplayResources()
{
m_logo_texture = m_display->CreateTexture(APP_ICON_WIDTH, APP_ICON_HEIGHT, 1, 1, 1, HostDisplayPixelFormat::RGBA8,
@ -451,14 +468,83 @@ bool CommonHostInterface::CreateHostDisplayResources()
if (!m_logo_texture)
Log_WarningPrintf("Failed to create logo texture");
const float framebuffer_scale = m_display->GetWindowScale();
ImGui::GetIO().DisplayFramebufferScale = ImVec2(framebuffer_scale, framebuffer_scale);
ImGui::GetStyle() = ImGuiStyle();
ImGui::StyleColorsDarker();
ImGui::GetStyle().ScaleAllSizes(framebuffer_scale);
if (!m_display->CreateImGuiContext())
{
Log_ErrorPrintf("Failed to create ImGui device context");
return false;
}
if (m_fullscreen_ui_enabled)
{
if (!FullscreenUI::Initialize(this))
{
Log_ErrorPrintf("Failed to initialize fullscreen UI, disabling.");
m_fullscreen_ui_enabled = false;
}
}
if (!m_fullscreen_ui_enabled)
ImGuiFullscreen::ResetFonts();
if (!m_display->UpdateImGuiFontTexture())
{
Log_ErrorPrintf("Failed to create ImGui font text");
if (m_fullscreen_ui_enabled)
FullscreenUI::Shutdown();
m_display->DestroyImGuiContext();
return false;
}
return true;
}
void CommonHostInterface::ReleaseHostDisplayResources()
{
if (m_fullscreen_ui_enabled)
FullscreenUI::Shutdown();
if (m_display)
m_display->DestroyImGuiContext();
m_logo_texture.reset();
}
void CommonHostInterface::OnHostDisplayResized(u32 new_width, u32 new_height, float new_scale)
{
if (new_scale != ImGui::GetIO().DisplayFramebufferScale.x)
{
ImGui::GetIO().DisplayFramebufferScale = ImVec2(new_scale, new_scale);
ImGui::GetStyle() = ImGuiStyle();
ImGui::StyleColorsDarker();
ImGui::GetStyle().ScaleAllSizes(new_scale);
ImGuiFullscreen::ResetFonts();
if (!m_display->UpdateImGuiFontTexture())
Panic("Failed to recreate font texture after resize");
}
if (m_fullscreen_ui_enabled)
{
if (ImGuiFullscreen::UpdateLayoutScale())
{
if (ImGuiFullscreen::UpdateFonts())
{
if (!m_display->UpdateImGuiFontTexture())
Panic("Failed to update font texture");
}
}
}
if (!System::IsShutdown())
g_gpu->UpdateResolutionScale();
}
std::unique_ptr<AudioStream> CommonHostInterface::CreateAudioStream(AudioBackend backend)
{
switch (backend)
@ -492,7 +578,7 @@ void CommonHostInterface::UpdateControllerInterface()
ControllerInterface::ParseBackendName(backend_str.c_str());
const ControllerInterface::Backend current_backend =
(m_controller_interface ? m_controller_interface->GetBackend() : ControllerInterface::Backend::None);
if (new_backend == current_backend || m_command_line_flags.disable_controller_interface)
if (new_backend == current_backend || m_flags.disable_controller_interface)
return;
if (m_controller_interface)
@ -781,6 +867,9 @@ void CommonHostInterface::OnSystemCreated()
{
HostInterface::OnSystemCreated();
if (m_fullscreen_ui_enabled)
FullscreenUI::SystemCreated();
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);
}
@ -789,6 +878,9 @@ void CommonHostInterface::OnSystemPaused(bool paused)
{
ReportFormattedMessage("System %s.", paused ? "paused" : "resumed");
if (m_fullscreen_ui_enabled)
FullscreenUI::SystemPaused(paused);
if (paused)
{
if (IsFullscreen() && !m_fullscreen_ui_enabled)
@ -808,6 +900,9 @@ void CommonHostInterface::OnSystemDestroyed()
HostInterface::OnSystemDestroyed();
if (m_fullscreen_ui_enabled)
FullscreenUI::SystemDestroyed();
StopControllerRumble();
}
@ -837,6 +932,9 @@ void CommonHostInterface::OnControllerTypeChanged(u32 slot)
void CommonHostInterface::DrawImGuiWindows()
{
if (m_save_state_selector_ui->IsOpen())
m_save_state_selector_ui->Draw();
if (m_fullscreen_ui_enabled)
{
FullscreenUI::Render();
@ -850,9 +948,6 @@ void CommonHostInterface::DrawImGuiWindows()
}
DrawOSDMessages();
if (m_save_state_selector_ui->IsOpen())
m_save_state_selector_ui->Draw();
}
void CommonHostInterface::DrawFPSWindow()
@ -2383,6 +2478,36 @@ void CommonHostInterface::LoadSettings(SettingsInterface& si)
#ifdef WITH_DISCORD_PRESENCE
SetDiscordPresenceEnabled(si.GetBoolValue("Main", "EnableDiscordPresence", false));
#endif
const bool fullscreen_ui_enabled =
si.GetBoolValue("Main", "EnableFullscreenUI", false) || m_flags.force_fullscreen_ui;
if (fullscreen_ui_enabled != m_fullscreen_ui_enabled)
{
m_fullscreen_ui_enabled = fullscreen_ui_enabled;
if (m_display)
{
if (!fullscreen_ui_enabled)
{
FullscreenUI::Shutdown();
ImGuiFullscreen::ResetFonts();
if (!m_display->UpdateImGuiFontTexture())
Panic("Failed to recreate font texture after fullscreen UI disable");
}
else
{
if (FullscreenUI::Initialize(this))
{
if (!m_display->UpdateImGuiFontTexture())
Panic("Failed to recreate font textre after fullscreen UI enable");
}
else
{
Log_ErrorPrintf("Failed to initialize fullscreen UI. Disabling.");
m_fullscreen_ui_enabled = false;
}
}
}
}
}
void CommonHostInterface::SaveSettings(SettingsInterface& si)

View File

@ -122,6 +122,7 @@ public:
virtual bool SetFullscreen(bool enabled);
virtual bool Initialize() override;
virtual void Shutdown() override;
virtual bool BootSystem(const SystemBootParameters& parameters) override;
@ -141,7 +142,7 @@ public:
ALWAYS_INLINE ControllerInterface* GetControllerInterface() const { return m_controller_interface.get(); }
/// Returns true if running in batch mode, i.e. exit after emulation.
ALWAYS_INLINE bool InBatchMode() const { return m_command_line_flags.batch_mode; }
ALWAYS_INLINE bool InBatchMode() const { return m_flags.batch_mode; }
/// Returns true if the fullscreen UI is enabled.
ALWAYS_INLINE bool IsFullscreenUIEnabled() const { return m_fullscreen_ui_enabled; }
@ -390,6 +391,7 @@ protected:
bool CreateHostDisplayResources();
void ReleaseHostDisplayResources();
void OnHostDisplayResized(u32 new_width, u32 new_height, float new_scale);
virtual void DrawImGuiWindows();
@ -430,7 +432,10 @@ protected:
// starting fullscreen (outside of boot options)
BitField<u8, bool, 2, 1> start_fullscreen;
} m_command_line_flags = {};
// force fullscreen UI enabled (nogui)
BitField<u8, bool, 3, 1> force_fullscreen_ui;
} m_flags = {};
private:
void LoadSettings();
@ -444,6 +449,7 @@ private:
bool UpdateControllerInputMapFromGameSettings();
void UpdateHotkeyInputMap(SettingsInterface& si);
void ClearAllControllerBindings();
void CreateImGuiContext();
#ifdef WITH_DISCORD_PRESENCE
void SetDiscordPresenceEnabled(bool enabled);

View File

@ -35,6 +35,8 @@ Log_SetChannel(FullscreenUI);
static constexpr float LAYOUT_MAIN_MENU_BAR_SIZE = 20.0f; // Should be DPI scaled, not layout scaled!
using ImGuiFullscreen::g_large_font;
using ImGuiFullscreen::g_layout_padding_left;
using ImGuiFullscreen::g_layout_padding_top;
using ImGuiFullscreen::g_medium_font;
using ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE;
using ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE;
@ -171,7 +173,6 @@ static bool s_save_state_selector_loading = true;
//////////////////////////////////////////////////////////////////////////
static void DrawGameListWindow();
static void SwitchToGameList();
static void QueueGameListRefresh();
static void SortGameList();
static HostDisplayTexture* GetTextureForGameListEntryType(GameListEntryType type);
static HostDisplayTexture* GetGameListCover(const GameListEntry* entry);
@ -194,12 +195,16 @@ bool Initialize(CommonHostInterface* host_interface)
s_settings_copy.Load(*s_host_interface->GetSettingsInterface());
SetDebugMenuEnabled(s_host_interface->GetSettingsInterface()->GetBoolValue("Main", "ShowDebugMenu", false));
QueueGameListRefresh();
ImGuiFullscreen::UpdateLayoutScale();
ImGuiFullscreen::UpdateFonts();
ImGuiFullscreen::SetResolveTextureFunction(ResolveTextureHandle);
if (System::IsValid())
SystemCreated();
if (System::IsPaused())
SystemPaused(true);
return true;
}

View File

@ -54,6 +54,7 @@ bool InvalidateCachedTexture(const std::string& path);
bool DrawErrorWindow(const char* message);
bool DrawConfirmWindow(const char* message, bool* result);
void QueueGameListRefresh();
void EnsureGameListLoaded();
Settings& GetSettingsCopy();

View File

@ -47,10 +47,18 @@ void SetFontFilename(const char* filename)
std::string().swap(s_font_filename);
}
void SetIconFontFilename(const char* icon_font_filename)
void SetFontFilename(std::string filename)
{
if (icon_font_filename)
s_icon_font_filename = icon_font_filename;
if (!filename.empty())
s_font_filename = std::move(filename);
else
std::string().swap(s_font_filename);
}
void SetIconFontFilename(std::string icon_font_filename)
{
if (!icon_font_filename.empty())
s_icon_font_filename = std::move(icon_font_filename);
else
std::string().swap(s_icon_font_filename);
}
@ -111,8 +119,8 @@ bool UpdateFonts()
const float medium_font_size = std::ceil(LayoutScale(LAYOUT_MEDIUM_FONT_SIZE));
const float large_font_size = std::ceil(LayoutScale(LAYOUT_LARGE_FONT_SIZE));
if (g_standard_font && g_standard_font->FontSize == standard_font_size && medium_font_size &&
g_medium_font->FontSize == medium_font_size && large_font_size && g_large_font->FontSize == large_font_size)
if (g_standard_font && g_standard_font->FontSize == standard_font_size && g_medium_font &&
g_medium_font->FontSize == medium_font_size && g_large_font && g_large_font->FontSize == large_font_size)
{
return false;
}
@ -147,6 +155,30 @@ bool UpdateFonts()
return true;
}
void ResetFonts()
{
const float standard_font_size = std::ceil(DPIScale(s_font_size));
ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
if (s_font_filename.empty())
{
g_standard_font = ImGui::AddRobotoRegularFont(standard_font_size);
}
else
{
g_standard_font =
io.Fonts->AddFontFromFileTTF(s_font_filename.c_str(), standard_font_size, nullptr, s_font_glyph_range);
}
g_medium_font = nullptr;
g_large_font = nullptr;
if (!io.Fonts->Build())
Panic("Failed to rebuild font atlas");
}
bool UpdateLayoutScale()
{
static constexpr float LAYOUT_RATIO = LAYOUT_SCREEN_WIDTH / LAYOUT_SCREEN_HEIGHT;

View File

@ -141,6 +141,9 @@ void SetResolveTextureFunction(ResolveTextureHandleCallback callback);
/// Rebuilds fonts to a new scale if needed. Returns true if fonts have changed and the texture needs updating.
bool UpdateFonts();
/// Removes the fullscreen fonts, leaving only the standard font.
void ResetFonts();
bool UpdateLayoutScale();
void BeginLayout();