fix(input): recover cleanly on joystick reconnect

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Andrew Quenehen
2026-08-01 03:53:43 -03:00
parent da60f40dfc
commit 7504753ea8
3 changed files with 76 additions and 1 deletions
+12
View File
@@ -393,6 +393,13 @@ function love.joystickhat(joystick, hat, direction)
Game:joystickhat(joystick, hat, direction)
end
function love.joystickadded(joystick)
SwitchDiagnostics.onJoystickEvent("joystickadded", joystick)
if editorMode or TouchEditor then return end
if Importer then return end
Game:joystickadded(joystick)
end
function love.joystickremoved(joystick)
SwitchDiagnostics.onJoystickEvent("joystickremoved", joystick)
if editorMode or TouchEditor then return end
@@ -423,6 +430,11 @@ function love.visible(v)
Game:visible(v)
end
function love.lowmemory()
if editorMode or TouchEditor or Importer then return end
if Game then Game:onResume() end
end
function love.touchpressed(id, x, y, dx, dy, pressure)
if editorMode then return end
if TouchEditor then
+34 -1
View File
@@ -546,15 +546,48 @@ function Game:focus(f)
end
function Game:visible(v)
if v then
self:onResume()
else
Input:reset()
TouchControls:reset()
end
end
function Game:onResume()
Input:reset()
TouchControls:reset()
-- Chip music may survive suspend as a duplicate stream; stop it and let
-- the active screen re-cue on the next frame (hardware audio check: T19).
require("src.core.ChipAudio").stopMusic()
local SwitchDiagnostics = require("src.debug.SwitchDiagnostics")
if SwitchDiagnostics.isEnabled() then
SwitchDiagnostics.onEvent("lifecycle", { event = "resume" })
end
end
function Game:recoverInput(event, joystick)
Input:reset()
TouchControls:reset()
local SwitchDiagnostics = require("src.debug.SwitchDiagnostics")
if SwitchDiagnostics.isEnabled() then
if joystick then
SwitchDiagnostics.onJoystickEvent(event, joystick)
else
SwitchDiagnostics.onEvent("lifecycle", { event = event })
end
end
end
function Game:joystickadded(joystick)
self:recoverInput("joystickadded", joystick)
end
-- A disconnected/dropped controller can't send the button-up for whatever
-- it was holding, so drop all input state rather than try to guess which
-- flags it owned.
function Game:joystickremoved(joystick)
Input:reset()
self:recoverInput("joystickremoved", joystick)
TouchControls:joystickremoved()
end
@@ -0,0 +1,30 @@
-- Joystick remove / resume clears stuck input sources (SWNX-19).
package.path = "./?.lua;./?/init.lua;" .. package.path
if not _G.love then _G.love = require("tests.love_stub") end
local T = require("tests.harness")
local check = T.check
local Input = require("src.core.Input")
local Game = require("src.core.Game")
Input:init()
Input:gamepadaxis(nil, "leftx", -0.9)
Input:step()
check(Input:isDown("left"), "stick holds left before disconnect")
Game:joystickremoved({})
check(not Input:isDown("left"), "joystickremoved clears stick hold")
Input:gamepadpressed(nil, "a")
Input:step()
check(Input:isDown("a"), "button held before reconnect reset")
Game:joystickadded({ getName = function() return "Joy-Con" end })
check(not Input:isDown("a"), "joystickadded clears stale button hold")
Input:gamepadaxis(nil, "lefty", 0.9)
Input:step()
Game:onResume()
check(not Input:isDown("down"), "resume clears stuck axis state")
T.finish()