mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 00:10:56 +02:00
fix(switch): restore pad cursor in Touch Controls and launcher overlays
Opening Touch Controls dropped the launcher virtual cursor and swallowed gamepad input while touch still worked. Share PadCursor with the save editor, forward pad events, and centralize overlay handoff/resume so both hosts park and re-arm the pointer cleanly. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,215 +1,3 @@
|
||||
-- Virtual pointer for the save editor on Switch / handhelds / any gamepad.
|
||||
-- Mirrors the launcher's RomImporter pad cursor (speeds, deadzone, dual-path
|
||||
-- raw gate) without sharing that module -- keeps RomImporter risk-free.
|
||||
--
|
||||
-- Stick / D-pad move; real mouse motion yields so desktop stays normal.
|
||||
-- Callers (App.lua) map A → click, B → close, shoulders → tabs, right stick
|
||||
-- → wheel notches.
|
||||
|
||||
local SafeArea = require("src.core.SafeArea")
|
||||
local GamepadMap = require("src.core.GamepadMap")
|
||||
|
||||
local PAD_DEAD = 0.28
|
||||
local PAD_SPEED = 560
|
||||
local PAD_DPAD_SPEED = 420
|
||||
-- Right stick → Kit wheel notches: ~2 notches/sec at full deflection so lists
|
||||
-- scroll at a usable pace without flooding one frame.
|
||||
local PAD_WHEEL_RATE = 2.0
|
||||
|
||||
local PadInput = {}
|
||||
|
||||
local cursor = { x = 0, y = 0 }
|
||||
local active = false
|
||||
local inited = false
|
||||
local axis = { leftx = 0, lefty = 0, righty = 0 }
|
||||
local dir = {}
|
||||
local rawHatDirs = {}
|
||||
local lastMouseX, lastMouseY
|
||||
local wheelAcc = 0
|
||||
|
||||
local function activate()
|
||||
if active then return end
|
||||
local ox, oy, w, h = SafeArea.rect()
|
||||
if not inited then
|
||||
cursor.x = ox + w * 0.5
|
||||
cursor.y = oy + h * 0.45
|
||||
inited = true
|
||||
end
|
||||
active = true
|
||||
end
|
||||
|
||||
function PadInput.reset()
|
||||
cursor.x, cursor.y = 0, 0
|
||||
active = false
|
||||
inited = false
|
||||
axis.leftx, axis.lefty, axis.righty = 0, 0, 0
|
||||
for k in pairs(dir) do dir[k] = nil end
|
||||
for k in pairs(rawHatDirs) do rawHatDirs[k] = nil end
|
||||
lastMouseX, lastMouseY = nil, nil
|
||||
wheelAcc = 0
|
||||
end
|
||||
|
||||
-- Touch / mouse press: drop the virtual cursor for this interaction so a tap
|
||||
-- is not swallowed by the Joy-Con pointer sitting elsewhere on screen.
|
||||
function PadInput.yieldToPointer()
|
||||
active = false
|
||||
end
|
||||
|
||||
-- Returns mx, my, isActive. When inactive the caller should use the system
|
||||
-- mouse; when active these coords feed Kit.beginFrame.
|
||||
function PadInput.pointer()
|
||||
return cursor.x, cursor.y, active
|
||||
end
|
||||
|
||||
function PadInput.isActive()
|
||||
return active
|
||||
end
|
||||
|
||||
-- Consume accumulated right-stick scroll as integer wheel notches (same
|
||||
-- units App.wheelmoved feeds Kit). Fractional remainder stays for next frame.
|
||||
function PadInput.takeWheel()
|
||||
local notches = 0
|
||||
if wheelAcc >= 1 or wheelAcc <= -1 then
|
||||
notches = wheelAcc > 0 and math.floor(wheelAcc) or math.ceil(wheelAcc)
|
||||
wheelAcc = wheelAcc - notches
|
||||
end
|
||||
return notches
|
||||
end
|
||||
|
||||
function PadInput.update(dt)
|
||||
if not (love and love.mouse and love.mouse.getPosition) then return end
|
||||
local mx, my = love.mouse.getPosition()
|
||||
if lastMouseX and active then
|
||||
if math.abs(mx - lastMouseX) > 3 or math.abs(my - lastMouseY) > 3 then
|
||||
active = false
|
||||
end
|
||||
end
|
||||
lastMouseX, lastMouseY = mx, my
|
||||
|
||||
local ax = axis.leftx or 0
|
||||
local ay = axis.lefty or 0
|
||||
local dx, dy = 0, 0
|
||||
if math.abs(ax) > PAD_DEAD then dx = dx + ax end
|
||||
if math.abs(ay) > PAD_DEAD then dy = dy + ay end
|
||||
if dir.dpleft then dx = dx - 1 end
|
||||
if dir.dpright then dx = dx + 1 end
|
||||
if dir.dpup then dy = dy - 1 end
|
||||
if dir.dpdown then dy = dy + 1 end
|
||||
|
||||
if dx ~= 0 or dy ~= 0 then
|
||||
activate()
|
||||
local mag = math.sqrt(dx * dx + dy * dy)
|
||||
if mag > 1 then dx, dy = dx / mag, dy / mag end
|
||||
local speed = (math.abs(ax) > PAD_DEAD or math.abs(ay) > PAD_DEAD)
|
||||
and PAD_SPEED or PAD_DPAD_SPEED
|
||||
local ox, oy, w, h = SafeArea.rect()
|
||||
local nx = cursor.x + dx * speed * dt
|
||||
local ny = cursor.y + dy * speed * dt
|
||||
cursor.x = math.max(ox, math.min(ox + w, nx))
|
||||
cursor.y = math.max(oy, math.min(oy + h, ny))
|
||||
end
|
||||
|
||||
local ry = axis.righty or 0
|
||||
if math.abs(ry) > PAD_DEAD then
|
||||
activate()
|
||||
-- Negative righty (stick up) scrolls lists up = positive wheel notches.
|
||||
wheelAcc = wheelAcc + (-ry) * PAD_WHEEL_RATE * dt
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns a string action the App layer handles:
|
||||
-- "a" | "b" | "tab_prev" | "tab_next" | nil
|
||||
function PadInput.gamepadpressed(_, button)
|
||||
activate()
|
||||
local action = GamepadMap.mapGamepadButton(button)
|
||||
if action == "a" or action == "b" then
|
||||
return action
|
||||
elseif button == "leftshoulder" then
|
||||
return "tab_prev"
|
||||
elseif button == "rightshoulder" then
|
||||
return "tab_next"
|
||||
elseif button == "dpup" or button == "dpdown"
|
||||
or button == "dpleft" or button == "dpright" then
|
||||
dir[button] = true
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function PadInput.gamepadreleased(_, button)
|
||||
if button == "dpup" or button == "dpdown"
|
||||
or button == "dpleft" or button == "dpright" then
|
||||
dir[button] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PadInput.gamepadaxis(_, axisName, value)
|
||||
if axisName == "leftx" or axisName == "lefty" or axisName == "righty" then
|
||||
axis[axisName] = value
|
||||
if math.abs(value) > PAD_DEAD then activate() end
|
||||
end
|
||||
end
|
||||
|
||||
function PadInput.joystickpressed(joystick, button)
|
||||
if GamepadMap.ignoreRawForJoystick(joystick) then return nil end
|
||||
local padButton = GamepadMap.mapRawToGamepadButton(button)
|
||||
if padButton then return PadInput.gamepadpressed(joystick, padButton) end
|
||||
return nil
|
||||
end
|
||||
|
||||
function PadInput.joystickreleased(joystick, button)
|
||||
if GamepadMap.ignoreRawForJoystick(joystick) then return end
|
||||
local padButton = GamepadMap.mapRawToGamepadButton(button)
|
||||
if padButton then PadInput.gamepadreleased(joystick, padButton) end
|
||||
end
|
||||
|
||||
function PadInput.joystickaxis(joystick, axisIndex, value)
|
||||
if GamepadMap.ignoreRawForJoystick(joystick) then return end
|
||||
if axisIndex == 1 then
|
||||
PadInput.gamepadaxis(joystick, "leftx", value)
|
||||
elseif axisIndex == 2 then
|
||||
PadInput.gamepadaxis(joystick, "lefty", value)
|
||||
end
|
||||
end
|
||||
|
||||
function PadInput.joystickhat(joystick, hat, direction)
|
||||
if GamepadMap.ignoreRawForJoystick(joystick) then return end
|
||||
for _, d in ipairs(rawHatDirs[hat] or {}) do
|
||||
dir[d] = nil
|
||||
end
|
||||
local dirs = ({
|
||||
u = { "dpup" }, d = { "dpdown" }, l = { "dpleft" }, r = { "dpright" },
|
||||
lu = { "dpleft", "dpup" }, ru = { "dpright", "dpup" },
|
||||
ld = { "dpleft", "dpdown" }, rd = { "dpright", "dpdown" },
|
||||
})[direction] or {}
|
||||
for _, d in ipairs(dirs) do dir[d] = true end
|
||||
rawHatDirs[hat] = dirs
|
||||
if #dirs > 0 then activate() end
|
||||
end
|
||||
|
||||
function PadInput.draw()
|
||||
if not active then return end
|
||||
if not (love and love.graphics) then return end
|
||||
local x, y = cursor.x, cursor.y
|
||||
love.graphics.push("all")
|
||||
if love.graphics.origin then love.graphics.origin() end
|
||||
if love.graphics.setLineWidth then love.graphics.setLineWidth(1) end
|
||||
love.graphics.setColor(0, 0, 0, 0.45)
|
||||
if love.graphics.polygon then
|
||||
love.graphics.polygon("fill",
|
||||
x + 2, y + 2, x + 2, y + 22, x + 8, y + 16, x + 14, y + 26,
|
||||
x + 18, y + 24, x + 11, y + 14, x + 20, y + 14)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.polygon("fill",
|
||||
x, y, x, y + 20, x + 6, y + 14, x + 12, y + 24,
|
||||
x + 16, y + 22, x + 9, y + 12, x + 18, y + 12)
|
||||
love.graphics.setColor(0.05, 0.07, 0.12, 1)
|
||||
love.graphics.polygon("line",
|
||||
x, y, x, y + 20, x + 6, y + 14, x + 12, y + 24,
|
||||
x + 16, y + 22, x + 9, y + 12, x + 18, y + 12)
|
||||
else
|
||||
love.graphics.rectangle("fill", x, y, 12, 18)
|
||||
end
|
||||
love.graphics.pop()
|
||||
end
|
||||
|
||||
return PadInput
|
||||
-- Compat shim: PadInput lived here first; shared implementation is now
|
||||
-- src/ui/PadCursor.lua so the touch-controls editor can reuse it.
|
||||
return require("src.ui.PadCursor")
|
||||
|
||||
Reference in New Issue
Block a user