mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
0dda920448
## Link to GitHub Issue, if one exists #345 ## Description of change For IIDX / SDVX, when the subscreen surface failed to capture, display an error message, suggesting the user to patch the game as needed. Prior to this change, the subscreen overlay just showed an invisible window if this happened. ## Testing Tested IIDX windowed / full screen, and voltex full screen.
249 lines
9.6 KiB
C++
249 lines
9.6 KiB
C++
#undef CINTERFACE
|
|
|
|
#include "generic_sub.h"
|
|
|
|
#include <fmt/format.h>
|
|
|
|
#include "avs/game.h"
|
|
#include "games/io.h"
|
|
#include "cfg/screen_resize.h"
|
|
#include "hooks/graphics/backends/d3d9/d3d9_backend.h"
|
|
#include "hooks/graphics/backends/d3d9/d3d9_device.h"
|
|
#include "hooks/graphics/graphics.h"
|
|
#include "util/logging.h"
|
|
#include "util/utils.h"
|
|
#include "touch/touch.h"
|
|
|
|
int GENERIC_SUB_WINDOW_X = 0;
|
|
int GENERIC_SUB_WINDOW_Y = 0;
|
|
int GENERIC_SUB_WINDOW_WIDTH = 0;
|
|
int GENERIC_SUB_WINDOW_HEIGHT = 0;
|
|
bool GENERIC_SUB_WINDOW_FULLSIZE = false;
|
|
|
|
// #define OVERLAYDBG 1
|
|
|
|
namespace overlay::windows {
|
|
|
|
const ImVec4 YELLOW(1.f, 1.f, 0.f, 1.f);
|
|
const ImVec4 WHITE(1.f, 1.f, 1.f, 1.f);
|
|
|
|
GenericSubScreen::GenericSubScreen(SpiceOverlay *overlay) : Window(overlay), device(overlay->get_device()) {
|
|
this->remove_window_padding = true;
|
|
// ImGuiWindowFlags_NoBackground is needed as the background is drawn on top of the subscreen image
|
|
this->flags = ImGuiWindowFlags_NoScrollbar |
|
|
ImGuiWindowFlags_NoCollapse |
|
|
ImGuiWindowFlags_NoBackground |
|
|
ImGuiWindowFlags_NoDocking;
|
|
|
|
this->size_max = ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y);
|
|
this->size_min = ImVec2(240, 135 + ImGui::GetFrameHeight());
|
|
this->init_size = size_min;
|
|
this->resize_callback = keep_16_by_9;
|
|
|
|
this->toggle_button = games::OverlayButtons::ToggleSubScreen;
|
|
this->texture_width = 0;
|
|
this->texture_height = 0;
|
|
|
|
overlay->set_subscreen_mouse_handler([this](LONG *x, LONG *y) -> bool {
|
|
// convert to normalized form (relative window coordinates 0.f-1.f)
|
|
ImVec2 xy;
|
|
|
|
// log_misc("sub::overlay", "mouse handler {} {}", to_string(*x), to_string(*y));
|
|
// log_misc("sub::overlay", "spicetouch coords {} {} {} {}", to_string(SPICETOUCH_TOUCH_X), to_string(SPICETOUCH_TOUCH_Y), to_string(SPICETOUCH_TOUCH_WIDTH), to_string(SPICETOUCH_TOUCH_HEIGHT));
|
|
|
|
float ratio_x, ratio_y;
|
|
|
|
if (GRAPHICS_WINDOWED) {
|
|
// input coords are relative to spicetouch wnd
|
|
ratio_x = (float)*x / SPICETOUCH_TOUCH_WIDTH;
|
|
ratio_y = (float)*y / SPICETOUCH_TOUCH_HEIGHT;
|
|
} else {
|
|
// inputs coords are relative to (0,0) for non-windowed mode
|
|
ratio_x = (float)*x / ImGui::GetIO().DisplaySize.x;
|
|
ratio_y = (float)*y / ImGui::GetIO().DisplaySize.y;
|
|
}
|
|
// log_misc("sub::overlay", "game coords {} {}", to_string(ratio_x), to_string(ratio_y));
|
|
|
|
// transform to subscreen overlay coords
|
|
if (!GENERIC_SUB_WINDOW_FULLSIZE) {
|
|
ratio_x = (ratio_x * ImGui::GetIO().DisplaySize.x - GENERIC_SUB_WINDOW_X) / GENERIC_SUB_WINDOW_WIDTH;
|
|
ratio_y = (ratio_y * ImGui::GetIO().DisplaySize.y - GENERIC_SUB_WINDOW_Y) / GENERIC_SUB_WINDOW_HEIGHT;
|
|
// log_misc("sub::overlay", "overlay coords {} {} {} {}", to_string(GENERIC_SUB_WINDOW_X), to_string(GENERIC_SUB_WINDOW_Y), to_string(GENERIC_SUB_WINDOW_WIDTH), to_string(GENERIC_SUB_WINDOW_HEIGHT));
|
|
}
|
|
|
|
xy.x = ratio_x;
|
|
xy.y = ratio_y;
|
|
// log_misc("sub::overlay", "ratio {} {}", to_string(xy.x), to_string(xy.y));
|
|
|
|
// x/y can be outside of window
|
|
if (xy.x < 0.f || 1.f < xy.x || xy.y < 0.f || 1.f < xy.y) {
|
|
return false;
|
|
}
|
|
|
|
// call into child
|
|
this->touch_transform(xy, x, y);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
void GenericSubScreen::touch_transform(const ImVec2 xy_in, LONG *x_out, LONG *y_out) {}
|
|
|
|
void GenericSubScreen::build_content() {
|
|
this->flags |= ImGuiWindowFlags_NoBackground;
|
|
|
|
if (this->disabled_message.has_value()) {
|
|
this->flags &= ~ImGuiWindowFlags_NoBackground;
|
|
ImGui::TextColored(YELLOW, "%s", this->disabled_message.value().c_str());
|
|
return;
|
|
}
|
|
|
|
this->draw_texture();
|
|
|
|
if (!this->texture) {
|
|
this->flags &= ~ImGuiWindowFlags_NoBackground;
|
|
ImGui::TextColored(YELLOW, "Failed to acquire surface texture for subscreen.");
|
|
if (avs::game::is_model("LDJ")) {
|
|
ImGui::TextColored(
|
|
YELLOW,
|
|
"Ensure that you are using the correct DLL type, \n"
|
|
"or patch the DLL to properly enable TDJ mode.");
|
|
} else if (avs::game::is_model("KFC")) {
|
|
ImGui::TextColored(
|
|
YELLOW,
|
|
"Ensure that you did not accidentally enable a patch \n"
|
|
"that turns off subscreen rendering.");
|
|
}
|
|
}
|
|
|
|
#if OVERLAYDBG
|
|
if (this->status_message.has_value()) {
|
|
log_warning("sub::overlay", "{}", this->status_message.value().c_str());
|
|
}
|
|
if (this->status_message.has_value()) {
|
|
ImGui::TextColored(YELLOW, "%s", this->status_message.value().c_str());
|
|
} else if (this->texture) {
|
|
ImGui::TextColored(WHITE, "Successfully acquired surface texture");
|
|
} else {
|
|
ImGui::TextColored(YELLOW, "Failed to acquire surface texture");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool GenericSubScreen::build_texture(IDirect3DSurface9 *surface, UINT width, UINT height) {
|
|
HRESULT hr;
|
|
|
|
D3DSURFACE_DESC desc {};
|
|
hr = surface->GetDesc(&desc);
|
|
if (FAILED(hr)) {
|
|
this->status_message = fmt::format("Failed to get surface descriptor, hr={}", FMT_HRESULT(hr));
|
|
return false;
|
|
}
|
|
|
|
hr = this->device->CreateTexture(width, height, 0, desc.Usage, desc.Format,
|
|
desc.Pool, &this->texture, nullptr);
|
|
if (FAILED(hr)) {
|
|
this->status_message = fmt::format("Failed to create render target, hr={}", FMT_HRESULT(hr));
|
|
return false;
|
|
}
|
|
|
|
this->texture_width = width;
|
|
this->texture_height = height;
|
|
return true;
|
|
}
|
|
|
|
void GenericSubScreen::draw_texture() {
|
|
HRESULT hr;
|
|
|
|
auto surface = graphics_d3d9_ldj_get_sub_screen();
|
|
if (surface == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (this->draws_window) {
|
|
// calculate the **content** location and size.
|
|
// GetContentRegionAvail returns the correct dimension, up to (and including) the resize handle
|
|
// don't be tempted to change it to some other ImGui routine that accomplishes similar things!
|
|
overlay_content_top_left = ImGui::GetCursorScreenPos();
|
|
overlay_content_size = ImGui::GetContentRegionAvail();
|
|
} else {
|
|
// no window, full screen
|
|
overlay_content_top_left = ImVec2(0, 0);
|
|
overlay_content_size = ImGui::GetIO().DisplaySize;
|
|
}
|
|
|
|
GENERIC_SUB_WINDOW_X = overlay_content_top_left.x;
|
|
GENERIC_SUB_WINDOW_Y = overlay_content_top_left.y;
|
|
GENERIC_SUB_WINDOW_WIDTH = overlay_content_size.x;
|
|
GENERIC_SUB_WINDOW_HEIGHT = overlay_content_size.y;
|
|
|
|
if (this->draws_window &&
|
|
this->texture &&
|
|
((UINT)overlay_content_size.x != this->texture_width)) {
|
|
|
|
#if OVERLAYDBG
|
|
log_info("sub::overlay", "resize {} != {} ", overlay_content_size.x, this->texture_width);
|
|
#endif
|
|
|
|
// hack needed for SDVX; resizing the texture results in darker image, so allocate texture
|
|
// again with the new size when the window is resized
|
|
this->texture->Release();
|
|
this->texture = nullptr;
|
|
this->texture_width = 0;
|
|
this->texture_height = 0;
|
|
}
|
|
|
|
if (this->texture == nullptr) {
|
|
if (!this->build_texture(surface, overlay_content_size.x, overlay_content_size.y)) {
|
|
this->texture = nullptr;
|
|
this->texture_width = 0;
|
|
this->texture_height = 0;
|
|
surface->Release();
|
|
return;
|
|
}
|
|
}
|
|
|
|
IDirect3DSurface9 *texture_surface = nullptr;
|
|
hr = this->texture->GetSurfaceLevel(0, &texture_surface);
|
|
if (FAILED(hr)) {
|
|
this->status_message = fmt::format("Failed to get texture surface, hr={}", FMT_HRESULT(hr));
|
|
surface->Release();
|
|
return;
|
|
}
|
|
|
|
hr = this->device->StretchRect(surface, nullptr, texture_surface, nullptr, D3DTEXF_LINEAR);
|
|
if (FAILED(hr)) {
|
|
this->status_message = fmt::format("Failed to copy back buffer contents, hr={}", FMT_HRESULT(hr));
|
|
surface->Release();
|
|
texture_surface->Release();
|
|
return;
|
|
}
|
|
|
|
surface->Release();
|
|
texture_surface->Release();
|
|
|
|
// draw the subscreen (this draws *under* ImGui windows, over the game surface)
|
|
auto bottom_right = overlay_content_size;
|
|
bottom_right.x += overlay_content_top_left.x;
|
|
bottom_right.y += overlay_content_top_left.y;
|
|
ImGui::GetBackgroundDrawList()->AddImage(
|
|
reinterpret_cast<ImTextureID>(this->texture),
|
|
overlay_content_top_left,
|
|
bottom_right);
|
|
|
|
if (this->draws_window) {
|
|
// draw an invisible button so that it swallows mouse input
|
|
// this is needed to prevent the window from being dragged around
|
|
// (alternatively io.ConfigWindowsMoveFromTitleBarOnly can be set but that is global)
|
|
ImGui::InvisibleButton(
|
|
(this->title + "__InvisibleButton").c_str(),
|
|
overlay_content_size,
|
|
ImGuiButtonFlags_None);
|
|
}
|
|
|
|
#if OVERLAYDBG
|
|
log_info("sub::overlay", "{} / {} = {}", overlay_content_size.x, overlay_content_size.y, overlay_content_size.x / overlay_content_size.y);
|
|
#endif
|
|
|
|
}
|
|
}
|