// // 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 . // #include #include #include #include #include #include #include #include #include #include #include #include #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; } }