mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
Handle generic joystick input
Unmapped handheld controllers emit joystick events, leaving the launcher cursor and in-game input inactive.
This commit is contained in:
@@ -331,6 +331,30 @@ function love.gamepadaxis(joystick, axis, value)
|
|||||||
Game:gamepadaxis(joystick, axis, value)
|
Game:gamepadaxis(joystick, axis, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function love.joystickpressed(joystick, button)
|
||||||
|
if editorMode then return end
|
||||||
|
if Importer then return Importer:joystickpressed(joystick, button) end
|
||||||
|
Game:joystickpressed(joystick, button)
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.joystickreleased(joystick, button)
|
||||||
|
if editorMode then return end
|
||||||
|
if Importer then return Importer:joystickreleased(joystick, button) end
|
||||||
|
Game:joystickreleased(joystick, button)
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.joystickaxis(joystick, axis, value)
|
||||||
|
if editorMode then return end
|
||||||
|
if Importer then return Importer:joystickaxis(joystick, axis, value) end
|
||||||
|
Game:joystickaxis(joystick, axis, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.joystickhat(joystick, hat, direction)
|
||||||
|
if editorMode then return end
|
||||||
|
if Importer then return Importer:joystickhat(joystick, hat, direction) end
|
||||||
|
Game:joystickhat(joystick, hat, direction)
|
||||||
|
end
|
||||||
|
|
||||||
function love.joystickremoved(joystick)
|
function love.joystickremoved(joystick)
|
||||||
if editorMode then return end
|
if editorMode then return end
|
||||||
if Importer then return end
|
if Importer then return end
|
||||||
|
|||||||
@@ -491,6 +491,25 @@ function Game:gamepadaxis(joystick, axis, value)
|
|||||||
Input:gamepadaxis(joystick, axis, value)
|
Input:gamepadaxis(joystick, axis, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Game:joystickpressed(joystick, button)
|
||||||
|
TouchControls:noteGamepad()
|
||||||
|
Input:joystickpressed(joystick, button)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:joystickreleased(joystick, button)
|
||||||
|
Input:joystickreleased(joystick, button)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:joystickaxis(joystick, axis, value)
|
||||||
|
if math.abs(value) > 0.5 then TouchControls:noteGamepad() end
|
||||||
|
Input:joystickaxis(joystick, axis, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:joystickhat(joystick, hat, direction)
|
||||||
|
if direction ~= "c" then TouchControls:noteGamepad() end
|
||||||
|
Input:joystickhat(joystick, hat, direction)
|
||||||
|
end
|
||||||
|
|
||||||
-- Window focus/visibility flips: a release due while unfocused/hidden can
|
-- Window focus/visibility flips: a release due while unfocused/hidden can
|
||||||
-- be swallowed by the OS. Reset on both edges -- gaining focus with a
|
-- be swallowed by the OS. Reset on both edges -- gaining focus with a
|
||||||
-- physically held key won't re-fire keypressed, so trusting leftover
|
-- physically held key won't re-fire keypressed, so trusting leftover
|
||||||
|
|||||||
@@ -39,6 +39,20 @@ local DEFAULT_GAMEPAD_BINDINGS = {
|
|||||||
local STICK_ON = 0.5
|
local STICK_ON = 0.5
|
||||||
local STICK_OFF = 0.3
|
local STICK_OFF = 0.3
|
||||||
|
|
||||||
|
-- Generic SDL joysticks expose the left stick as the first two numbered
|
||||||
|
-- axes and the D-pad as a hat. This is common on Linux handhelds whose
|
||||||
|
-- controller has no game-controller database entry.
|
||||||
|
local RAW_BUTTON_BINDINGS = {
|
||||||
|
[1] = "a", [2] = "b",
|
||||||
|
[7] = "select", [8] = "start", [9] = "select", [10] = "start",
|
||||||
|
}
|
||||||
|
|
||||||
|
local HAT_DIRECTIONS = {
|
||||||
|
u = { "up" }, d = { "down" }, l = { "left" }, r = { "right" },
|
||||||
|
lu = { "left", "up" }, ru = { "right", "up" },
|
||||||
|
ld = { "left", "down" }, rd = { "right", "down" },
|
||||||
|
}
|
||||||
|
|
||||||
function Input:init()
|
function Input:init()
|
||||||
self:applyBindings(nil)
|
self:applyBindings(nil)
|
||||||
self:reset()
|
self:reset()
|
||||||
@@ -79,6 +93,7 @@ function Input:reset()
|
|||||||
self.sources = {}
|
self.sources = {}
|
||||||
self.stickAxis = { x = 0, y = 0 }
|
self.stickAxis = { x = 0, y = 0 }
|
||||||
self.stickDir = nil
|
self.stickDir = nil
|
||||||
|
self.hatDirs = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Multiple physical sources (W + Up, d-pad + stick, etc.) can claim the
|
-- Multiple physical sources (W + Up, d-pad + stick, etc.) can claim the
|
||||||
@@ -179,6 +194,16 @@ function Input:gamepadreleased(joystick, button)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Input:joystickpressed(joystick, button)
|
||||||
|
local btn = RAW_BUTTON_BINDINGS[button]
|
||||||
|
if btn then press(self, btn, "joy:" .. button) end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:joystickreleased(joystick, button)
|
||||||
|
local btn = RAW_BUTTON_BINDINGS[button]
|
||||||
|
if btn then release(self, btn, "joy:" .. button) end
|
||||||
|
end
|
||||||
|
|
||||||
-- left stick treated as a continuous held direction, same 4-way rule as
|
-- left stick treated as a continuous held direction, same 4-way rule as
|
||||||
-- the touch swipe d-pad: whichever axis has the larger magnitude wins.
|
-- the touch swipe d-pad: whichever axis has the larger magnitude wins.
|
||||||
function Input:gamepadaxis(joystick, axis, value)
|
function Input:gamepadaxis(joystick, axis, value)
|
||||||
@@ -214,6 +239,26 @@ function Input:gamepadaxis(joystick, axis, value)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Input:joystickaxis(joystick, axis, value)
|
||||||
|
if axis == 1 then
|
||||||
|
self:gamepadaxis(joystick, "leftx", value)
|
||||||
|
elseif axis == 2 then
|
||||||
|
self:gamepadaxis(joystick, "lefty", value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Input:joystickhat(joystick, hat, direction)
|
||||||
|
local source = "hat:" .. hat
|
||||||
|
for _, btn in ipairs(self.hatDirs[hat] or {}) do
|
||||||
|
release(self, btn, source)
|
||||||
|
end
|
||||||
|
local dirs = HAT_DIRECTIONS[direction] or {}
|
||||||
|
for _, btn in ipairs(dirs) do
|
||||||
|
press(self, btn, source)
|
||||||
|
end
|
||||||
|
self.hatDirs[hat] = dirs
|
||||||
|
end
|
||||||
|
|
||||||
function Input:isDown(btn)
|
function Input:isDown(btn)
|
||||||
return self.state[btn] or false
|
return self.state[btn] or false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -540,6 +540,7 @@ function RomImporter.new(onComplete, opts)
|
|||||||
_padCursorActive = false,
|
_padCursorActive = false,
|
||||||
_padAxis = { leftx = 0, lefty = 0, righty = 0 },
|
_padAxis = { leftx = 0, lefty = 0, righty = 0 },
|
||||||
_padDir = {},
|
_padDir = {},
|
||||||
|
_rawHatDirs = {},
|
||||||
_padInited = false,
|
_padInited = false,
|
||||||
}, RomImporter)
|
}, RomImporter)
|
||||||
|
|
||||||
@@ -1184,6 +1185,36 @@ function RomImporter:gamepadaxis(_, axis, value)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function RomImporter:joystickpressed(joystick, button)
|
||||||
|
if button == 1 then self:gamepadpressed(joystick, "a") end
|
||||||
|
end
|
||||||
|
|
||||||
|
function RomImporter:joystickreleased(joystick, button)
|
||||||
|
if button == 1 then self:gamepadreleased(joystick, "a") end
|
||||||
|
end
|
||||||
|
|
||||||
|
function RomImporter:joystickaxis(joystick, axis, value)
|
||||||
|
if axis == 1 then
|
||||||
|
self:gamepadaxis(joystick, "leftx", value)
|
||||||
|
elseif axis == 2 then
|
||||||
|
self:gamepadaxis(joystick, "lefty", value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function RomImporter:joystickhat(_, hat, direction)
|
||||||
|
for _, dir in ipairs(self._rawHatDirs[hat] or {}) do
|
||||||
|
self._padDir[dir] = 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 _, dir in ipairs(dirs) do self._padDir[dir] = true end
|
||||||
|
self._rawHatDirs[hat] = dirs
|
||||||
|
if #dirs > 0 then self:_activatePadCursor() end
|
||||||
|
end
|
||||||
|
|
||||||
-- Player pressed Play on a game whose ROM is imported: hand off to boot.
|
-- Player pressed Play on a game whose ROM is imported: hand off to boot.
|
||||||
function RomImporter:play(version)
|
function RomImporter:play(version)
|
||||||
if self.workState == "working" then return end
|
if self.workState == "working" then return end
|
||||||
|
|||||||
+3
-1
@@ -50,7 +50,9 @@ local CALLBACK_NAMES = {
|
|||||||
"keypressed", "keyreleased", "textinput",
|
"keypressed", "keyreleased", "textinput",
|
||||||
"mousepressed", "mousereleased", "mousemoved", "wheelmoved",
|
"mousepressed", "mousereleased", "mousemoved", "wheelmoved",
|
||||||
"touchpressed", "touchmoved", "touchreleased",
|
"touchpressed", "touchmoved", "touchreleased",
|
||||||
"gamepadpressed", "gamepadreleased", "gamepadaxis", "joystickremoved",
|
"gamepadpressed", "gamepadreleased", "gamepadaxis",
|
||||||
|
"joystickpressed", "joystickreleased", "joystickaxis", "joystickhat",
|
||||||
|
"joystickremoved",
|
||||||
"focus", "visible", "resize", "filedropped", "directorydropped",
|
"focus", "visible", "resize", "filedropped", "directorydropped",
|
||||||
"errorhandler", "threaderror", "lowmemory",
|
"errorhandler", "threaderror", "lowmemory",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,51 @@ Input:step()
|
|||||||
check(Input:wasPressed("left"), "stick flick edges wasPressed")
|
check(Input:wasPressed("left"), "stick flick edges wasPressed")
|
||||||
check(not Input:isDown("left"), "stick flick does not stick isDown")
|
check(not Input:isDown("left"), "stick flick does not stick isDown")
|
||||||
|
|
||||||
|
-- Linux handhelds without an SDL game-controller mapping send raw joystick
|
||||||
|
-- axes and D-pad hats instead of gamepad events.
|
||||||
|
Input:reset()
|
||||||
|
Input:joystickaxis(nil, 1, -0.9)
|
||||||
|
Input:step()
|
||||||
|
check(Input:isDown("left"), "raw joystick left axis holds left")
|
||||||
|
Input:joystickaxis(nil, 1, 0)
|
||||||
|
check(not Input:isDown("left"), "raw joystick axis release clears left")
|
||||||
|
|
||||||
|
Input:reset()
|
||||||
|
Input:joystickhat(nil, 1, "u")
|
||||||
|
Input:step()
|
||||||
|
check(Input:isDown("up"), "raw joystick hat holds up")
|
||||||
|
Input:joystickhat(nil, 1, "c")
|
||||||
|
check(not Input:isDown("up"), "raw joystick hat release clears up")
|
||||||
|
|
||||||
|
Input:reset()
|
||||||
|
Input:joystickpressed(nil, 1)
|
||||||
|
Input:step()
|
||||||
|
check(Input:isDown("a"), "raw joystick primary button presses A")
|
||||||
|
|
||||||
|
-- The launcher has a separate virtual cursor, so prove generic joystick
|
||||||
|
-- events reach its left-stick and D-pad state too.
|
||||||
|
local RomImporter = require("src.import.RomImporter")
|
||||||
|
local importer = setmetatable({
|
||||||
|
_padCursor = { x = 0, y = 0 }, _padCursorActive = false,
|
||||||
|
_padAxis = { leftx = 0, lefty = 0, righty = 0 },
|
||||||
|
_padDir = {}, _rawHatDirs = {}, _padInited = true,
|
||||||
|
}, RomImporter)
|
||||||
|
local clicked = false
|
||||||
|
function importer:mousepressed(_, _, button)
|
||||||
|
clicked = button == 1
|
||||||
|
end
|
||||||
|
importer:joystickaxis(nil, 1, -0.8)
|
||||||
|
check(importer._padAxis.leftx == -0.8,
|
||||||
|
"raw joystick left axis reaches the launcher cursor")
|
||||||
|
importer:joystickhat(nil, 1, "r")
|
||||||
|
check(importer._padDir.dpright,
|
||||||
|
"raw joystick hat reaches the launcher cursor")
|
||||||
|
importer:joystickhat(nil, 1, "c")
|
||||||
|
check(not importer._padDir.dpright,
|
||||||
|
"raw joystick hat release clears the launcher cursor")
|
||||||
|
importer:joystickpressed(nil, 1)
|
||||||
|
check(clicked, "raw joystick primary button clicks the launcher cursor")
|
||||||
|
|
||||||
-- Drivers that only inject pressQueue still get a one-step hold.
|
-- Drivers that only inject pressQueue still get a one-step hold.
|
||||||
Input:reset()
|
Input:reset()
|
||||||
table.insert(Input.pressQueue, "down")
|
table.insert(Input.pressQueue, "down")
|
||||||
|
|||||||
Reference in New Issue
Block a user