From 54ec9f272eeaf7b100108fb6bc3d6ad139fbd70b Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Fri, 10 Apr 2026 01:05:44 -0700 Subject: [PATCH] popn: new cab windowed mode (#621) ## Link to GitHub Issue or related Pull Request, if one exists #618 ## Description of change Get windowed mode to work. ## Testing tbd... i/o doesn't work right now but it launches two windows at least... --- .../hooks/graphics/backends/d3d9/d3d9_backend.cpp | 14 +++++++++++--- .../hooks/graphics/backends/d3d9/d3d9_device.cpp | 7 ++++--- src/spice2x/hooks/graphics/graphics.cpp | 11 +++++++++-- src/spice2x/hooks/graphics/graphics.h | 1 + src/spice2x/launcher/launcher.cpp | 4 ++++ src/spice2x/overlay/imgui/impl_spice.cpp | 3 ++- src/spice2x/touch/touch.cpp | 1 + 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp index 38c394d..9b7ac7b 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_backend.cpp @@ -15,6 +15,7 @@ #include "cfg/screen_resize.h" #include "games/gitadora/gitadora.h" #include "games/iidx/iidx.h" +#include "games/popn/popn.h" #include "games/sdvx/sdvx.h" #include "games/mfc/mfc.h" #include "games/io.h" @@ -675,6 +676,10 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::GetDeviceCaps(UINT Adapter, D3DDEVT } else if (GRAPHICS_FORCE_SINGLE_ADAPTER) { pCaps->NumberOfAdaptersInGroup = 1; } + } else if (games::popn::is_pikapika_model()) { + if (GRAPHICS_WINDOWED) { + pCaps->NumberOfAdaptersInGroup = 2; + } } return ret; @@ -1021,7 +1026,8 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx( // // note from MSDN: `pFullscreenDisplayMode` must be NULL for windowed mode. if (GRAPHICS_WINDOWED) { - if (avs::game::is_model({"LDJ", "KFC"}) && (BehaviorFlags & D3DCREATE_ADAPTERGROUP_DEVICE)) { + if (avs::game::is_model({"LDJ", "KFC", "M39"}) && + (BehaviorFlags & D3DCREATE_ADAPTERGROUP_DEVICE)) { log_misc("graphics::d3d9", "disabling adapter group device in windowed mode"); D3D9_BEHAVIOR_DISABLE |= D3DCREATE_ADAPTERGROUP_DEVICE; @@ -1093,7 +1099,8 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3D9::CreateDeviceEx( *ppReturnedDeviceInterface = new WrappedIDirect3DDevice9(hFocusWindow, *ppReturnedDeviceInterface); // initialize sub screen if IIDX/SDVX requested a multi-head context - if (avs::game::is_model({"LDJ", "KFC"}) && (orig_behavior_flags & D3DCREATE_ADAPTERGROUP_DEVICE)) { + if (avs::game::is_model({"LDJ", "KFC", "M39"}) && + (orig_behavior_flags & D3DCREATE_ADAPTERGROUP_DEVICE)) { graphics_d3d9_ldj_init_sub_screen(*ppReturnedDeviceInterface, &pPresentationParameters[1]); } } @@ -1396,7 +1403,8 @@ void graphics_d3d9_on_present( const bool is_tdj = avs::game::is_model("LDJ") && games::iidx::TDJ_MODE; const bool is_gfdm_arena = games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW; - if (is_vm || is_tdj || is_gfdm_arena) { + const bool is_pika = games::popn::is_pikapika_model(); + if (is_vm || is_tdj || is_gfdm_arena || is_pika) { graphics_d3d9_ldj_on_present(wrapped_device); } diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp index 37675e2..4870ba3 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp @@ -251,7 +251,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain( HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain); bool create_swap_chain = false; - if (avs::game::is_model({"LDJ", "KFC"})) { + if (avs::game::is_model({"LDJ", "KFC", "M39"})) { create_swap_chain = true; } else if (games::gitadora::is_arena_model() && games::gitadora::ARENA_SINGLE_WINDOW && @@ -263,6 +263,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain( if (SUCCEEDED(hr) && !sub_swapchain) { sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain); sub_swapchain->should_run_hooks = false; + log_info("graphics::d3d9", "created additional swap chain"); } else if (FAILED(hr) && !fake_sub_swapchain) { log_warning("graphics::d3d9", "failed to create sub swap chain, hr={}, using fake swap chain", @@ -305,7 +306,7 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain( } bool swap = false; - if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC"})) { + if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC", "M39"})) { swap = true; } if (games::gitadora::is_arena_model() && @@ -344,7 +345,7 @@ UINT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetNumberOfSwapChains() { UINT n = pReal->GetNumberOfSwapChains(); - if (sub_swapchain && avs::game::is_model({"LDJ", "KFC"})) { + if (sub_swapchain && avs::game::is_model({"LDJ", "KFC", "M39"})) { n += 1; } diff --git a/src/spice2x/hooks/graphics/graphics.cpp b/src/spice2x/hooks/graphics/graphics.cpp index 4d6821b..8b21216 100644 --- a/src/spice2x/hooks/graphics/graphics.cpp +++ b/src/spice2x/hooks/graphics/graphics.cpp @@ -38,6 +38,7 @@ struct CaptureData { HWND TDJ_SUBSCREEN_WINDOW = nullptr; HWND SDVX_SUBSCREEN_WINDOW = nullptr; HWND GFDM_SUBSCREEN_WINDOW = nullptr; +HWND POPN_SUBSCREEN_WINDOW = nullptr; // icon static HICON WINDOW_ICON = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(MAINICON)); @@ -334,17 +335,18 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC bool is_tdj_sub_window = avs::game::is_model("LDJ") && window_name.ends_with(" sub"); bool is_sdvx_sub_window = avs::game::is_model("KFC") && window_name.ends_with(" Sub Screen"); + bool is_popn_sub_window = avs::game::is_model("M39") && window_name.ends_with("Sub Screen"); bool is_gfdm_sub_window = games::gitadora::is_arena_model() && window_name.ends_with("SMALL"); // hide maximize button (prevent misaligned touches) - if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window) { + if ((is_tdj_sub_window && GRAPHICS_IIDX_WSUB) || is_sdvx_sub_window || is_gfdm_sub_window || is_popn_sub_window) { dwStyle &= ~(WS_MAXIMIZEBOX); } // mouse clicks become misaligned when resized if (is_gfdm_sub_window) { dwStyle &= ~(WS_SIZEBOX); } - if ((is_tdj_sub_window || is_sdvx_sub_window) && GRAPHICS_WSUB_BORDERLESS) { + if ((is_tdj_sub_window || is_sdvx_sub_window || is_popn_sub_window) && GRAPHICS_WSUB_BORDERLESS) { dwStyle &= ~(WS_OVERLAPPEDWINDOW); } @@ -393,6 +395,11 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC graphics_hook_subscreen_window(GFDM_SUBSCREEN_WINDOW); } + if (is_popn_sub_window) { + POPN_SUBSCREEN_WINDOW = result; + graphics_hook_subscreen_window(POPN_SUBSCREEN_WINDOW); + } + disable_touch_indicators(result); log_misc( "graphics", diff --git a/src/spice2x/hooks/graphics/graphics.h b/src/spice2x/hooks/graphics/graphics.h index c09343c..99077b9 100644 --- a/src/spice2x/hooks/graphics/graphics.h +++ b/src/spice2x/hooks/graphics/graphics.h @@ -63,6 +63,7 @@ extern bool GRAPHICS_WSUB_BORDERLESS; extern bool GRAPHICS_WSUB_ALWAYS_ON_TOP; extern HWND TDJ_SUBSCREEN_WINDOW; extern HWND SDVX_SUBSCREEN_WINDOW; +extern HWND POPN_SUBSCREEN_WINDOW; extern HWND GFDM_SUBSCREEN_WINDOW; extern bool SUBSCREEN_FORCE_REDRAW; diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index daf309f..dc23eb0 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -1764,6 +1764,10 @@ int main_implementation(int argc, char *argv[]) { if (check_dll("libaio-iob2_video.dll")) { // pop'n music (pika cabinet) attach_popn = true; + // automatically show cursor in windowed mode to interact with second window (sub) + if (GRAPHICS_WINDOWED) { + GRAPHICS_SHOW_CURSOR = true; + } } else { // HELLO! Pop'n Music attach_hpm = true; diff --git a/src/spice2x/overlay/imgui/impl_spice.cpp b/src/spice2x/overlay/imgui/impl_spice.cpp index 721a2de..59eb51c 100644 --- a/src/spice2x/overlay/imgui/impl_spice.cpp +++ b/src/spice2x/overlay/imgui/impl_spice.cpp @@ -265,7 +265,8 @@ static void ImGui_ImplSpice_UpdateMousePos() { // trigger any overlay, don't process anything else const auto is_windowed_subscreen = (GRAPHICS_IIDX_WSUB && active_window == TDJ_SUBSCREEN_WINDOW) || - (active_window == SDVX_SUBSCREEN_WINDOW); + (active_window == SDVX_SUBSCREEN_WINDOW) || + (active_window == POPN_SUBSCREEN_WINDOW); if (is_windowed_subscreen) { io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); return; diff --git a/src/spice2x/touch/touch.cpp b/src/spice2x/touch/touch.cpp index 849dd0c..43edd5f 100644 --- a/src/spice2x/touch/touch.cpp +++ b/src/spice2x/touch/touch.cpp @@ -243,6 +243,7 @@ static LRESULT CALLBACK SpiceTouchWndProc(HWND hWnd, UINT msg, WPARAM wParam, LP const auto is_windowed_sub = (GRAPHICS_IIDX_WSUB && hWnd == TDJ_SUBSCREEN_WINDOW) || (hWnd == SDVX_SUBSCREEN_WINDOW) || + (hWnd == POPN_SUBSCREEN_WINDOW) || (hWnd == GFDM_SUBSCREEN_WINDOW); if (msg == WM_CLOSE) {