From 127d31a85cedcafba8eaee4fc9619a611a46be37 Mon Sep 17 00:00:00 2001 From: Emma Date: Thu, 16 Apr 2026 02:51:53 +0100 Subject: [PATCH 1/9] graphics: add option to disable win11 rounded corners on windows (#641) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description of change This adds a toggleable option to remove the rounded window corners when running a game, or a subscreen, in a window on Windows 11 and upwards. ## Testing On Windows 11: Resort Anthem, MÚSECA and EPOLIS were tested and the window corners were removed from both main windows and the TDJ subscreen when running in windowed mode, with no change in fullscreen mode. On Windows 7: IIDX18 was tested with no change, the call to `DwmSetWindowAttribute` is ignored as `DWMWA_WINDOW_CORNER_PREFERENCE` doesn't exist. For XP builds, the call is compiled out to avoid linking against dwmapi and windows_dll_compat_checker returns successfully when testing against the XP Embedded image. I can't test it on XP, but it ran on Vista. --- src/spice2x/cfg/screen_resize.h | 1 + src/spice2x/hooks/graphics/graphics.cpp | 3 ++ src/spice2x/hooks/graphics/graphics.h | 5 ++++ .../hooks/graphics/graphics_windowed.cpp | 30 +++++++++++++++++++ src/spice2x/launcher/launcher.cpp | 1 + src/spice2x/launcher/options.cpp | 8 +++++ src/spice2x/launcher/options.h | 1 + 7 files changed, 49 insertions(+) diff --git a/src/spice2x/cfg/screen_resize.h b/src/spice2x/cfg/screen_resize.h index f986028..aa4cdcc 100644 --- a/src/spice2x/cfg/screen_resize.h +++ b/src/spice2x/cfg/screen_resize.h @@ -60,6 +60,7 @@ namespace cfg { // window = rectangle including the frame // client = just the content area without frames. bool window_always_on_top = false; + bool window_disable_round_corners = false; bool client_keep_aspect_ratio = true; bool enable_window_resize = false; int window_decoration = 0; // enum type WindowDecorationMode diff --git a/src/spice2x/hooks/graphics/graphics.cpp b/src/spice2x/hooks/graphics/graphics.cpp index 3d54e17..00522a1 100644 --- a/src/spice2x/hooks/graphics/graphics.cpp +++ b/src/spice2x/hooks/graphics/graphics.cpp @@ -924,6 +924,9 @@ void graphics_hook_subscreen_window(HWND hWnd) { if (GRAPHICS_WSUB_ALWAYS_ON_TOP) { graphics_update_z_order(hWnd, true); } + if (GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS) { + graphics_set_corner_preference(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 f49ce21..060fa73 100644 --- a/src/spice2x/hooks/graphics/graphics.h +++ b/src/spice2x/hooks/graphics/graphics.h @@ -7,6 +7,9 @@ #include #include +#if !SPICE_XP +#include +#endif #include "external/toojpeg/toojpeg.h" @@ -50,6 +53,7 @@ extern std::optional> GRAPHICS_WINDOW_SIZE; extern std::optional GRAPHICS_WINDOW_POS; extern bool GRAPHICS_WINDOW_ALWAYS_ON_TOP; extern bool GRAPHICS_WINDOW_BACKBUFFER_SCALE; +extern bool GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS; extern std::optional GRAPHICS_HOOKED_WINDOW; extern bool GRAPHICS_IIDX_WSUB; @@ -103,6 +107,7 @@ void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara void graphics_capture_initial_window(HWND hWnd); void graphics_update_window_style(HWND hWnd, bool force_update=false); void graphics_update_z_order(HWND hWnd, bool always_on_top); +void graphics_set_corner_preference(HWND hWnd, bool disable); void graphics_move_resize_window(HWND hWnd); bool graphics_window_options_breaks_game(); bool graphics_window_decoration_change_crashes_game(); diff --git a/src/spice2x/hooks/graphics/graphics_windowed.cpp b/src/spice2x/hooks/graphics/graphics_windowed.cpp index 12a4f8a..56eafcc 100644 --- a/src/spice2x/hooks/graphics/graphics_windowed.cpp +++ b/src/spice2x/hooks/graphics/graphics_windowed.cpp @@ -24,6 +24,7 @@ std::optional> GRAPHICS_WINDOW_SIZE; std::optional GRAPHICS_WINDOW_POS; bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false; bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false; +bool GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS = false; std::optional GRAPHICS_HOOKED_WINDOW; // IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed @@ -132,6 +133,10 @@ void graphics_capture_initial_window(HWND hWnd) { log_info("graphics-windowed", "[{}] change window z-order - always on top", fmt::ptr(hWnd)); graphics_update_z_order(hWnd, true); } + if (cfg::SCREENRESIZE->window_disable_round_corners) { + log_info("graphics-windowed", "[{}] disabling rounded corners", fmt::ptr(hWnd)); + graphics_set_corner_preference(hWnd, true); + } // ensure spicetouch coordinates are initialized update_spicetouch_window_dimensions(hWnd); @@ -181,6 +186,10 @@ void graphics_load_windowed_parameters() { cfg::SCREENRESIZE->window_always_on_top = true; } + if (GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS) { + cfg::SCREENRESIZE->window_disable_round_corners = true; + } + log_debug("graphics-windowed", "graphics_load_windowed_parameters returned"); } @@ -469,6 +478,27 @@ void graphics_update_z_order(HWND hWnd, bool always_on_top) { log_debug("graphics-windowed", "graphics_update_z_order returned"); } +void graphics_set_corner_preference(HWND hWnd, bool disable) { + if (!GRAPHICS_WINDOWED) { + return; + } + +#if !SPICE_XP + log_debug("graphics-windowed", "graphics_set_corner_preference called"); + + DWM_WINDOW_CORNER_PREFERENCE corner_preference = disable ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT; + DwmSetWindowAttribute( + hWnd, + DWMWA_WINDOW_CORNER_PREFERENCE, + &corner_preference, sizeof(corner_preference)); + + log_debug("graphics-windowed", "graphics_set_corner_preference returned"); +#else + // don't link against DwmSetWindowAttribute on XP + log_debug("graphics-windowed", "graphics_set_corner_preference called; ignoring"); +#endif +} + void graphics_move_resize_window(HWND hWnd) { if (!GRAPHICS_WINDOWED) { return; diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index ad6789c..dea4527 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -1117,6 +1117,7 @@ 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(); + GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS = options[launcher::Options::WindowDisableRoundedCorners].value_bool(); // IIDX/SDVX Windowed Subscreen if (options[launcher::Options::spice2x_IIDXWindowedSubscreenSize].is_active()) { diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index 66c6e28..0862188 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -2218,6 +2218,14 @@ static const std::vector OPTION_DEFINITIONS = { .type = OptionType::Bool, .category = "Graphics (Windowed)", }, + { + // WindowDisableRoundedCorners + .title = "Disable Round Window Corners", + .name = "windownoroundcorners", + .desc = "Windows 11 and above only: Disables rounded corners on the game window(s).", + .type = OptionType::Bool, + .category = "Graphics (Windowed)", + }, { // spice2x_IIDXWindowedSubscreenSize .title = "IIDX Windowed Subscreen Size", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index bf1d03d..6a3934d 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -232,6 +232,7 @@ namespace launcher { spice2x_WindowPosition, spice2x_WindowAlwaysOnTop, WindowForceScaling, + WindowDisableRoundedCorners, spice2x_IIDXWindowedSubscreenSize, spice2x_IIDXWindowedSubscreenPosition, IIDXWindowedSubscreenBorderless, From 231c998cc0605b9a39e27ded3a7c191ffd0acbc6 Mon Sep 17 00:00:00 2001 From: Emma Date: Thu, 16 Apr 2026 02:53:21 +0100 Subject: [PATCH 2/9] acio: implement ICCA slotted reader emulation for iidx18 (#642) ## Link to GitHub Issue or related Pull Request, if one exists #121 ## Description of change This implements a seperate ICCA card workflow for the slotted readers acting how Resort Anthem expects, enabling cards to be inserted. Additionally this fixes a bug where both card units would be treated the same (for keypad reads) under Resort Anthem. The slotted reader workflow was implemented as a seperate function for two reasons - it would be nice to not mess with any existing code to avoid breaking all the functional games, and the way the slotted readers are handled by RA seemed different enough to how everything was already designed to where it didn't make sense to keep adding special edge cases for RA. ## Testing IIDX18 was tested and cards could be inserted on both P1 and P2 side. IIDX19 through 22 was tested to make sure nothing had broken for wavepass emulation. ## Known Issues In IIDX18, when a card isn't set but the insert key is pressed, after the card timeout the game will error out with an invalid state error. --- src/spice2x/acio/icca/icca.cpp | 142 ++++++++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 20 deletions(-) diff --git a/src/spice2x/acio/icca/icca.cpp b/src/spice2x/acio/icca/icca.cpp index 82d3b4f..f8493cb 100644 --- a/src/spice2x/acio/icca/icca.cpp +++ b/src/spice2x/acio/icca/icca.cpp @@ -35,24 +35,33 @@ struct ICCA_STATUS_LA9 { static_assert(sizeof(struct ICCA_STATUS) == 24, "ICCA_STATUS must be 24 bytes"); enum ICCA_WORKFLOW { - STEP, - SLEEP, - START, - INIT, - READY, - GET_USERID, - ACTIVE, - EJECT, - EJECT_CHECK, - END, - CLOSE_EJECT, - CLOSE_E_CHK, - CLOSE_END, - ERR_GETUID = -2 + STEP = 0, + SLEEP = 1, + START = 2, + INIT = 3, + READY = 4, + GET_USERID = 5, + ACTIVE = 6, + EJECT = 7, + EJECT_CHECK = 8, + END = 9, + CLOSE_EJECT = 10, + CLOSE_E_CHK = 11, + CLOSE_END = 12, + ERR_GETUID = -2, + ERR_REMOVED = -11 }; + +// used by RA as the status_code +enum ICCA_STATUS_CODE { + EMPTY = 1, + ENGAGED = 2 +}; + struct ICCA_UNIT { struct ICCA_STATUS status {}; enum ICCA_WORKFLOW state = STEP; + enum ICCA_WORKFLOW state_jdz = SLEEP; bool card_cmd_pressed = false; bool card_in = false; double card_in_time = 0.0; @@ -75,6 +84,11 @@ static inline int icca_get_active_count() { } static inline int icca_get_unit_id(int unit_id) { + // on RA the unit_id is zero-indexed + if (avs::game::is_model("JDZ")) { + return unit_id; + } + if (icca_get_active_count() < 2) return 1; else { @@ -122,9 +136,15 @@ static inline void update_card(int unit_id) { IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid); unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE; + unit->state_jdz = ACTIVE; unit->status.error = 0; + unit->status.front_sensor = 1; + unit->status.rear_sensor = 1; } else { unit->state = ERR_GETUID; + unit->state_jdz = ERR_GETUID; + unit->status.front_sensor = 1; + unit->status.rear_sensor = 1; memset(unit->status.uid, 0, 8); } unit->card_in = true; @@ -132,10 +152,25 @@ static inline void update_card(int unit_id) { } else if (unit->state == EJECT_CHECK) { unit->state = SLEEP; unit->card_in = false; + unit->status.front_sensor = 0; + unit->status.rear_sensor = 0; + } else if (unit->state_jdz == READY) { + unit->state = GET_USERID; + unit->state_jdz = GET_USERID; + unit->status.error = 0; + unit->status.front_sensor = 1; + unit->status.rear_sensor = 1; + unit->card_in = true; + unit->card_in_time = get_performance_seconds(); } } else { unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE; + if (unit->state_jdz == READY) { + unit->state_jdz = GET_USERID; + } unit->status.error = 0; + unit->status.front_sensor = 1; + unit->status.rear_sensor = 1; unit->card_in = true; unit->card_in_time = get_performance_seconds(); } @@ -144,6 +179,12 @@ static inline void update_card(int unit_id) { unit->state = CLOSE_EJECT; if (fabs(get_performance_seconds() - unit->card_in_time) > CARD_TIMEOUT) { unit->card_in = false; + memset(unit->status.uid, 0, 8); + if (unit->state_jdz == ACTIVE || unit->state_jdz == GET_USERID) { + unit->state_jdz = ERR_REMOVED; + } + unit->status.front_sensor = 0; + unit->status.rear_sensor = 0; } } @@ -302,7 +343,11 @@ static char __cdecl ac_io_icca_get_status(void *a1, void *a2) { // copy state to output buffer ICCA_UNIT *unit = &ICCA_UNITS[unit_id]; - unit->status.status_code = unit->state; + if (avs::game::is_model("JDZ")) { + unit->status.status_code = unit->card_in ? ENGAGED : EMPTY; + } else { + unit->status.status_code = unit->state; + } memcpy(status, &unit->status, sizeof(struct ICCA_STATUS)); // funny workaround @@ -387,6 +432,11 @@ static char __cdecl ac_io_icca_req_uid(int unit_id) { static int __cdecl ac_io_icca_req_uid_isfinished(int unit_id, DWORD *read_state) { unit_id = icca_get_unit_id(unit_id); ICCA_UNIT *unit = &ICCA_UNITS[unit_id]; + if (avs::game::is_model("JDZ")) { + // hack for RA - only ever seen it as this + *read_state = 8; + return 1; + } if (unit->card_in) { if (fabs(get_performance_seconds() - unit->card_in_time) < CARD_TIMEOUT) { unit->state = END; @@ -403,16 +453,61 @@ static int __cdecl ac_io_icca_send_keep_alive_packet(int a1, int a2, int a3) { return 0; } +static int __cdecl ac_io_icca_workflow_jdz(int workflow, int unit_id) { + + unit_id = icca_get_unit_id(unit_id); + ICCA_UNIT *unit = &ICCA_UNITS[unit_id]; + switch (workflow) { + // RA uses "STEP" as more of a polling thing it seems + case STEP: + switch (unit->state_jdz) { + case CLOSE_EJECT: + unit->state_jdz = CLOSE_E_CHK; + break; + case CLOSE_E_CHK: + unit->state_jdz = CLOSE_END; + break; + case CLOSE_END: + unit->state_jdz = SLEEP; + break; + default: + break; + } + break; + case SLEEP: + unit->state = SLEEP; + break; + case START: + unit->state_jdz = INIT; + break; + case INIT: + case READY: + unit->state_jdz = READY; + break; + case CLOSE_EJECT: + unit->state_jdz = CLOSE_EJECT; + return CLOSE_E_CHK; + case CLOSE_END: + unit->state_jdz = SLEEP; + break; + default: + // should probably log a warning here + break; + } + return unit->state_jdz; +} + static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) { + // RA uses a different workflow process for the slotted readers; treat it differently + if (avs::game::is_model("JDZ")) { + return ac_io_icca_workflow_jdz(workflow, unit_id); + } unit_id = icca_get_unit_id(unit_id); ICCA_UNIT *unit = &ICCA_UNITS[unit_id]; switch (workflow) { case STEP: - if (avs::game::is_model("JDZ")) - unit->state = SLEEP; - else - unit->state = STEP; + unit->state = STEP; break; case SLEEP: unit->state = SLEEP; @@ -441,7 +536,6 @@ static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) { default: break; } - return unit->state; } @@ -467,6 +561,14 @@ acio::ICCAModule::ICCAModule(HMODULE module, acio::HookMode hookMode) : ACIOModu void acio::ICCAModule::attach() { ACIOModule::attach(); + // workaround for RA; ac_io_icca_cardunit_init is never called so treat like both are initialized + if (avs::game::is_model("JDZ")) { + ICCA_UNITS[0].initialized = true; + ICCA_UNITS[1].initialized = true; + + CARD_TIMEOUT = 10.0; // keep the card in the slot for longer + } + // hooks ACIO_MODULE_HOOK(ac_io_icca_cardunit_init); ACIO_MODULE_HOOK(ac_io_icca_cardunit_init_isfinished); From b38160d6c23247c542e1f8f565c963c419d4c868 Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:31:26 -0700 Subject: [PATCH 3/9] popn: native touch option, keypad warning (#640) #639 the usual --- src/spice2x/games/popn/popn.cpp | 12 +++++-- src/spice2x/games/popn/popn.h | 1 + src/spice2x/launcher/launcher.cpp | 3 ++ src/spice2x/launcher/options.cpp | 12 +++++++ src/spice2x/launcher/options.h | 1 + src/spice2x/overlay/windows/config.cpp | 43 +++++++++++++++++++------- src/spice2x/overlay/windows/config.h | 8 +++-- 7 files changed, 62 insertions(+), 18 deletions(-) diff --git a/src/spice2x/games/popn/popn.cpp b/src/spice2x/games/popn/popn.cpp index ac3f0f1..0ae443e 100644 --- a/src/spice2x/games/popn/popn.cpp +++ b/src/spice2x/games/popn/popn.cpp @@ -19,11 +19,13 @@ #include "util/sysutils.h" #include "io.h" #include "util/deferlog.h" +#include "misc/nativetouchhook.h" #include "misc/wintouchemu.h" namespace games::popn { bool SHOW_PIKA_MONITOR_WARNING = false; + bool NATIVE_TOUCH = false; #if SPICE64 && !SPICE_XP @@ -670,9 +672,13 @@ namespace games::popn { // set third column to 0 and it will work with BIO2 if (!GRAPHICS_WINDOWED) { - wintouchemu::FORCE = true; - wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true; - wintouchemu::hook_title_ends("", "Main Screen", avs::game::DLL_INSTANCE); + if (NATIVE_TOUCH) { + nativetouchhook::hook(avs::game::DLL_INSTANCE); + } else { + wintouchemu::FORCE = true; + wintouchemu::INJECT_MOUSE_AS_WM_TOUCH = true; + wintouchemu::hook_title_ends("", "Main Screen", avs::game::DLL_INSTANCE); + } } sysutils::hook_EnumDisplayDevicesA(); diff --git a/src/spice2x/games/popn/popn.h b/src/spice2x/games/popn/popn.h index caebe6e..2e57706 100644 --- a/src/spice2x/games/popn/popn.h +++ b/src/spice2x/games/popn/popn.h @@ -22,6 +22,7 @@ namespace games::popn { #endif extern bool SHOW_PIKA_MONITOR_WARNING; + extern bool NATIVE_TOUCH; class POPNGame : public games::Game { public: diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index dea4527..b533c99 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -597,6 +597,9 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::PopnSubMonitorOverride].is_active()) { sysutils::SECOND_MONITOR_OVERRIDE = options[launcher::Options::PopnSubMonitorOverride].value_text(); } + if (options[launcher::Options::PopnNativeTouch].value_bool()) { + games::popn::NATIVE_TOUCH = true; + } if (options[launcher::Options::LoadMetalGearArcadeModule].value_bool()) { attach_mga = true; } diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index 0862188..1142122 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -1008,6 +1008,18 @@ static const std::vector OPTION_DEFINITIONS = { .category = "Monitor", .picker = OptionPickerType::Monitor, }, + { + // PopnNativeTouch + .title = "Pop'n Music PikaPika Native Touch Handling", + .name = "popnnativetouch", + .desc = "Disables touch hooks and lets the game access a touch screen directly. " + "Requires a touch screen to be connected as a secondary monitor. " + "Touch input must be routed to the primary screen via Windows Tablet PC settings. " + "Enable this when you get duplicate touch inputs from an actual touch screen.", + .type = OptionType::Bool, + .game_name = "Pop'n Music", + .category = "Game Options (Advanced)", + }, { .title = "Force Load HELLO! Pop'n Music Module", .name = "hpm", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index 6a3934d..49974c5 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -101,6 +101,7 @@ namespace launcher { PopnMusicForceSDMode, PopnNoSub, PopnSubMonitorOverride, + PopnNativeTouch, LoadHelloPopnMusicModule, LoadGitaDoraModule, GitaDoraTwoChannelAudio, diff --git a/src/spice2x/overlay/windows/config.cpp b/src/spice2x/overlay/windows/config.cpp index 1ff8bca..1a9683f 100644 --- a/src/spice2x/overlay/windows/config.cpp +++ b/src/spice2x/overlay/windows/config.cpp @@ -17,6 +17,7 @@ #include "external/imgui/misc/cpp/imgui_stdlib.h" #include "games/io.h" #include "games/sdvx/sdvx.h" +#include "games/popn/popn.h" #include "avs/core.h" #include "avs/ea3.h" #include "avs/game.h" @@ -283,18 +284,8 @@ namespace overlay::windows { // keypad buttons ImGui::TextUnformatted(""); - if (this->games_selected_name == "Beatmania IIDX") { - ImGui::Indent(INDENT); - ImGui::TextColored( - ImVec4(1, 0.5f, 0.5f, 1.f), - "WARNING: Lightning Model (TDJ) I/O will ignore the keypad!"); - ImGui::TextWrapped( - "Use Toggle Sub Screen button to show the overlay and use your mouse, " - "connect using SpiceCompanion app, or connect a touch screen to enter " - "the PIN."); - ImGui::Unindent(INDENT); - ImGui::TextUnformatted(""); - } + this->build_keypad_warning(); + auto keypad_buttons = games::get_buttons_keypads(this->games_selected_name); auto keypad_count = eamuse_get_game_keypads_name(); if (keypad_count == 1) { @@ -602,6 +593,34 @@ namespace overlay::windows { } } + void Config::build_keypad_warning() { + // not checking the -iidxtdj option here, we could do that in the future + const bool is_tdj = this->games_selected_name == "Beatmania IIDX"; + + // not using games::popn::is_pikapika_model() here since that always returns false on 32-bit + const bool is_popn = this->games_selected_name == "Pop'n Music" && avs::game::SPEC[0] == 'D'; + if (!is_tdj && !is_popn) { + return; + } + + ImGui::Indent(INDENT); + if (is_tdj) { + ImGui::TextColored( + ImVec4(1, 0.5f, 0.5f, 1.f), + "WARNING: Lightning Model (TDJ) I/O will ignore keypad number input!"); + } else if (is_popn) { + ImGui::TextColored( + ImVec4(1, 0.5f, 0.5f, 1.f), + "WARNING: PikaPika Pop-Kun model will ignore keypad number input!"); + } + ImGui::TextWrapped( + "Use Toggle Sub Screen button (Overlay tab) to show the overlay and use your mouse, " + "connect using SpiceCompanion app, or connect a touch screen to enter " + "the PIN."); + ImGui::Unindent(INDENT); + ImGui::TextUnformatted(""); + } + void Config::build_buttons(const std::string &name, std::vector