mirror of
https://github.com/xemu-project/xemu.git
synced 2024-12-13 06:27:28 +00:00
332 lines
11 KiB
C++
332 lines
11 KiB
C++
//
|
|
// xemu User Interface
|
|
//
|
|
// Copyright (C) 2020-2022 Matt Borgerson
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
#include <SDL.h>
|
|
#include <epoxy/gl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <functional>
|
|
#include <assert.h>
|
|
#include <fpng.h>
|
|
|
|
#include <deque>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <memory>
|
|
|
|
#include "actions.hh"
|
|
#include "common.hh"
|
|
#include "xemu-hud.h"
|
|
#include "misc.hh"
|
|
#include "gl-helpers.hh"
|
|
#include "input-manager.hh"
|
|
#include "snapshot-manager.hh"
|
|
#include "viewport-manager.hh"
|
|
#include "font-manager.hh"
|
|
#include "scene.hh"
|
|
#include "scene-manager.hh"
|
|
#include "main-menu.hh"
|
|
#include "popup-menu.hh"
|
|
#include "notifications.hh"
|
|
#include "monitor.hh"
|
|
#include "debug.hh"
|
|
#include "welcome.hh"
|
|
#include "menubar.hh"
|
|
#include "compat.hh"
|
|
#if defined(_WIN32)
|
|
#include "update.hh"
|
|
#endif
|
|
|
|
bool g_screenshot_pending;
|
|
const char *g_snapshot_pending_load_name;
|
|
|
|
float g_main_menu_height;
|
|
|
|
static ImGuiStyle g_base_style;
|
|
static SDL_Window *g_sdl_window;
|
|
static float g_last_scale;
|
|
static int g_vsync;
|
|
static GLuint g_tex;
|
|
static bool g_flip_req;
|
|
|
|
|
|
static void InitializeStyle()
|
|
{
|
|
g_font_mgr.Rebuild();
|
|
|
|
ImGui::StyleColorsDark();
|
|
ImVec4 *c = ImGui::GetStyle().Colors;
|
|
c[ImGuiCol_Text] = ImVec4(0.94f, 0.94f, 0.94f, 1.00f);
|
|
c[ImGuiCol_TextDisabled] = ImVec4(0.86f, 0.93f, 0.89f, 0.28f);
|
|
c[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
|
|
c[ImGuiCol_ChildBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.98f);
|
|
c[ImGuiCol_PopupBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
|
|
c[ImGuiCol_Border] = ImVec4(0.11f, 0.11f, 0.11f, 0.60f);
|
|
c[ImGuiCol_BorderShadow] = ImVec4(0.16f, 0.16f, 0.16f, 0.00f);
|
|
c[ImGuiCol_FrameBg] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f);
|
|
c[ImGuiCol_FrameBgHovered] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);
|
|
c[ImGuiCol_FrameBgActive] = ImVec4(0.28f, 0.71f, 0.25f, 1.00f);
|
|
c[ImGuiCol_TitleBg] = ImVec4(0.20f, 0.51f, 0.18f, 1.00f);
|
|
c[ImGuiCol_TitleBgActive] = ImVec4(0.26f, 0.66f, 0.23f, 1.00f);
|
|
c[ImGuiCol_TitleBgCollapsed] = ImVec4(0.16f, 0.16f, 0.16f, 0.75f);
|
|
c[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 0.00f);
|
|
c[ImGuiCol_ScrollbarBg] = ImVec4(0.16f, 0.16f, 0.16f, 0.00f);
|
|
c[ImGuiCol_ScrollbarGrab] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);
|
|
c[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.24f, 0.60f, 0.00f, 1.00f);
|
|
c[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.24f, 0.60f, 0.00f, 1.00f);
|
|
c[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.66f, 0.23f, 1.00f);
|
|
c[ImGuiCol_SliderGrab] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
|
|
c[ImGuiCol_SliderGrabActive] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
|
c[ImGuiCol_Button] = ImVec4(0.17f, 0.17f, 0.17f, 1.00f);
|
|
c[ImGuiCol_ButtonHovered] = ImVec4(0.24f, 0.60f, 0.00f, 1.00f);
|
|
c[ImGuiCol_ButtonActive] = ImVec4(0.26f, 0.66f, 0.23f, 1.00f);
|
|
c[ImGuiCol_Header] = ImVec4(0.24f, 0.60f, 0.00f, 1.00f);
|
|
c[ImGuiCol_HeaderHovered] = ImVec4(0.24f, 0.60f, 0.00f, 1.00f);
|
|
c[ImGuiCol_HeaderActive] = ImVec4(0.24f, 0.60f, 0.00f, 1.00f);
|
|
c[ImGuiCol_Separator] = ImVec4(1.00f, 1.00f, 1.00f, 0.25f);
|
|
c[ImGuiCol_SeparatorHovered] = ImVec4(0.13f, 0.87f, 0.16f, 0.78f);
|
|
c[ImGuiCol_SeparatorActive] = ImVec4(0.25f, 0.75f, 0.10f, 1.00f);
|
|
c[ImGuiCol_ResizeGrip] = ImVec4(0.47f, 0.83f, 0.49f, 0.04f);
|
|
c[ImGuiCol_ResizeGripHovered] = ImVec4(0.28f, 0.71f, 0.25f, 0.78f);
|
|
c[ImGuiCol_ResizeGripActive] = ImVec4(0.28f, 0.71f, 0.25f, 1.00f);
|
|
c[ImGuiCol_Tab] = ImVec4(0.26f, 0.67f, 0.23f, 0.95f);
|
|
c[ImGuiCol_TabHovered] = ImVec4(0.24f, 0.60f, 0.00f, 1.00f);
|
|
c[ImGuiCol_TabActive] = ImVec4(0.24f, 0.60f, 0.00f, 1.00f);
|
|
c[ImGuiCol_TabUnfocused] = ImVec4(0.21f, 0.54f, 0.19f, 0.99f);
|
|
c[ImGuiCol_TabUnfocusedActive] = ImVec4(0.24f, 0.60f, 0.21f, 1.00f);
|
|
c[ImGuiCol_PlotLines] = ImVec4(0.86f, 0.93f, 0.89f, 0.63f);
|
|
c[ImGuiCol_PlotLinesHovered] = ImVec4(0.28f, 0.71f, 0.25f, 1.00f);
|
|
c[ImGuiCol_PlotHistogram] = ImVec4(0.86f, 0.93f, 0.89f, 0.63f);
|
|
c[ImGuiCol_PlotHistogramHovered] = ImVec4(0.28f, 0.71f, 0.25f, 1.00f);
|
|
c[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.66f, 0.23f, 1.00f);
|
|
c[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
|
c[ImGuiCol_NavHighlight] = ImVec4(0.28f, 0.71f, 0.25f, 1.00f);
|
|
c[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
|
c[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
|
c[ImGuiCol_ModalWindowDimBg] = ImVec4(0.16f, 0.16f, 0.16f, 0.73f);
|
|
|
|
ImGuiStyle &s = ImGui::GetStyle();
|
|
s.WindowRounding = 6.0;
|
|
s.FrameRounding = 6.0;
|
|
s.PopupRounding = 6.0;
|
|
g_base_style = s;
|
|
}
|
|
|
|
void xemu_hud_init(SDL_Window* window, void* sdl_gl_context)
|
|
{
|
|
xemu_monitor_init();
|
|
g_vsync = g_config.display.window.vsync;
|
|
|
|
InitCustomRendering();
|
|
|
|
// Setup Dear ImGui context
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
|
io.IniFilename = NULL;
|
|
|
|
// Setup Platform/Renderer bindings
|
|
ImGui_ImplSDL2_InitForOpenGL(window, sdl_gl_context);
|
|
ImGui_ImplOpenGL3_Init("#version 150");
|
|
g_sdl_window = window;
|
|
ImPlot::CreateContext();
|
|
|
|
#if defined(_WIN32)
|
|
if (!g_config.general.show_welcome && g_config.general.updates.check) {
|
|
update_window.CheckForUpdates();
|
|
}
|
|
#endif
|
|
g_last_scale = g_viewport_mgr.m_scale;
|
|
InitializeStyle();
|
|
g_main_menu.SetNextViewIndex(g_config.general.last_viewed_menu_index);
|
|
first_boot_window.is_open = g_config.general.show_welcome;
|
|
}
|
|
|
|
void xemu_hud_cleanup(void)
|
|
{
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
ImGui_ImplSDL2_Shutdown();
|
|
ImGui::DestroyContext();
|
|
}
|
|
|
|
void xemu_hud_process_sdl_events(SDL_Event *event)
|
|
{
|
|
ImGui_ImplSDL2_ProcessEvent(event);
|
|
}
|
|
|
|
void xemu_hud_should_capture_kbd_mouse(int *kbd, int *mouse)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
if (kbd) *kbd = io.WantCaptureKeyboard;
|
|
if (mouse) *mouse = io.WantCaptureMouse;
|
|
}
|
|
|
|
void xemu_hud_set_framebuffer_texture(GLuint tex, bool flip)
|
|
{
|
|
g_tex = tex;
|
|
g_flip_req = flip;
|
|
}
|
|
|
|
void xemu_hud_render(void)
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
uint32_t now = SDL_GetTicks();
|
|
|
|
g_viewport_mgr.Update();
|
|
g_font_mgr.Update();
|
|
if (g_last_scale != g_viewport_mgr.m_scale) {
|
|
ImGuiStyle &style = ImGui::GetStyle();
|
|
style = g_base_style;
|
|
style.ScaleAllSizes(g_viewport_mgr.m_scale);
|
|
g_last_scale = g_viewport_mgr.m_scale;
|
|
}
|
|
|
|
if (!first_boot_window.is_open) {
|
|
int ww, wh;
|
|
SDL_GL_GetDrawableSize(g_sdl_window, &ww, &wh);
|
|
RenderFramebuffer(g_tex, ww, wh, g_flip_req);
|
|
}
|
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
io.ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
|
|
ImGui_ImplSDL2_NewFrame(g_sdl_window);
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
|
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
|
g_input_mgr.Update();
|
|
|
|
ImGui::NewFrame();
|
|
ProcessKeyboardShortcuts();
|
|
|
|
#if defined(DEBUG_NV2A_GL) && defined(CONFIG_RENDERDOC)
|
|
if (g_capture_renderdoc_frame) {
|
|
nv2a_dbg_renderdoc_capture_frames(1);
|
|
g_capture_renderdoc_frame = false;
|
|
}
|
|
#endif
|
|
|
|
if (g_config.display.ui.show_menubar && !first_boot_window.is_open) {
|
|
// Auto-hide main menu after 5s of inactivity
|
|
static uint32_t last_check = 0;
|
|
float alpha = 1.0;
|
|
const uint32_t timeout = 5000;
|
|
const float fade_duration = 1000.0;
|
|
bool menu_wakeup = g_input_mgr.MouseMoved();
|
|
if (menu_wakeup) {
|
|
last_check = now;
|
|
}
|
|
if ((now-last_check) > timeout) {
|
|
if (g_config.display.ui.use_animations) {
|
|
float t = fmin((float)((now-last_check)-timeout)/fade_duration, 1);
|
|
alpha = 1-t;
|
|
if (t >= 1) {
|
|
alpha = 0;
|
|
}
|
|
} else {
|
|
alpha = 0;
|
|
}
|
|
}
|
|
if (alpha > 0.0) {
|
|
ImVec4 tc = ImGui::GetStyle().Colors[ImGuiCol_Text];
|
|
tc.w = alpha;
|
|
ImGui::PushStyleColor(ImGuiCol_Text, tc);
|
|
ImGui::SetNextWindowBgAlpha(alpha);
|
|
ShowMainMenu();
|
|
ImGui::PopStyleColor();
|
|
} else {
|
|
g_main_menu_height = 0;
|
|
}
|
|
}
|
|
|
|
static uint32_t last_mouse_move = 0;
|
|
if (g_input_mgr.MouseMoved()) {
|
|
last_mouse_move = now;
|
|
}
|
|
|
|
// FIXME: Handle time wrap around
|
|
if (g_config.display.ui.hide_cursor && (now - last_mouse_move) > 3000) {
|
|
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
|
|
}
|
|
|
|
if (!ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow) &&
|
|
!g_scene_mgr.IsDisplayingScene()) {
|
|
|
|
// If the guide button is pressed, wake the ui
|
|
bool menu_button = false;
|
|
uint32_t buttons = g_input_mgr.CombinedButtons();
|
|
if (buttons & CONTROLLER_BUTTON_GUIDE) {
|
|
menu_button = true;
|
|
}
|
|
|
|
// Allow controllers without a guide button to also work
|
|
if ((buttons & CONTROLLER_BUTTON_BACK) &&
|
|
(buttons & CONTROLLER_BUTTON_START)) {
|
|
menu_button = true;
|
|
}
|
|
|
|
if (ImGui::IsKeyPressed(ImGuiKey_F1)) {
|
|
g_scene_mgr.PushScene(g_main_menu);
|
|
} else if (ImGui::IsKeyPressed(ImGuiKey_F2)) {
|
|
g_scene_mgr.PushScene(g_popup_menu);
|
|
} else if (menu_button ||
|
|
(ImGui::IsMouseClicked(ImGuiMouseButton_Right) &&
|
|
!ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemHovered())) {
|
|
g_scene_mgr.PushScene(g_popup_menu);
|
|
}
|
|
|
|
bool mod_key_down = ImGui::IsKeyDown(ImGuiKey_ModShift);
|
|
for (int f_key = 0; f_key < 4; ++f_key) {
|
|
if (ImGui::IsKeyPressed((enum ImGuiKey)(ImGuiKey_F5 + f_key))) {
|
|
ActionActivateBoundSnapshot(f_key, mod_key_down);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
first_boot_window.Draw();
|
|
monitor_window.Draw();
|
|
apu_window.Draw();
|
|
video_window.Draw();
|
|
compatibility_reporter_window.Draw();
|
|
#if defined(_WIN32)
|
|
update_window.Draw();
|
|
#endif
|
|
g_scene_mgr.Draw();
|
|
if (!first_boot_window.is_open) notification_manager.Draw();
|
|
g_snapshot_mgr.Draw();
|
|
|
|
// static bool show_demo = true;
|
|
// if (show_demo) ImGui::ShowDemoWindow(&show_demo);
|
|
|
|
ImGui::Render();
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
if (g_vsync != g_config.display.window.vsync) {
|
|
g_vsync = g_config.display.window.vsync;
|
|
SDL_GL_SetSwapInterval(g_vsync ? 1 : 0);
|
|
}
|
|
|
|
if (g_screenshot_pending) {
|
|
SaveScreenshot(g_tex, g_flip_req);
|
|
g_screenshot_pending = false;
|
|
}
|
|
}
|