sdvx: fix api touch and -sdvxnosub sub window (#868)
## Link to GitHub Issue or related Pull Request, if one exists Related: #859 and #862. ## Description of change Three SDVX (Valkyrie model) fixes: * **API touch went to the wrong window.** SDVX registers touch on both its Main Screen and Sub Screen windows, and the native injector kept whichever attached last. The touch surface is now published explicitly: sub screen window when windowed, main window in fullscreen. * **Landscape never rotated API coordinates.** Synthetic contacts bypass `transform::hardware_to_game`, so they missed the rotation a real finger gets. Applied before injection now, as portrait already did. Extracted to `sdvx_landscape_rotate` so both paths share it; gated on the native path since `wintouchemu` rotates via the subscreen overlay instead. * **`-sdvxnosub` didn't hide the sub window in fullscreen.** `ShowWindow_hook` had branches for GITADORA, pop'n and IIDX but not SDVX. Added the missing one. ## Testing Tested Nabla
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include "touch/touch.h"
|
||||
#include "touch/native/inject.h"
|
||||
#include "touch/native/nativetouchhook.h"
|
||||
#include "touch/native/transform.h"
|
||||
#include "util/utils.h"
|
||||
#include "games/gitadora/gitadora.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
@@ -41,6 +42,35 @@ namespace api::modules {
|
||||
return nativetouch::inject::inject_synthetic_touch(position, true);
|
||||
}
|
||||
|
||||
// map API coordinates onto the touch space SDVX reads, which depends on how it is displayed
|
||||
static void sdvx_touch_errata(
|
||||
int &x, int &y, bool use_native, int canvas_w, int canvas_h) {
|
||||
|
||||
// windowed coordinates already match the sub screen window they land on
|
||||
if (GRAPHICS_WINDOWED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// landscape mode: native injection hands the game these coordinates
|
||||
// unchanged, so apply the rotation the touchscreen gets, while wintouchemu instead
|
||||
// rotates them later through the subscreen overlay
|
||||
if (GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
if (use_native) {
|
||||
POINT position { x, y };
|
||||
if (nativetouch::transform::sdvx_landscape_rotate(&position, canvas_w, canvas_h)) {
|
||||
x = position.x;
|
||||
y = position.y;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// rotate into the portrait touch space
|
||||
const int x_raw = x;
|
||||
x = canvas_w - y;
|
||||
y = x_raw;
|
||||
}
|
||||
|
||||
Touch::Touch() : Module("touch") {
|
||||
is_sdvx = avs::game::is_model("KFC");
|
||||
|
||||
@@ -56,8 +86,8 @@ namespace api::modules {
|
||||
native_canvas_w = 0;
|
||||
native_canvas_h = 0;
|
||||
if (is_sdvx) {
|
||||
// windowed and landscape API coordinates already match the primary screen orientation;
|
||||
// fullscreen portrait coordinates are rotated by apply_touch_errata
|
||||
// windowed API coordinates land on the sub screen window as-is; fullscreen
|
||||
// coordinates are rotated into the game's touch space by apply_touch_errata
|
||||
const bool landscape_coordinates =
|
||||
GRAPHICS_WINDOWED || GRAPHICS_FS_ORIENTATION_SWAP;
|
||||
native_canvas_w = landscape_coordinates ? 1920 : 1080;
|
||||
@@ -217,19 +247,14 @@ namespace api::modules {
|
||||
}
|
||||
|
||||
void Touch::apply_touch_errata(int &x, int &y) {
|
||||
int x_raw = x;
|
||||
int y_raw = y;
|
||||
|
||||
if (is_tdj_fhd) {
|
||||
// deal with TDJ FHD resolution mismatch (upgrade 720p to 1080p)
|
||||
// we don't know what screen is being shown on the companion and the API doesn't specify
|
||||
// the target of the touch events so just assume it's the sub screen
|
||||
x = x_raw * 1920 / 1280;
|
||||
y = y_raw * 1080 / 720;
|
||||
} else if (is_sdvx && !GRAPHICS_WINDOWED && !GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
// rotate API coordinates into SDVX's portrait touch space
|
||||
x = 1080 - y_raw;
|
||||
y = x_raw;
|
||||
x = x * 1920 / 1280;
|
||||
y = y * 1080 / 720;
|
||||
} else if (is_sdvx) {
|
||||
sdvx_touch_errata(x, y, use_native, native_canvas_w, native_canvas_h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,8 +629,10 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_sdvx = avs::game::is_model("KFC");
|
||||
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_sdvx_sub_window = is_sdvx && window_name.ends_with(" Sub Screen");
|
||||
bool is_sdvx_main_window = is_sdvx && window_name.ends_with(" Main Screen");
|
||||
bool is_popn_sub_window = avs::game::is_model("M39") && window_name.ends_with("Sub Screen");
|
||||
const std::string gfdm_window_name = games::gitadora::is_arena_model()
|
||||
? gitadora_canonical_window_name(effective_window_name)
|
||||
@@ -733,6 +735,20 @@ static HWND WINAPI CreateWindowExA_hook(DWORD dwExStyle, LPCSTR lpClassName, LPC
|
||||
graphics_hook_subscreen_window(SDVX_SUBSCREEN_WINDOW);
|
||||
}
|
||||
|
||||
// SDVX registers touch on both windows, so name the one synthetic touches must land on
|
||||
// instead of letting window creation order decide: the sub screen window when windowed,
|
||||
// the main window in fullscreen since the game reads it in primary-display coordinates
|
||||
if (nativetouch::is_hooked() &&
|
||||
result != nullptr &&
|
||||
(GRAPHICS_WINDOWED ? is_sdvx_sub_window : is_sdvx_main_window)) {
|
||||
log_misc(
|
||||
"graphics",
|
||||
"SDVX touch surface is {}, {}",
|
||||
fmt::ptr(result),
|
||||
window_name);
|
||||
nativetouch::inject::set_preferred_injection_window(result);
|
||||
}
|
||||
|
||||
// only hook touch window if multiple windows are allowed
|
||||
if (gfdm_window_name == "LEFT" || gfdm_window_name == "RIGHT") {
|
||||
gitadora_remember_window(result, gfdm_window_name);
|
||||
@@ -1122,6 +1138,15 @@ static BOOL WINAPI ShowWindow_hook(HWND hWnd, int nCmdShow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// fullscreen SDVX keeps two adapters so the subscreen overlay can draw, so the game still
|
||||
// creates the sub window even when the user asked for it to be gone
|
||||
if (avs::game::is_model("KFC") &&
|
||||
GRAPHICS_PREVENT_SECONDARY_WINDOWS &&
|
||||
hWnd == SDVX_SUBSCREEN_WINDOW) {
|
||||
log_info("graphics", "ShowWindow_hook - hiding sub window {}", fmt::ptr(hWnd));
|
||||
return true;
|
||||
}
|
||||
|
||||
// call original
|
||||
return ShowWindow_orig(hWnd, nCmdShow);
|
||||
}
|
||||
|
||||
@@ -94,23 +94,28 @@ namespace nativetouch::transform {
|
||||
return overlay::OVERLAY->transform_touch_point(&position->x, &position->y);
|
||||
}
|
||||
|
||||
// the digitizer is mapped to the zero-based primary display, while SDVX
|
||||
// still expects portrait coordinates when its image is rendered in landscape:
|
||||
// SDVX still expects portrait coordinates when its image is rendered in landscape:
|
||||
// (x, y) -> (width * (1 - y / height), height * x / width).
|
||||
bool sdvx_landscape_rotate(POINT *position, LONG width, LONG height) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto input_x = position->x;
|
||||
position->x = width - MulDiv(position->y, width, height);
|
||||
position->y = MulDiv(input_x, height, width);
|
||||
return true;
|
||||
}
|
||||
|
||||
// the digitizer is mapped to the zero-based primary display, so the contact is already
|
||||
// in the effective landscape resolution the rotation is based on
|
||||
static bool transform_sdvx_landscape_touch_position(POINT *position) {
|
||||
const auto landscape_width = static_cast<LONG>(GRAPHICS_FS_CUSTOM_RESOLUTION.has_value() ?
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first : GRAPHICS_FS_ORIGINAL_HEIGHT);
|
||||
const auto landscape_height = static_cast<LONG>(GRAPHICS_FS_CUSTOM_RESOLUTION.has_value() ?
|
||||
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second : GRAPHICS_FS_ORIGINAL_WIDTH);
|
||||
if (landscape_width <= 0 || landscape_height <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto input_x = position->x;
|
||||
position->x = landscape_width -
|
||||
MulDiv(position->y, landscape_width, landscape_height);
|
||||
position->y = MulDiv(input_x, landscape_height, landscape_width);
|
||||
return true;
|
||||
return sdvx_landscape_rotate(position, landscape_width, landscape_height);
|
||||
}
|
||||
|
||||
// convert physical screen coordinates to game touch coordinates for a known target
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace nativetouch::transform {
|
||||
};
|
||||
|
||||
bool is_tdj_dedicated_subscreen(HWND window);
|
||||
bool sdvx_landscape_rotate(POINT *position, LONG width, LONG height);
|
||||
bool game_to_screen(HWND window, POINT *position);
|
||||
bool screen_to_game(HWND window, POINT *position);
|
||||
bool mouse_to_game(HWND window, POINT *position);
|
||||
|
||||
Reference in New Issue
Block a user