Merge pull request #574 from andrewqsantos/feat/switch-nx

Nintendo Switch / love-nx support (#531)
This commit is contained in:
bryanthaboi
2026-08-03 17:08:54 -04:00
committed by GitHub
87 changed files with 8915 additions and 276 deletions
+67 -8
View File
@@ -9,6 +9,7 @@ local Renderer = require("src.render.Renderer")
local SaveData = require("src.core.SaveData")
local StateStack = require("src.core.StateStack")
local TouchControls = require("src.core.TouchControls")
local GamepadMap = require("src.core.GamepadMap")
local ModLoader = require("src.mods.Loader")
local ModRuntime = require("src.mods.Runtime")
local Screens = require("src.ui.Screens")
@@ -656,14 +657,26 @@ function Game:gamepadpressed(joystick, button)
-- a controller is being used: the touch overlay steps aside until the
-- next screen touch (mobile only; a no-op elsewhere)
TouchControls:noteGamepad()
-- Select held? Needed both to suppress shoulder speed hotkeys (Select+L
-- is a display chord on NX) and for the chord path below.
local selectHeld = Input:isDown("select")
if not selectHeld and joystick and joystick.isGamepadDown then
local ok, down = pcall(function()
return joystick:isGamepadDown("back")
end)
selectHeld = ok and down == true
end
-- shoulder buttons cycle GAME SPEED (R2/rightshoulder = faster,
-- L2/leftshoulder = slower; same as keyboard hotkey 1)
if button == "rightshoulder" then
self:_cycleSpeed(1)
return
elseif button == "leftshoulder" then
self:_cycleSpeed(-1)
return
-- L2/leftshoulder = slower; same as keyboard hotkey 1). Skip while
-- Select is held so Select+L can reach displayChordDigit ("7").
if not selectHeld then
if button == "rightshoulder" then
self:_cycleSpeed(1)
return
elseif button == "leftshoulder" then
self:_cycleSpeed(-1)
return
end
end
-- BindingsMenu's pad capture rides the same top-state routing as keys
local top = self.stack and self.stack:top()
@@ -671,6 +684,16 @@ function Game:gamepadpressed(joystick, button)
top:onGamepadPressed(button)
return
end
-- Select+face display chords → same digit path as Game:keypressed
-- (COLORS/TILT/pipelines). Intercept before Input so face does not
-- also fire GB A/B. Dual-path: raw already ignored when isGamepad().
if selectHeld then
local digit = GamepadMap.displayChordDigit(button)
if digit then
self:keypressed(digit)
return
end
end
Input:gamepadpressed(joystick, button)
end
@@ -754,15 +777,51 @@ 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 NX suspend as a duplicate stream; stop it and let
-- the active screen re-cue on the next frame (hardware audio check: T19).
-- Desktop/mobile window-visible flips must not kill overworld music.
if require("src.core.Platform").isNX() then
require("src.core.ChipAudio").stopMusic()
end
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