mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
9936088286
## Link to GitHub Issue or related Pull Request, if one exists #618 ## Description of change Add overlay for subscreen. Add an option for no subscreen window. Use the same "force redraw subscreen" logic we used in SDVX to fix the same issue. Known issues: * still crashing when running windowed if there is only one monitor * having only one monitor causes sunscreen overlay to not work (NumberOfAdaptersInGroup issue?) ## Testing
746 lines
23 KiB
C++
746 lines
23 KiB
C++
#include "overlay.h"
|
|
|
|
#include "avs/game.h"
|
|
#include "cfg/configurator.h"
|
|
#include "games/io.h"
|
|
#include "games/gitadora/gitadora.h"
|
|
#include "games/iidx/iidx.h"
|
|
#include "games/popn/popn.h"
|
|
#include "hooks/graphics/graphics.h"
|
|
#include "misc/eamuse.h"
|
|
#include "touch/touch.h"
|
|
#include "util/fileutils.h"
|
|
#include "util/logging.h"
|
|
#include "util/resutils.h"
|
|
#include "build/resource.h"
|
|
|
|
#include "external/imgui/backends/imgui_impl_dx9.h"
|
|
#include "overlay/imgui/impl_spice.h"
|
|
#include "overlay/imgui/impl_sw.h"
|
|
|
|
#include "window.h"
|
|
#ifdef SPICE64
|
|
#include "windows/camera_control.h"
|
|
#endif
|
|
#include "windows/card_manager.h"
|
|
#include "windows/screen_resize.h"
|
|
#include "windows/config.h"
|
|
#include "windows/control.h"
|
|
#include "windows/fps.h"
|
|
#include "windows/generic_sub.h"
|
|
#include "windows/iidx_seg.h"
|
|
#include "windows/iidx_sub.h"
|
|
#include "windows/gfdm_sub.h"
|
|
#include "windows/drs_dancefloor.h"
|
|
#include "windows/iopanel.h"
|
|
#include "windows/iopanel_ddr.h"
|
|
#include "windows/iopanel_gfdm.h"
|
|
#include "windows/iopanel_iidx.h"
|
|
#include "windows/popn_sub.h"
|
|
#include "windows/sdvx_sub.h"
|
|
#include "windows/keypad.h"
|
|
#include "windows/log.h"
|
|
#include "windows/patch_manager.h"
|
|
#include "windows/exitprompt.cpp"
|
|
|
|
static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) \
|
|
{ return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
|
|
|
|
namespace overlay {
|
|
|
|
// settings
|
|
bool ENABLED = true;
|
|
bool AUTO_SHOW_FPS = false;
|
|
bool AUTO_SHOW_SUBSCREEN = false;
|
|
bool AUTO_SHOW_IOPANEL = false;
|
|
bool AUTO_SHOW_KEYPAD_P1 = false;
|
|
bool AUTO_SHOW_KEYPAD_P2 = false;
|
|
bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT = false;
|
|
bool FPS_SHOULD_FLIP = false;
|
|
std::optional<uint32_t> UI_SCALE_PERCENT;
|
|
|
|
// global
|
|
std::mutex OVERLAY_MUTEX;
|
|
std::unique_ptr<overlay::SpiceOverlay> OVERLAY = nullptr;
|
|
ImFont* DSEG_FONT = nullptr;
|
|
bool SHOW_DEBUG_LOG_WINDOW = false;
|
|
|
|
ImVec2 apply_scaling_to_vector(float x, float y) {
|
|
if (!UI_SCALE_PERCENT.has_value()) {
|
|
return ImVec2(x, y);
|
|
}
|
|
return ImVec2(apply_scaling(x), apply_scaling(y));
|
|
}
|
|
|
|
ImVec2 apply_scaling_to_vector(const ImVec2& input) {
|
|
return apply_scaling_to_vector(input.x, input.y);
|
|
}
|
|
}
|
|
|
|
static void *ImGui_Alloc(size_t sz, void *user_data) {
|
|
void *data = malloc(sz);
|
|
if (!data) {
|
|
return nullptr;
|
|
}
|
|
|
|
memset(data, 0, sz);
|
|
|
|
return data;
|
|
}
|
|
|
|
static void ImGui_Free(void *data, void *user_data) {
|
|
free(data);
|
|
}
|
|
|
|
void overlay::create_d3d9(HWND hWnd, IDirect3D9 *d3d, IDirect3DDevice9 *device) {
|
|
if (!overlay::ENABLED) {
|
|
return;
|
|
}
|
|
|
|
const std::lock_guard<std::mutex> lock(OVERLAY_MUTEX);
|
|
|
|
if (!overlay::OVERLAY) {
|
|
overlay::OVERLAY = std::make_unique<overlay::SpiceOverlay>(hWnd, d3d, device);
|
|
}
|
|
}
|
|
|
|
void overlay::create_software(HWND hWnd) {
|
|
if (!overlay::ENABLED) {
|
|
return;
|
|
}
|
|
|
|
const std::lock_guard<std::mutex> lock(OVERLAY_MUTEX);
|
|
|
|
if (!overlay::OVERLAY) {
|
|
overlay::OVERLAY = std::make_unique<overlay::SpiceOverlay>(hWnd);
|
|
}
|
|
}
|
|
|
|
void overlay::destroy(HWND hWnd) {
|
|
if (!overlay::ENABLED) {
|
|
return;
|
|
}
|
|
|
|
const std::lock_guard<std::mutex> lock(OVERLAY_MUTEX);
|
|
|
|
if (overlay::OVERLAY && (hWnd == nullptr || overlay::OVERLAY->uses_window(hWnd))) {
|
|
overlay::OVERLAY.reset();
|
|
}
|
|
}
|
|
|
|
overlay::SpiceOverlay::SpiceOverlay(HWND hWnd, IDirect3D9 *d3d, IDirect3DDevice9 *device)
|
|
: renderer(OverlayRenderer::D3D9), hWnd(hWnd), d3d(d3d), device(device) {
|
|
log_info("overlay", "initializing (D3D9)");
|
|
|
|
// increment reference counts
|
|
this->d3d->AddRef();
|
|
this->device->AddRef();
|
|
|
|
// get creation parameters
|
|
HRESULT ret;
|
|
ret = this->device->GetCreationParameters(&this->creation_parameters);
|
|
if (FAILED(ret)) {
|
|
log_fatal("overlay", "GetCreationParameters failed, hr={}", FMT_HRESULT(ret));
|
|
}
|
|
|
|
// get adapter identifier
|
|
ret = this->d3d->GetAdapterIdentifier(
|
|
creation_parameters.AdapterOrdinal,
|
|
0,
|
|
&this->adapter_identifier);
|
|
if (FAILED(ret)) {
|
|
log_fatal("overlay", "GetAdapterIdentifier failed, hr={}", FMT_HRESULT(ret));
|
|
}
|
|
|
|
// init
|
|
this->init();
|
|
}
|
|
|
|
overlay::SpiceOverlay::SpiceOverlay(HWND hWnd)
|
|
: renderer(OverlayRenderer::SOFTWARE), hWnd(hWnd) {
|
|
log_info("overlay", "initializing (SOFTWARE)");
|
|
|
|
// init
|
|
this->init();
|
|
}
|
|
|
|
void overlay::SpiceOverlay::init() {
|
|
|
|
// init imgui
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::SetAllocatorFunctions(ImGui_Alloc, ImGui_Free, nullptr);
|
|
ImGui::CreateContext();
|
|
ImGui::GetIO();
|
|
|
|
// set style
|
|
ImGui::StyleColorsDark();
|
|
if (this->renderer == OverlayRenderer::SOFTWARE) {
|
|
imgui_sw::make_style_fast();
|
|
ImVec4* colors = ImGui::GetStyle().Colors;
|
|
colors[ImGuiCol_Border].w = 0;
|
|
colors[ImGuiCol_Separator].w = 0.25f;
|
|
} else {
|
|
auto &style = ImGui::GetStyle();
|
|
style.WindowRounding = 0;
|
|
}
|
|
|
|
if (UI_SCALE_PERCENT.has_value()) {
|
|
const auto scale = static_cast<float>(UI_SCALE_PERCENT.value()) / 100.f;
|
|
ImGui::GetStyle().ScaleAllSizes(scale);
|
|
ImGui::GetStyle().FontScaleMain = scale;
|
|
}
|
|
|
|
// based on CrimsonVesuvius theme from
|
|
// https://github.com/ocornut/imgui/issues/707#issuecomment-4107169777
|
|
ImVec4* colors = ImGui::GetStyle().Colors;
|
|
|
|
// Text
|
|
colors[ImGuiCol_Text] = ImVec4(1.00f, 0.90f, 0.90f, 1.00f); // Slight pinkish tint to off-white
|
|
colors[ImGuiCol_TextDisabled] = ImVec4(0.35f, 0.26f, 0.26f, 1.00f);
|
|
|
|
// Backgrounds
|
|
colors[ImGuiCol_WindowBg] = ImVec4(0.08f, 0.07f, 0.07f, 1.00f); // Deep charcoal
|
|
colors[ImGuiCol_ChildBg] = ImVec4(0.10f, 0.09f, 0.09f, 1.00f);
|
|
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.07f, 0.07f, 0.96f);
|
|
|
|
// Borders
|
|
colors[ImGuiCol_Border] = ImVec4(0.25f, 0.15f, 0.15f, 0.80f);
|
|
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
|
|
|
// Frames
|
|
colors[ImGuiCol_FrameBg] = ImVec4(0.15f, 0.10f, 0.10f, 1.00f);
|
|
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.25f, 0.15f, 0.15f, 1.00f);
|
|
colors[ImGuiCol_FrameBgActive] = ImVec4(0.35f, 0.20f, 0.20f, 1.00f);
|
|
|
|
// Title Bars
|
|
colors[ImGuiCol_TitleBg] = ImVec4(0.12f, 0.08f, 0.08f, 1.00f);
|
|
colors[ImGuiCol_TitleBgActive] = ImVec4(0.25f, 0.10f, 0.10f, 1.00f);
|
|
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
|
|
|
// Menus
|
|
colors[ImGuiCol_MenuBarBg] = ImVec4(0.12f, 0.08f, 0.08f, 1.00f);
|
|
|
|
// Scrollbars
|
|
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.05f, 0.05f, 0.05f, 1.00f);
|
|
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.25f, 0.12f, 0.12f, 1.00f);
|
|
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.35f, 0.15f, 0.15f, 1.00f);
|
|
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.45f, 0.20f, 0.20f, 1.00f);
|
|
|
|
// Interactables (The High-Intensity Red)
|
|
colors[ImGuiCol_CheckMark] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f); // Sharp Red
|
|
colors[ImGuiCol_SliderGrab] = ImVec4(0.60f, 0.12f, 0.12f, 1.00f);
|
|
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f);
|
|
colors[ImGuiCol_Button] = ImVec4(0.30f, 0.12f, 0.12f, 1.00f);
|
|
colors[ImGuiCol_ButtonHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
|
colors[ImGuiCol_ButtonActive] = ImVec4(0.70f, 0.25f, 0.25f, 1.00f);
|
|
colors[ImGuiCol_Header] = ImVec4(0.30f, 0.12f, 0.12f, 1.00f);
|
|
colors[ImGuiCol_HeaderHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
|
colors[ImGuiCol_HeaderActive] = ImVec4(0.70f, 0.25f, 0.25f, 1.00f);
|
|
|
|
// Tabs
|
|
colors[ImGuiCol_Tab] = ImVec4(0.15f, 0.10f, 0.10f, 1.00f);
|
|
colors[ImGuiCol_TabHovered] = ImVec4(0.50f, 0.18f, 0.18f, 1.00f);
|
|
colors[ImGuiCol_TabActive] = ImVec4(0.35f, 0.12f, 0.12f, 1.00f);
|
|
|
|
// Misc
|
|
colors[ImGuiCol_PlotLines] = ImVec4(0.85f, 0.20f, 0.20f, 1.00f);
|
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.85f, 0.15f, 0.15f, 0.35f);
|
|
colors[ImGuiCol_NavHighlight] = ImVec4(0.85f, 0.15f, 0.15f, 1.00f);
|
|
|
|
// spice custom
|
|
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.025f);
|
|
colors[ImGuiCol_Separator] = ImVec4(0.32f, 0.22f, 0.22f, 1.00f);
|
|
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.42f, 0.22f, 0.22f, 1.00f);
|
|
colors[ImGuiCol_SeparatorActive] = ImVec4(0.52f, 0.22f, 0.22f, 1.00f);
|
|
|
|
#ifdef IMGUI_HAS_DOCK
|
|
colors[ImGuiCol_DockingPreview] = ImVec4(0.85f, 0.15f, 0.15f, 0.40f);
|
|
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.08f, 0.07f, 0.07f, 1.00f);
|
|
#endif
|
|
|
|
// configure IO
|
|
auto &io = ImGui::GetIO();
|
|
io.UserData = this;
|
|
io.ConfigFlags = ImGuiConfigFlags_NavEnableKeyboard
|
|
| ImGuiConfigFlags_NavEnableGamepad
|
|
| ImGuiConfigFlags_NavEnableSetMousePos;
|
|
|
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
|
}
|
|
if (is_touch_available("SpiceOverlay::init")) {
|
|
io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
|
|
}
|
|
|
|
// temporarily turn this off as it can cause crashes during font load failures
|
|
// turns back on in ImGui_ImplSpice_Init
|
|
io.ConfigErrorRecoveryEnableTooltip = false;
|
|
|
|
io.MouseDrawCursor = !GRAPHICS_SHOW_CURSOR;
|
|
|
|
// disable config
|
|
io.IniFilename = nullptr;
|
|
|
|
// allow CTRL+WHEEL scaling
|
|
io.FontAllowUserScaling = true;
|
|
|
|
// add default font
|
|
io.Fonts->AddFontDefaultBitmap();
|
|
|
|
// add fallback fonts for missing glyph ranges
|
|
ImFontConfig config {};
|
|
config.MergeMode = true;
|
|
|
|
log_misc("overlay", "loading fonts, failures are not fatal");
|
|
add_font("simsun.ttc", &config, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
|
|
add_font("arial.ttf", &config, io.Fonts->GetGlyphRangesCyrillic());
|
|
add_font("meiryu.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
|
|
add_font("meiryo.ttc", &config, io.Fonts->GetGlyphRangesJapanese());
|
|
add_font("gulim.ttc", &config, io.Fonts->GetGlyphRangesKorean());
|
|
add_font("cordia.ttf", &config, io.Fonts->GetGlyphRangesThai());
|
|
add_font("arial.ttf", &config, io.Fonts->GetGlyphRangesVietnamese());
|
|
|
|
// add special font
|
|
if (avs::game::is_model("LDJ")) {
|
|
DWORD size;
|
|
ImFontConfig config {};
|
|
config.FontDataOwnedByAtlas = false;
|
|
auto buffer = resutil::load_file(IDR_DSEGFONT, &size);
|
|
DSEG_FONT = io.Fonts->AddFontFromMemoryTTF((void *)buffer, size,
|
|
overlay::windows::IIDX_SEGMENT_FONT_SIZE);
|
|
}
|
|
|
|
// init implementation
|
|
ImGui_ImplSpice_Init(this->hWnd);
|
|
switch (this->renderer) {
|
|
case OverlayRenderer::D3D9:
|
|
ImGui_ImplDX9_Init(this->device);
|
|
break;
|
|
case OverlayRenderer::SOFTWARE:
|
|
imgui_sw::bind_imgui_painting();
|
|
break;
|
|
}
|
|
|
|
// create empty frame
|
|
switch (this->renderer) {
|
|
case OverlayRenderer::D3D9:
|
|
ImGui_ImplDX9_NewFrame();
|
|
break;
|
|
case OverlayRenderer::SOFTWARE:
|
|
break;
|
|
}
|
|
ImGui_ImplSpice_NewFrame();
|
|
ImGui::NewFrame();
|
|
ImGui::EndFrame();
|
|
|
|
// fix navigation buttons causing crash on overlay activation
|
|
ImGui::Begin("Temp");
|
|
ImGui::End();
|
|
|
|
bool set_overlay_active = false;
|
|
|
|
// referenced windows
|
|
this->window_add(window_fps = new overlay::windows::FPS(this));
|
|
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_FPS) {
|
|
window_fps->set_active(true);
|
|
set_overlay_active = true;
|
|
}
|
|
|
|
this->window_add(window_main_menu = new overlay::windows::ExitPrompt(this));
|
|
|
|
// add default windows
|
|
this->window_add(window_config = new overlay::windows::Config(this));
|
|
this->window_add(window_control = new overlay::windows::Control(this));
|
|
this->window_add(window_log = new overlay::windows::Log(this));
|
|
#if SPICE64 && !SPICE_XP
|
|
if (avs::game::is_model("LDJ")) {
|
|
this->window_add(window_camera = new overlay::windows::CameraControl(this));
|
|
}
|
|
#endif
|
|
this->window_add(window_cards = new overlay::windows::CardManager(this));
|
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
|
this->window_add(window_resize = new overlay::windows::ScreenResize(this));
|
|
}
|
|
this->window_add(new overlay::windows::PatchManager(this));
|
|
|
|
{
|
|
window_keypad1 = new overlay::windows::Keypad(this, 0);
|
|
this->window_add(window_keypad1);
|
|
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_KEYPAD_P1) {
|
|
window_keypad1->set_active(true);
|
|
set_overlay_active = true;
|
|
}
|
|
}
|
|
if (eamuse_get_game_keypads() > 1) {
|
|
window_keypad2 = new overlay::windows::Keypad(this, 1);
|
|
this->window_add(window_keypad2);
|
|
if (!cfg::CONFIGURATOR_STANDALONE && AUTO_SHOW_KEYPAD_P2) {
|
|
window_keypad2->set_active(true);
|
|
set_overlay_active = true;
|
|
}
|
|
}
|
|
|
|
// IO panel needs to know what game is running
|
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
|
window_iopanel = nullptr;
|
|
if (avs::game::is_model("LDJ")) {
|
|
window_iopanel = new overlay::windows::IIDXIOPanel(this);
|
|
} else if (avs::game::is_model("MDX")) {
|
|
window_iopanel = new overlay::windows::DDRIOPanel(this);
|
|
} else if (avs::game::is_model({"J32", "J33", "K32", "K33", "L32", "L33", "M32"}) &&
|
|
!games::gitadora::is_arena_model()) {
|
|
window_iopanel = new overlay::windows::GitadoraIOPanel(this);
|
|
} else {
|
|
window_iopanel = new overlay::windows::IOPanel(this);
|
|
}
|
|
if (window_iopanel) {
|
|
this->window_add(window_iopanel);
|
|
if (AUTO_SHOW_IOPANEL) {
|
|
window_iopanel->set_active(true);
|
|
set_overlay_active = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// subscreens need DirectX, so don't try to initialize them in standalone
|
|
if (!cfg::CONFIGURATOR_STANDALONE) {
|
|
window_sub = nullptr;
|
|
if (avs::game::is_model("LDJ")) {
|
|
if (games::iidx::TDJ_MODE) {
|
|
window_sub = new overlay::windows::IIDXSubScreen(this);
|
|
} else {
|
|
window_sub = new overlay::windows::IIDXSegmentDisplay(this);
|
|
}
|
|
} else if (avs::game::is_model("REC")) {
|
|
window_sub = new overlay::windows::DRSDanceFloorDisplay(this);
|
|
} else if (avs::game::is_model("KFC")) {
|
|
window_sub = new overlay::windows::SDVXSubScreen(this);
|
|
} else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW) {
|
|
window_sub = new overlay::windows::GitaDoraSubScreen(this);
|
|
} else if (games::popn::is_pikapika_model()) {
|
|
window_sub = new overlay::windows::PopnSubScreen(this);
|
|
}
|
|
|
|
if (window_sub) {
|
|
this->window_add(window_sub);
|
|
if (AUTO_SHOW_SUBSCREEN) {
|
|
window_sub->set_active(true);
|
|
set_overlay_active = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (set_overlay_active) {
|
|
this->set_active(true);
|
|
}
|
|
}
|
|
|
|
overlay::SpiceOverlay::~SpiceOverlay() {
|
|
|
|
// imgui shutdown
|
|
ImGui_ImplSpice_Shutdown();
|
|
switch (this->renderer) {
|
|
case OverlayRenderer::D3D9:
|
|
ImGui_ImplDX9_Shutdown();
|
|
|
|
// drop references
|
|
this->device->Release();
|
|
this->d3d->Release();
|
|
|
|
break;
|
|
case OverlayRenderer::SOFTWARE:
|
|
imgui_sw::unbind_imgui_painting();
|
|
break;
|
|
}
|
|
ImGui::DestroyContext();
|
|
}
|
|
|
|
void overlay::SpiceOverlay::window_add(Window *wnd) {
|
|
this->windows.emplace_back(std::unique_ptr<Window>(wnd));
|
|
}
|
|
|
|
void overlay::SpiceOverlay::new_frame() {
|
|
|
|
// update implementation
|
|
ImGui_ImplSpice_NewFrame();
|
|
this->total_elapsed += ImGui::GetIO().DeltaTime;
|
|
|
|
// check if inactive
|
|
if (!this->active) {
|
|
return;
|
|
}
|
|
|
|
// init frame
|
|
switch (this->renderer) {
|
|
case OverlayRenderer::D3D9:
|
|
ImGui_ImplDX9_NewFrame();
|
|
break;
|
|
case OverlayRenderer::SOFTWARE:
|
|
ImGui_ImplSpice_UpdateDisplaySize();
|
|
break;
|
|
}
|
|
ImGui::NewFrame();
|
|
|
|
// build windows
|
|
for (auto &window : this->windows) {
|
|
window->build();
|
|
}
|
|
|
|
if (SHOW_DEBUG_LOG_WINDOW) {
|
|
ImGui::ShowDebugLogWindow(&SHOW_DEBUG_LOG_WINDOW);
|
|
}
|
|
|
|
// end frame
|
|
ImGui::EndFrame();
|
|
}
|
|
|
|
void overlay::SpiceOverlay::render() {
|
|
|
|
// check if inactive
|
|
if (!this->active) {
|
|
return;
|
|
}
|
|
|
|
// imgui render
|
|
ImGui::Render();
|
|
|
|
// implementation render
|
|
switch (this->renderer) {
|
|
case OverlayRenderer::D3D9:
|
|
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
|
|
break;
|
|
case OverlayRenderer::SOFTWARE: {
|
|
|
|
// get display metrics
|
|
auto &io = ImGui::GetIO();
|
|
auto width = static_cast<size_t>(std::ceil(io.DisplaySize.x));
|
|
auto height = static_cast<size_t>(std::ceil(io.DisplaySize.y));
|
|
auto pixels = width * height;
|
|
|
|
// make sure buffer is big enough
|
|
if (this->pixel_data.size() < pixels) {
|
|
this->pixel_data.resize(pixels, 0);
|
|
}
|
|
|
|
// reset buffer
|
|
memset(&this->pixel_data[0], 0, width * height * sizeof(uint32_t));
|
|
|
|
// render to pixel data
|
|
imgui_sw::SwOptions options {
|
|
.optimize_text = true,
|
|
.optimize_rectangles = true,
|
|
};
|
|
imgui_sw::paint_imgui(&this->pixel_data[0], width, height, options);
|
|
pixel_data_width = width;
|
|
pixel_data_height = height;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (auto &window : this->windows) {
|
|
window->after_render();
|
|
}
|
|
}
|
|
|
|
void overlay::SpiceOverlay::update() {
|
|
|
|
// check overlay toggle
|
|
auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game());
|
|
bool toggle_down_new = overlay_buttons
|
|
&& this->hotkeys_triggered()
|
|
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(games::OverlayButtons::ToggleOverlay));
|
|
if (toggle_down_new && !this->toggle_down) {
|
|
toggle_active(true);
|
|
}
|
|
this->toggle_down = toggle_down_new;
|
|
|
|
// check main menu
|
|
const auto main_menu_down_new = overlay_buttons
|
|
&& this->hotkeys_triggered()
|
|
&& GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(games::OverlayButtons::ToggleMainMenu));
|
|
if (main_menu_down_new && !this->main_menu_down) {
|
|
show_main_menu();
|
|
}
|
|
this->main_menu_down = main_menu_down_new;
|
|
|
|
// update windows
|
|
for (auto &window : this->windows) {
|
|
window->update();
|
|
}
|
|
|
|
// deactivate if no windows are shown
|
|
bool window_active = false;
|
|
for (auto &window : this->windows) {
|
|
if (window->get_active()) {
|
|
window_active = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!window_active) {
|
|
this->set_active(false);
|
|
}
|
|
}
|
|
|
|
bool overlay::SpiceOverlay::update_cursor() {
|
|
return ImGui_ImplSpice_UpdateMouseCursor();
|
|
}
|
|
|
|
void overlay::SpiceOverlay::toggle_active(bool overlay_key) {
|
|
|
|
// invert active state
|
|
this->active = !this->active;
|
|
|
|
// get rid of main menu if it was visible
|
|
if (this->window_main_menu) {
|
|
this->window_main_menu->set_active(false);
|
|
}
|
|
|
|
// show FPS window if toggled with overlay key
|
|
if (overlay_key) {
|
|
this->window_fps->set_active(this->active);
|
|
}
|
|
}
|
|
|
|
void overlay::SpiceOverlay::show_main_menu() {
|
|
if (!this->window_main_menu) {
|
|
return;
|
|
}
|
|
if (this->window_main_menu->get_active()) {
|
|
// window already visible - close the window
|
|
this->window_main_menu->set_active(false);
|
|
return;
|
|
}
|
|
if (ImGui::IsPopupOpen(0, ImGuiPopupFlags_AnyPopup)) {
|
|
return;
|
|
}
|
|
|
|
if (this->get_active()) {
|
|
if (!ImGui::IsAnyItemActive() && !ImGui::IsAnyItemFocused()) {
|
|
this->window_main_menu->set_active(true);
|
|
}
|
|
} else {
|
|
this->set_active(true);
|
|
this->window_main_menu->set_active(true);
|
|
}
|
|
}
|
|
|
|
void overlay::SpiceOverlay::set_active(bool new_active) {
|
|
|
|
// toggle if different
|
|
if (this->active != new_active) {
|
|
this->toggle_active();
|
|
}
|
|
}
|
|
|
|
bool overlay::SpiceOverlay::get_active() {
|
|
return this->active;
|
|
}
|
|
|
|
bool overlay::SpiceOverlay::has_focus() {
|
|
return this->get_active() && ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow);
|
|
}
|
|
|
|
bool overlay::SpiceOverlay::hotkeys_triggered() {
|
|
|
|
// check if disabled first
|
|
if (!this->hotkeys_enable) {
|
|
return false;
|
|
}
|
|
|
|
// get buttons
|
|
auto buttons = games::get_buttons_overlay(eamuse_get_game());
|
|
if (!buttons) {
|
|
return false;
|
|
}
|
|
|
|
auto &hotkey1 = buttons->at(games::OverlayButtons::HotkeyEnable1);
|
|
auto &hotkey2 = buttons->at(games::OverlayButtons::HotkeyEnable2);
|
|
auto &toggle = buttons->at(games::OverlayButtons::HotkeyToggle);
|
|
|
|
// check hotkey toggle
|
|
auto toggle_state = GameAPI::Buttons::getState(RI_MGR, toggle);
|
|
if (toggle_state) {
|
|
if (!this->hotkey_toggle_last) {
|
|
this->hotkey_toggle_last = true;
|
|
this->hotkey_toggle = !this->hotkey_toggle;
|
|
}
|
|
} else {
|
|
this->hotkey_toggle_last = false;
|
|
}
|
|
|
|
// hotkey toggle overrides hotkey enable button states
|
|
if (hotkey_toggle) {
|
|
return true;
|
|
}
|
|
|
|
// check hotkey enable buttons
|
|
bool triggered = true;
|
|
if (hotkey1.isSet() && !GameAPI::Buttons::getState(RI_MGR, hotkey1)) {
|
|
triggered = false;
|
|
}
|
|
if (hotkey2.isSet() && !GameAPI::Buttons::getState(RI_MGR, hotkey2)) {
|
|
triggered = false;
|
|
}
|
|
return triggered;
|
|
}
|
|
|
|
void overlay::SpiceOverlay::reset_invalidate() {
|
|
ImGui_ImplDX9_InvalidateDeviceObjects();
|
|
}
|
|
|
|
void overlay::SpiceOverlay::reset_recreate() {
|
|
ImGui_ImplDX9_CreateDeviceObjects();
|
|
}
|
|
|
|
void overlay::SpiceOverlay::input_char(unsigned int c) {
|
|
// add character to ImGui
|
|
ImGui::GetIO().AddInputCharacter(c);
|
|
}
|
|
|
|
uint32_t *overlay::SpiceOverlay::sw_get_pixel_data(int *width, int *height) {
|
|
|
|
// check if active
|
|
if (!this->active) {
|
|
*width = 0;
|
|
*height = 0;
|
|
return nullptr;
|
|
}
|
|
|
|
// ensure buffer has the right size
|
|
const size_t total_size = this->pixel_data_width * this->pixel_data_height;
|
|
if (this->pixel_data.size() < total_size) {
|
|
this->pixel_data.resize(total_size, 0);
|
|
}
|
|
|
|
// check for empty surface
|
|
if (this->pixel_data.empty()) {
|
|
*width = 0;
|
|
*height = 0;
|
|
return nullptr;
|
|
}
|
|
|
|
// copy and return pointer to data
|
|
*width = this->pixel_data_width;
|
|
*height = this->pixel_data_height;
|
|
return &this->pixel_data[0];
|
|
}
|
|
|
|
void overlay::SpiceOverlay::add_font(const char* font, ImFontConfig* config, const ImWchar* glyphs) {
|
|
CHAR fonts_dir[MAX_PATH];
|
|
ExpandEnvironmentStringsA(R"(%SYSTEMROOT%\Fonts\)", fonts_dir, MAX_PATH);
|
|
std::filesystem::path full_path = fonts_dir;
|
|
full_path += font;
|
|
|
|
if (fileutils::file_exists(full_path)) {
|
|
log_misc("overlay", "loading font: {}", full_path.string());
|
|
ImGui::GetIO().Fonts->AddFontFromFileTTF(
|
|
full_path.string().c_str(),
|
|
13.0f,
|
|
config,
|
|
glyphs);
|
|
} else {
|
|
log_misc("overlay", "font not found: {}", full_path.string());
|
|
}
|
|
}
|