diff --git a/src/spice2x/api/modules/touch.cpp b/src/spice2x/api/modules/touch.cpp index 49be819..eeb7550 100644 --- a/src/spice2x/api/modules/touch.cpp +++ b/src/spice2x/api/modules/touch.cpp @@ -55,9 +55,10 @@ namespace api::modules { native_canvas_w = 0; native_canvas_h = 0; if (is_sdvx) { - // exceed gear subscreen, portrait after the rotation applied in apply_touch_errata - native_canvas_w = 1080; - native_canvas_h = 1920; + // landscape API coordinates already match the primary screen orientation; + // portrait coordinates are rotated by apply_touch_errata + native_canvas_w = GRAPHICS_FS_ORIENTATION_SWAP ? 1920 : 1080; + native_canvas_h = GRAPHICS_FS_ORIENTATION_SWAP ? 1080 : 1920; } else if (avs::game::is_model("LDJ")) { // TDJ subscreen; FHD models are upscaled to 1080p by apply_touch_errata native_canvas_w = is_tdj_fhd ? 1920 : 1280; @@ -218,8 +219,8 @@ namespace api::modules { // 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) { - // for exceed gear, they are both 1080p screens, but need to apply transformation + } else if (is_sdvx && !GRAPHICS_FS_ORIENTATION_SWAP) { + // rotate API coordinates into SDVX's portrait touch space x = 1080 - y_raw; y = x_raw; } diff --git a/src/spice2x/touch/native/transform.cpp b/src/spice2x/touch/native/transform.cpp index 267ca1d..8ba5528 100644 --- a/src/spice2x/touch/native/transform.cpp +++ b/src/spice2x/touch/native/transform.cpp @@ -1,5 +1,6 @@ #include "transform.h" +#include "avs/game.h" #include "hooks/graphics/graphics.h" #include "overlay/overlay.h" #include "settings.h" @@ -82,6 +83,25 @@ 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: + // (x, y) -> (width * (1 - y / height), height * x / width). + static bool transform_sdvx_landscape_touch_position(POINT *position) { + const auto landscape_width = static_cast(GRAPHICS_FS_CUSTOM_RESOLUTION.has_value() ? + GRAPHICS_FS_CUSTOM_RESOLUTION.value().first : GRAPHICS_FS_ORIGINAL_HEIGHT); + const auto landscape_height = static_cast(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; + } + // convert physical screen coordinates to game touch coordinates for a known target bool screen_to_game(HWND window, POINT *position) { if (settings::SYNTHETIC_TOUCH_USES_CLIENT_COORDINATES) { @@ -144,6 +164,13 @@ namespace nativetouch::transform { const auto dedicated_subscreen = is_tdj_dedicated_subscreen(TDJ_SUBSCREEN_WINDOW); const auto active_overlay = has_active_overlay_transform(); + // special case for SDVX landscape mode + if (!dedicated_subscreen && !active_overlay && + GRAPHICS_FS_ORIENTATION_SWAP && avs::game::is_model("KFC")) { + return transform_sdvx_landscape_touch_position(position) ? + Result::Transformed : Result::Rejected; + } + // no dedicated subscreen or active overlay mapping; pass the point through unchanged if (!dedicated_subscreen && !active_overlay) { return Result::Unchanged;