Skip to content

Commit

Permalink
Re-implement Joystick touch mode
Browse files Browse the repository at this point in the history
- Also implement Auto, which switches between both depending on which was used last
  • Loading branch information
JesseTG committed Oct 2, 2023
1 parent c719b98 commit 10a00f0
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 19 deletions.
94 changes: 78 additions & 16 deletions src/libretro/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <NDS.h>
#include <glm/gtx/common.hpp>
#include <features/features_cpu.h>

#include "config.hpp"
#include "environment.hpp"
Expand Down Expand Up @@ -175,48 +176,76 @@ void melonds::InputState::Update(const ScreenLayoutData& screen_layout_data) noe
previousPointerTouchPosition = pointerTouchPosition;
previousIsPointerTouching = isPointerTouching;

previousJoystickTouchButton = joystickTouchButton;
previousJoystickCursorPosition = joystickCursorPosition;
retro_perf_tick_t now = cpu_features_get_perf_counter();

ScreenLayout layout = screen_layout_data.Layout();
if (layout == ScreenLayout::TopOnly) {
isPointerTouching = false;
joystickTouchButton = false;
} else {
isPointerTouching = retro::input_state(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED);
pointerRawPosition.x = retro::input_state(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
pointerRawPosition.y = retro::input_state(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y);
if (touchMode == TouchMode::Pointer || touchMode == TouchMode::Auto) {
isPointerTouching = retro::input_state(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED);
pointerRawPosition.x = retro::input_state(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
pointerRawPosition.y = retro::input_state(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y);
pointerTouchPosition = screen_layout_data.TransformPointerInput(pointerRawPosition);
hybridTouchPosition = screen_layout_data.TransformPointerInputToHybridScreen(pointerRawPosition);

if (isPointerTouching != previousIsPointerTouching || pointerTouchPosition != previousPointerTouchPosition) {
// If the player moved, pressed, or released the pointer within the past frame...
cursorTimeout = maxCursorTimeout * 60;
pointerUpdateTimestamp = now;
}
}

pointerTouchPosition = screen_layout_data.TransformPointerInput(pointerRawPosition);
hybridTouchPosition = screen_layout_data.TransformPointerInputToHybridScreen(pointerRawPosition);
}
if (touchMode == TouchMode::Joystick || touchMode == TouchMode::Auto) {
joystickTouchButton = retroInputBits & (1 << RETRO_DEVICE_ID_JOYPAD_R3);
joystickRawDirection.x = retro::input_state(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X);
joystickRawDirection.y = retro::input_state(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y);

if (joystickRawDirection != i16vec2(0)) {
if (pointerUpdateTimestamp > joystickTimestamp) {
joystickCursorPosition = pointerTouchPosition;
}
joystickCursorPosition += joystickRawDirection / i16vec2(2048);
joystickCursorPosition = clamp(joystickCursorPosition, ivec2(0), NDS_SCREEN_SIZE<int> - 1);
}

if (cursorMode == CursorMode::Timeout) {
if (isPointerTouching != previousIsPointerTouching || pointerTouchPosition != previousPointerTouchPosition) {
// If the player moved, pressed, or released the pointer within the past frame...
cursorTimeout = maxCursorTimeout * 60;
} else if (cursorTimeout > 0) {
cursorTimeout--;
if (joystickTouchButton != previousJoystickTouchButton || joystickCursorPosition != previousJoystickCursorPosition) {
// If the player moved, pressed, or released the joystick within the past frame...
cursorTimeout = maxCursorTimeout * 60;
joystickTimestamp = now;
}
}
}

if (cursorMode == CursorMode::Timeout && cursorTimeout > 0) {
cursorTimeout--;
}

cursorSettingsDirty = false;
}

glm::uvec2 melonds::InputState::ConsoleTouchCoordinates(const ScreenLayoutData& layout) const noexcept {
uvec2 clampedTouch;

switch (layout.Layout()) {
case ScreenLayout::HybridBottom:
if (layout.HybridSmallScreenLayout() == HybridSideScreenDisplay::One) {
// If the touch screen is only shown in the hybrid-screen position...
clampedTouch = clamp(hybridTouchPosition, ivec2(0), NDS_SCREEN_SIZE<int> - 1);
// ...then that's the only transformation we'll use for input.
break;
} else if (!all(glm::openBounded(pointerTouchPosition, ivec2(0), NDS_SCREEN_SIZE<int>))) {
} else if (!all(glm::openBounded(TouchPosition(), ivec2(0), NDS_SCREEN_SIZE<int>))) {
// The touch screen is shown in both the hybrid and secondary positions.
// If the touch input is not within the secondary position's bounds...
clampedTouch = clamp(hybridTouchPosition, ivec2(0), NDS_SCREEN_SIZE<int> - 1);
break;
}
[[fallthrough]];
default:
clampedTouch = clamp(pointerTouchPosition, ivec2(0), NDS_SCREEN_SIZE<int> - 1);
clampedTouch = clamp(TouchPosition(), ivec2(0), NDS_SCREEN_SIZE<int> - 1);
break;

}
Expand All @@ -226,7 +255,7 @@ glm::uvec2 melonds::InputState::ConsoleTouchCoordinates(const ScreenLayoutData&

bool melonds::InputState::CursorVisible() const noexcept {
bool modeAllowsCursor = false;
switch (config::screen::CursorMode()) {
switch (cursorMode) {
case CursorMode::Always:
modeAllowsCursor = true;
break;
Expand All @@ -243,6 +272,39 @@ bool melonds::InputState::CursorVisible() const noexcept {

return modeAllowsCursor && !NDS::IsLidClosed() && pointerRawPosition != i16vec2(0);
// libretro's pointer API returns (0, 0) if the pointer is not over the play area (even if it's still over the window).
// Theoretically means that the cursor is hidden if the player moves the pointer to the dead center of the screen,
// Theoretically means that the cursor will be hidden if the player moves the pointer to the dead center of the screen,
// but the screen's resolution probably isn't big enough for that to happen in practice.
}

bool melonds::InputState::IsTouchingScreen() const noexcept {
switch (touchMode) {
case TouchMode::Joystick:
return joystickTouchButton;
case TouchMode::Pointer:
return isPointerTouching;
case TouchMode::Auto:
return isPointerTouching || joystickTouchButton;
default:
return false;
}
}

ivec2 melonds::InputState::TouchPosition() const noexcept {
if (touchMode == TouchMode::Joystick) {
return joystickCursorPosition;
}

if (touchMode == TouchMode::Pointer) {
return pointerTouchPosition;
}

if (isPointerTouching) {
return pointerTouchPosition;
}

if (joystickTouchButton) {
return joystickCursorPosition;
}

return pointerUpdateTimestamp > joystickTimestamp ? pointerTouchPosition : joystickCursorPosition;
}
15 changes: 12 additions & 3 deletions src/libretro/input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ namespace melonds {
{
public:
[[nodiscard]] bool CursorVisible() const noexcept;
[[nodiscard]] bool IsTouchingScreen() const noexcept { return isPointerTouching; }
[[nodiscard]] bool IsTouchingScreen() const noexcept;
[[nodiscard]] bool ScreenTouched() const noexcept { return isPointerTouching && !previousIsPointerTouching; }
[[nodiscard]] bool ScreenReleased() const noexcept { return !isPointerTouching && previousIsPointerTouching; }
[[nodiscard]] bool ScreenReleased() const noexcept {
return (!isPointerTouching && previousIsPointerTouching) || (!joystickTouchButton && previousJoystickTouchButton);
}
[[nodiscard]] glm::ivec2 TouchPosition() const noexcept;
[[nodiscard]] glm::ivec2 PointerTouchPosition() const noexcept { return pointerTouchPosition; }
[[nodiscard]] glm::ivec2 JoystickTouchPosition() const noexcept { return joystickCursorPosition; }
[[nodiscard]] glm::i16vec2 PointerInput() const noexcept { return pointerRawPosition; }
[[nodiscard]] glm::ivec2 HybridTouchPosition() const noexcept { return hybridTouchPosition; }
[[nodiscard]] bool CycleLayoutDown() const noexcept { return cycleLayoutButton; }
Expand Down Expand Up @@ -70,14 +74,19 @@ namespace melonds {
glm::ivec2 previousPointerTouchPosition;
glm::ivec2 pointerTouchPosition;
glm::i16vec2 pointerRawPosition;
retro_perf_tick_t pointerUpdateTimestamp;

/// Touch coordinates of the pointer on the hybrid screen,
/// in NDS pixel coordinates.
/// Only relevant if a hybrid layout is active
glm::ivec2 hybridTouchPosition;
glm::ivec2 joystickCursorPosition;
glm::ivec2 previousJoystickCursorPosition;
glm::i16vec2 joystickRawDirection;
retro_perf_tick_t joystickTimestamp;
enum CursorMode cursorMode;
enum TouchMode touchMode;
enum TouchMode mostRecentTouchMode;

unsigned cursorTimeout = 0;
unsigned maxCursorTimeout;
Expand All @@ -88,8 +97,8 @@ namespace melonds {
bool cycleLayoutButton;
bool previousCycleLayoutButton;
bool joystickTouchButton;
bool previousJoystickTouchButton;
uint32_t consoleButtons;

};

void HandleInput(InputState& inputState, ScreenLayoutData& screenLayout) noexcept;
Expand Down

0 comments on commit 10a00f0

Please sign in to comment.