diff --git a/main.lua b/main.lua index f8dd9b74..fa4d1087 100644 --- a/main.lua +++ b/main.lua @@ -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 diff --git a/src/core/Game.lua b/src/core/Game.lua index c2ab339f..1caef072 100644 --- a/src/core/Game.lua +++ b/src/core/Game.lua @@ -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 diff --git a/tests/engine/input_joystick_recovery_test.lua b/tests/engine/input_joystick_recovery_test.lua new file mode 100644 index 00000000..3bdeaeb1 --- /dev/null +++ b/tests/engine/input_joystick_recovery_test.lua @@ -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()