Files
spice2x.github.io/src/spice2x/overlay/overlay.cpp
T
bicarus 9936088286 popn: subscreen overlay (#631)
## 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
2026-04-12 02:11:24 -07:00

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());
}
}