This commit is contained in:
bryanthaboi
2026-08-05 11:15:49 -04:00
23 changed files with 1128 additions and 132 deletions
+10 -3
View File
@@ -793,11 +793,13 @@ function Game:joystickhat(joystick, hat, direction)
end
-- Window focus/visibility flips: a release due while unfocused/hidden can
-- be swallowed by the OS. Reset on both edges -- gaining focus with a
-- physically held key won't re-fire keypressed, so trusting leftover
-- state is worse than asking the player to re-press.
-- be swallowed by the OS. Reset on both edges; on the regain, reconcile
-- re-arms only what is still physically held -- a held key won't re-fire
-- keypressed by itself, and without the rebuild a spurious lifecycle event
-- parked the player until every direction was re-pressed (#799).
function Game:focus(f)
Input:reset()
if f then Input:reconcile() end
TouchControls:reset()
self:cancelPointers()
end
@@ -814,6 +816,7 @@ end
function Game:onResume()
Input:reset()
Input:reconcile()
TouchControls:reset()
self:cancelPointers()
-- Chip music may survive NX suspend as a duplicate stream; stop it and let
@@ -830,6 +833,10 @@ end
function Game:recoverInput(event, joystick)
Input:reset()
-- A hotplug can arrive with no hotplug (macOS Bluetooth re-enumeration),
-- and the blanket reset above also drops unrelated keyboard holds; put
-- back whatever is still physically down (#799).
Input:reconcile()
TouchControls:reset()
-- reset just dropped every source, mod holds included: retire the mods'
-- outstanding press tokens so nothing stale can be released later, and
+65
View File
@@ -301,6 +301,71 @@ function Input:joystickhat(joystick, hat, direction)
self.hatDirs[hat] = dirs
end
-- Lifecycle resets (focus/visibility flips, joystick add/remove, resume)
-- wipe held state because a release can be swallowed while the OS owns the
-- event stream. A direction the player is STILL holding never re-fires
-- keypressed/gamepadpressed after the wipe either, so a spurious reset --
-- macOS re-enumerating a Bluetooth pad fires joystickadded with no hotplug,
-- and the blanket reset took unrelated keyboard holds down with it --
-- parked the player in place until every direction was released and
-- pressed again (#799). Rebuild holds from the devices' ground truth
-- instead: only what is physically down right now comes back, so the
-- swallowed-release hazards the resets guard against stay cleared.
-- Deliberately separate from reset(): the soft-reset chord path in
-- Game:step needs the clean slate (re-arming A there would read it as a
-- title-menu choice).
function Input:reconcile()
local kb = love and love.keyboard
if kb and kb.isDown then
for key, btn in pairs(self.keyBindings) do
local ok, down = pcall(kb.isDown, key)
if ok and down then press(self, btn, "key:" .. key) end
end
end
local js = love and love.joystick
if not (js and js.getJoysticks) then return end
local ok, joysticks = pcall(js.getJoysticks)
if not ok or type(joysticks) ~= "table" then return end
for _, j in ipairs(joysticks) do
if GamepadMap.ignoreRawForJoystick(j) then
-- SDL-recognized pad: buttons + left stick, the gamepad surfaces
if j.isGamepadDown then
for button, btn in pairs(self.padBindings) do
local ok2, down = pcall(j.isGamepadDown, j, button)
if ok2 and down then press(self, btn, "pad:" .. button) end
end
end
if j.getGamepadAxis then
for _, axis in ipairs({ "leftx", "lefty" }) do
local ok2, v = pcall(j.getGamepadAxis, j, axis)
if ok2 and type(v) == "number" then self:gamepadaxis(j, axis, v) end
end
end
else
-- raw stick (#620/#632): the surfaces the joystick* events feed
if j.isDown then
for index, btn in pairs(self.joyBindings) do
local ok2, down = pcall(j.isDown, j, index)
if ok2 and down then press(self, btn, "joy:" .. index) end
end
end
if j.getAxis then
for _, axis in ipairs({ 1, 2 }) do
local ok2, v = pcall(j.getAxis, j, axis)
if ok2 and type(v) == "number" then self:joystickaxis(j, axis, v) end
end
end
if j.getHatCount and j.getHat then
local ok2, count = pcall(j.getHatCount, j)
for hat = 1, (ok2 and count) or 0 do
local ok3, dir = pcall(j.getHat, j, hat)
if ok3 and dir then self:joystickhat(j, hat, dir) end
end
end
end
end
end
function Input:isDown(btn)
return self.state[btn] or false
end