From 4f72b0a98bc78d00e65773ebb7654e454389e456 Mon Sep 17 00:00:00 2001 From: bicarus-dev <202771338+bicarus-dev@users.noreply.github.com> Date: Sun, 27 Apr 2025 21:20:55 -0700 Subject: [PATCH] graphics: more window options for windowed subscreen (#308) ## Link to GitHub Issue, if one exists Fixes #307 ## Description of change Add an option for making TDJ/UFC subscreen window borderless in windowed mode Add another option for keeping the subscreen "always on top". Add size / position arguments to SDVX windowed subscreen, exactly the same as what is allowed for IIDX. ## Testing Seems to work.... --- src/spice2x/games/iidx/poke.cpp | 4 +- src/spice2x/hooks/graphics/graphics.cpp | 30 +++++++++- src/spice2x/hooks/graphics/graphics.h | 16 +++--- .../hooks/graphics/graphics_windowed.cpp | 40 ++++++------- src/spice2x/launcher/launcher.cpp | 18 +++++- src/spice2x/launcher/options.cpp | 56 +++++++++++++++++++ src/spice2x/launcher/options.h | 6 ++ src/spice2x/overlay/windows/screen_resize.cpp | 4 +- 8 files changed, 138 insertions(+), 36 deletions(-) diff --git a/src/spice2x/games/iidx/poke.cpp b/src/spice2x/games/iidx/poke.cpp index e8f13b7..e0dc816 100644 --- a/src/spice2x/games/iidx/poke.cpp +++ b/src/spice2x/games/iidx/poke.cpp @@ -266,8 +266,8 @@ namespace games::iidx::poke { y *= rawinput::TOUCHSCREEN_RANGE_Y; } else if (GRAPHICS_IIDX_WSUB) { // Scale to windowed subscreen - x *= GRAPHICS_IIDX_WSUB_WIDTH; - y *= GRAPHICS_IIDX_WSUB_HEIGHT; + x *= GRAPHICS_WSUB_WIDTH; + y *= GRAPHICS_WSUB_HEIGHT; } else if (GENERIC_SUB_WINDOW_FULLSIZE || !overlay::OVERLAY->get_active()) { // Overlay is not present, scale to main screen if (GRAPHICS_WINDOWED) { diff --git a/src/spice2x/hooks/graphics/graphics.cpp b/src/spice2x/hooks/graphics/graphics.cpp index 9aa0521..ed178f3 100644 --- a/src/spice2x/hooks/graphics/graphics.cpp +++ b/src/spice2x/hooks/graphics/graphics.cpp @@ -374,6 +374,27 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window) { dwStyle &= ~(WS_MAXIMIZEBOX); } + if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) { + dwStyle &= ~(WS_OVERLAPPEDWINDOW); + } + + if (is_sdvx_sub_window) { + graphics_load_windowed_subscreen_parameters(); + if (GRAPHICS_WSUB_SIZE.has_value()) { + nWidth = GRAPHICS_WSUB_WIDTH; + nHeight = GRAPHICS_WSUB_HEIGHT; + } else { + GRAPHICS_WSUB_WIDTH = nWidth; + GRAPHICS_WSUB_HEIGHT = nHeight; + } + if (GRAPHICS_WSUB_POS.has_value()) { + x = GRAPHICS_WSUB_X; + y = GRAPHICS_WSUB_Y; + } else { + GRAPHICS_WSUB_X = x; + GRAPHICS_WSUB_Y = y; + } + } // call original HWND result = CreateWindowExA_orig(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, @@ -529,11 +550,11 @@ static BOOL WINAPI MoveWindow_hook(HWND hWnd, int X, int Y, int nWidth, int nHei dwStyle = GetWindowLongA(hWnd, GWL_STYLE); - SetRect(&rect, 0, 0, GRAPHICS_IIDX_WSUB_WIDTH, GRAPHICS_IIDX_WSUB_HEIGHT); + SetRect(&rect, 0, 0, GRAPHICS_WSUB_WIDTH, GRAPHICS_WSUB_HEIGHT); AdjustWindowRect(&rect, dwStyle, 0); - X = GRAPHICS_IIDX_WSUB_X; - Y = GRAPHICS_IIDX_WSUB_Y; + X = GRAPHICS_WSUB_X; + Y = GRAPHICS_WSUB_Y; nWidth = rect.right - rect.left; nHeight = rect.bottom - rect.top; @@ -865,6 +886,9 @@ void graphics_hook_subscreen_window(HWND hWnd) { WSUB_WNDPROC_ORIG = reinterpret_cast(GetWindowLongPtrA(hWnd, GWLP_WNDPROC)); SetWindowLongPtrA(hWnd, GWLP_WNDPROC, reinterpret_cast(WsubWindowProc)); } + if (GRAPHICS_WSUB_ALWAYS_ON_TOP) { + graphics_update_z_order(hWnd, true); + } } void graphics_screens_register(int screen) { diff --git a/src/spice2x/hooks/graphics/graphics.h b/src/spice2x/hooks/graphics/graphics.h index 8c80159..35263ac 100644 --- a/src/spice2x/hooks/graphics/graphics.h +++ b/src/spice2x/hooks/graphics/graphics.h @@ -50,12 +50,14 @@ extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP; extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE; extern bool GRAPHICS_IIDX_WSUB; -extern std::optional GRAPHICS_IIDX_WSUB_SIZE; -extern std::optional GRAPHICS_IIDX_WSUB_POS; -extern int GRAPHICS_IIDX_WSUB_WIDTH; -extern int GRAPHICS_IIDX_WSUB_HEIGHT; -extern int GRAPHICS_IIDX_WSUB_X; -extern int GRAPHICS_IIDX_WSUB_Y; +extern std::optional GRAPHICS_WSUB_SIZE; +extern std::optional GRAPHICS_WSUB_POS; +extern int GRAPHICS_WSUB_WIDTH; +extern int GRAPHICS_WSUB_HEIGHT; +extern int GRAPHICS_WSUB_X; +extern int GRAPHICS_WSUB_Y; +extern bool GRAPHICS_WSUB_BORDERLESS; +extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP; extern HWND TDJ_SUBSCREEN_WINDOW; extern HWND SDVX_SUBSCREEN_WINDOW; @@ -94,7 +96,7 @@ std::string graphics_screenshot_genpath(); void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void graphics_capture_initial_window(HWND hWnd); void graphics_update_window_style(HWND hWnd); -void graphics_update_z_order(HWND hWnd); +void graphics_update_z_order(HWND hWnd, bool always_on_top); void graphics_move_resize_window(HWND hWnd); bool graphics_window_change_crashes_game(); void graphics_load_windowed_subscreen_parameters(); diff --git a/src/spice2x/hooks/graphics/graphics_windowed.cpp b/src/spice2x/hooks/graphics/graphics_windowed.cpp index 185f843..2cb1ec2 100644 --- a/src/spice2x/hooks/graphics/graphics_windowed.cpp +++ b/src/spice2x/hooks/graphics/graphics_windowed.cpp @@ -26,12 +26,14 @@ bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false; // IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed bool GRAPHICS_IIDX_WSUB = false; -std::optional GRAPHICS_IIDX_WSUB_SIZE; -std::optional GRAPHICS_IIDX_WSUB_POS; -int GRAPHICS_IIDX_WSUB_WIDTH = 1280; -int GRAPHICS_IIDX_WSUB_HEIGHT = 720; -int GRAPHICS_IIDX_WSUB_X = 0; -int GRAPHICS_IIDX_WSUB_Y = 0; +std::optional GRAPHICS_WSUB_SIZE; +std::optional GRAPHICS_WSUB_POS; +int GRAPHICS_WSUB_WIDTH = 1280; +int GRAPHICS_WSUB_HEIGHT = 720; +int GRAPHICS_WSUB_X = 0; +int GRAPHICS_WSUB_Y = 0; +bool GRAPHICS_WSUB_BORDERLESS = false; +bool GRAPHICS_WSUB_ALWAYS_ON_TOP = false; // these flags are carefully constructed to ensure maximum compatibility // (e.g., DDR likes to hang when SetWindowPos is called with certain params) @@ -108,7 +110,7 @@ void graphics_capture_initial_window(HWND hWnd) { } if (cfg::SCREENRESIZE->window_always_on_top) { log_info("graphics-windowed", "change window z-order - always on top"); - graphics_update_z_order(hWnd); + graphics_update_z_order(hWnd, true); } // ensure spicetouch coordinates are initialized @@ -169,29 +171,29 @@ void graphics_load_windowed_subscreen_parameters() { log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called"); - if (GRAPHICS_IIDX_WSUB_SIZE.has_value()) { + if (GRAPHICS_WSUB_SIZE.has_value()) { log_debug( "graphics-windowed", - "graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_SIZE"); + "graphics_load_windowed_parameters - load GRAPHICS_WSUB_SIZE"); std::pair result; - if (parse_width_height(GRAPHICS_IIDX_WSUB_SIZE.value(), result)) { - GRAPHICS_IIDX_WSUB_WIDTH = result.first; - GRAPHICS_IIDX_WSUB_HEIGHT = result.second; + if (parse_width_height(GRAPHICS_WSUB_SIZE.value(), result)) { + GRAPHICS_WSUB_WIDTH = result.first; + GRAPHICS_WSUB_HEIGHT = result.second; } else { log_warning("graphics-windowed", "failed to parse -wsubsize"); } } - if (GRAPHICS_IIDX_WSUB_POS.has_value()) { + if (GRAPHICS_WSUB_POS.has_value()) { log_debug( "graphics-windowed", - "graphics_load_windowed_parameters - load GRAPHICS_IIDX_WSUB_POS"); + "graphics_load_windowed_parameters - load GRAPHICS_WSUB_POS"); std::pair result; - if (parse_width_height(GRAPHICS_IIDX_WSUB_POS.value(), result)) { - GRAPHICS_IIDX_WSUB_X = result.first; - GRAPHICS_IIDX_WSUB_Y = result.second; + if (parse_width_height(GRAPHICS_WSUB_POS.value(), result)) { + GRAPHICS_WSUB_X = result.first; + GRAPHICS_WSUB_Y = result.second; } else { log_warning("graphics-windowed", "failed to parse -wsubpos"); } @@ -398,7 +400,7 @@ void graphics_update_window_style(HWND hWnd) { log_debug("graphics-windowed", "graphics_update_window_style returned"); } -void graphics_update_z_order(HWND hWnd) { +void graphics_update_z_order(HWND hWnd, bool always_on_top) { if (!GRAPHICS_WINDOWED) { return; } @@ -406,7 +408,7 @@ void graphics_update_z_order(HWND hWnd) { log_debug("graphics-windowed", "graphics_update_z_order called"); HWND insert_after = nullptr; - if (cfg::SCREENRESIZE->window_always_on_top) { + if (always_on_top) { insert_after = HWND_TOPMOST; } else { insert_after = HWND_NOTOPMOST; diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index dfd7620..5652d75 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -976,12 +976,24 @@ int main_implementation(int argc, char *argv[]) { GRAPHICS_WINDOW_ALWAYS_ON_TOP = options[launcher::Options::spice2x_WindowAlwaysOnTop].value_bool(); GRAPHICS_WINDOW_BACKBUFFER_SCALE = options[launcher::Options::WindowForceScaling].value_bool(); - // IIDX Windowed Subscreen + // IIDX/SDVX Windowed Subscreen if (options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].is_active()) { - GRAPHICS_IIDX_WSUB_SIZE = options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].value_text(); + GRAPHICS_WSUB_SIZE = options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].value_text(); + } else if (options[launcher::Options::SDVXWindowedSubscreenSize].is_active()) { + GRAPHICS_WSUB_SIZE = options[launcher::Options::SDVXWindowedSubscreenSize].value_text(); } if (options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].is_active()) { - GRAPHICS_IIDX_WSUB_POS = options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].value_text(); + GRAPHICS_WSUB_POS = options[launcher::Options::spice2x_IIDXWindowedSubscreenPosition].value_text(); + } else if (options[launcher::Options::SDVXWindowedSubscreenPosition].is_active()) { + GRAPHICS_WSUB_POS = options[launcher::Options::SDVXWindowedSubscreenPosition].value_text(); + } + if (options[launcher::Options::IIDXWindowedSubscreenBorderless].value_bool() || + options[launcher::Options::SDVXWindowedSubscreenBorderless].value_bool()) { + GRAPHICS_WSUB_BORDERLESS = true; + } + if (options[launcher::Options::IIDXWindowedSubscreenAlwaysOnTop].value_bool() || + options[launcher::Options::SDVXWindowedSubscreenAlwaysOnTop].value_bool()) { + GRAPHICS_WSUB_ALWAYS_ON_TOP = true; } if (options[launcher::Options::spice2x_JubeatLegacyTouch].value_bool()) { diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index d91402d..093f4cd 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -1842,6 +1842,24 @@ static const std::vector OPTION_DEFINITIONS = { .game_name = "Beatmania IIDX", .category = "Graphics (Windowed)", }, + { + // IIDXWindowedSubscreenBorderless + .title = "IIDX Windowed Subscreen Borderless", + .name = "iidxwsubborderless", + .desc = "Remove window decoration from windowed subscreen", + .type = OptionType::Bool, + .game_name = "Beatmania IIDX", + .category = "Graphics (Windowed)", + }, + { + // IIDXWindowedSubscreenAlwaysOnTop + .title = "IIDX Windowed Subscreen Always On Top", + .name = "iidxwsubtop", + .desc = "Keep windowed subscreen on top", + .type = OptionType::Bool, + .game_name = "Beatmania IIDX", + .category = "Graphics (Windowed)", + }, { // spice2x_JubeatLegacyTouch .title = "JB Legacy Touch Targets", @@ -2153,6 +2171,44 @@ static const std::vector OPTION_DEFINITIONS = { {"2", "double buffer"}, {"3", "triple buffer"}, }, + }, + { + // SDVXWindowedSubscreenSize + .title = "SDVX Windowed Subscreen Size", + .name = "sdvxwsubsize", + .desc = "Size of the subscreen window. Defaults to (1920,1080)", + .type = OptionType::Text, + .setting_name = "1920,1080", + .game_name = "Sound Voltex", + .category = "Graphics (Windowed)", + }, + { + // SDVXWindowedSubscreenPosition + .title = "SDVX Windowed Subscreen Position", + .name = "sdvxwsubpos", + .desc = "Initial position of the subscreen window. Defaults to (0,0)", + .type = OptionType::Text, + .setting_name = "0,0", + .game_name = "Sound Voltex", + .category = "Graphics (Windowed)", + }, + { + // SDVXWindowedSubscreenBorderless + .title = "SDVX Windowed Subscreen Borderless", + .name = "sdvxwsubborderless", + .desc = "Remove window decoration from windowed subscreen", + .type = OptionType::Bool, + .game_name = "Sound Voltex", + .category = "Graphics (Windowed)", + }, + { + // SDVXWindowedSubscreenAlwaysOnTop + .title = "SDVX Windowed Subscreen Always On Top", + .name = "sdvxwsubtop", + .desc = "Keep windowed subscreen on top", + .type = OptionType::Bool, + .game_name = "Sound Voltex", + .category = "Graphics (Windowed)", } }; diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index 3f6ae1d..46d9ec9 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -207,6 +207,8 @@ namespace launcher { WindowForceScaling, spice2x_IIDXWindowedSubscreenSize, spice2x_IIDXWindowedSubscreenPosition, + IIDXWindowedSubscreenBorderless, + IIDXWindowedSubscreenAlwaysOnTop, spice2x_JubeatLegacyTouch, spice2x_RBTouchScale, spice2x_AsioForceUnload, @@ -231,6 +233,10 @@ namespace launcher { MidiNoteSustain, NostalgiaPoke, ForceBackBufferCount, + SDVXWindowedSubscreenSize, + SDVXWindowedSubscreenPosition, + SDVXWindowedSubscreenBorderless, + SDVXWindowedSubscreenAlwaysOnTop, }; enum class OptionsCategory { diff --git a/src/spice2x/overlay/windows/screen_resize.cpp b/src/spice2x/overlay/windows/screen_resize.cpp index ed242aa..f3c53b8 100644 --- a/src/spice2x/overlay/windows/screen_resize.cpp +++ b/src/spice2x/overlay/windows/screen_resize.cpp @@ -173,8 +173,8 @@ namespace overlay::windows { "Change window decoration. Resizable Window may not cause your mouse cursor to change, " "but you can still drag to resize. Disabled for some games due to incompatibility."); - if (ImGui::Checkbox("Always on Top", &cfg::SCREENRESIZE->window_always_on_top) ) { - graphics_update_z_order(window); + if (ImGui::Checkbox("Always On Top", &cfg::SCREENRESIZE->window_always_on_top) ) { + graphics_update_z_order(window, cfg::SCREENRESIZE->window_always_on_top); } ImGui::BeginDisabled(); ImGui::Checkbox("Forced Render Scaling", &GRAPHICS_WINDOW_BACKBUFFER_SCALE);