CLOSES #883, CLOSES #887, CLOSES #894

This commit is contained in:
bryanthaboi
2026-08-06 06:36:12 -04:00
parent e2820e02c5
commit cb6cfb5556
13 changed files with 476 additions and 39 deletions
+35 -1
View File
@@ -62,8 +62,35 @@ function HostShell.hideHostConsole()
return consoleHidden
end
-- #254 was fixed inside the launcher and nowhere else: a native dialog opened
-- while a mouse button is still down blocks the whole loop in io.popen, so SDL
-- never processes the button-up and never drops the pointer capture it took
-- for the press (on X11 an XGrabPointer with owner_events). The grab outlives
-- the click, every pointer event over the child dialog is still routed to our
-- window, and the dialog draws and keyboard-navigates but ignores the mouse.
-- src/import/RomImporter.lua owns the launcher's copy; hoisting it here means
-- every host spawn inherits it, including one a mod reaches through HostShell.
-- Pump until nothing is held so SDL sees the release first; bounded, so a
-- stuck button costs a moment and never the game. pump() drains OS events
-- into LOVE's queue and dispatches nothing, so there is no reentry. Worker
-- threads load neither love.mouse nor love.event, so the guard below makes
-- this a no-op off the main thread.
function HostShell.releasePointerGrab()
if not (love and love.mouse and love.mouse.isDown and love.event
and love.event.pump and love.timer) then
return
end
local deadline = love.timer.getTime() + 1
while love.mouse.isDown(1, 2, 3) do
love.event.pump()
if love.timer.getTime() > deadline then break end
love.timer.sleep(0.005)
end
end
-- Wraps io.popen with the AppImage env fix applied and lua errors swallowed
function HostShell.popen(command, mode)
HostShell.releasePointerGrab()
local ok, pipe = pcall(io.popen, HostShell.envPrefix() .. command, mode or "r")
if not ok or not pipe then return nil end
return pipe
@@ -154,8 +181,15 @@ local function haveBridge()
if not (love and love.system and type(love.system.httpDownload) == "function") then
return false
end
-- The OS allowlist is deliberate: the bridge is a per-port native addition,
-- not part of LOVE, so a build that exports the name on a platform we never
-- wired one for is a name collision, not a transport. UWP is listed because
-- Xbox has no curl and no way to spawn one (Platform.canSpawnProcess is
-- false there), so the bridge is its only possible transport (#876). Its
-- LOVE backend does not export it today and this still returns false, but
-- the gate is no longer the thing in the way.
local osName = love.system.getOS and love.system.getOS()
return osName == "Android" or osName == "iOS"
return osName == "Android" or osName == "iOS" or osName == "UWP"
end
-- Is any transport available at all? Callers gate on this, never on curl.
+11 -5
View File
@@ -1,10 +1,16 @@
-- Launch options: boot straight into a game, skipping the launcher.
--
-- love . --game red -- boot Red
-- love . --game yellow --slot 2 -- boot Yellow on save slot 2
-- love . --game red --launcher -- open the launcher anyway (a shortcut
-- the player wants to edit)
-- POKEPORT_GAME=blue love . -- same, for launchers that only pass env
-- love . --game=red -- boot Red
-- love . --game=yellow --slot=2 -- boot Yellow on save slot 2
-- love . --game=red --launcher -- open the launcher anyway (a shortcut
-- the player wants to edit)
-- POKEPORT_GAME=blue love . -- same, for launchers that only pass env
--
-- The "--flag value" spelling parses here (argValue reads argv[i + 1]), but it
-- does not survive LOVE: boot.lua takes the first bare argument as a path to a
-- game to run, so `--game red` dies with "Cannot load game at path .../red"
-- before love.load is ever called, fused or not. Only the "=" spelling is
-- reachable, so that is the one the docs quote.
--
-- This exists for the click-once cases: a desktop shortcut per game, a Steam
-- entry, an EmulationStation/Playnite entry, a handheld frontend. Those all
+17
View File
@@ -12,6 +12,8 @@ local function compute()
local mobile = osName == "Android" or osName == "iOS"
local nativePicker = love and love.system
and type(love.system.pickFile) == "function"
local nativeHttp = love and love.system
and type(love.system.httpDownload) == "function"
return {
os = osName,
nx = nx,
@@ -24,6 +26,17 @@ local function compute()
or (nativePicker and "native-picker")
or "desktop",
networkValidated = not nx and not uwp,
-- networkValidated is the self-updater's gate and stays a per-platform
-- policy call: a console package cannot replace itself on disk, so that
-- answer never depends on whether a transport exists. Fetching a mod
-- index or a mod zip is the narrower question, and #876 showed the two
-- had been conflated, so Xbox lost the mod catalog for the updater's
-- reason. Desktop answers it with curl through HostShell; the mobile and
-- console ports answer it with the native love.system.httpDownload bridge
-- (#597). The UWP LOVE backend does not export that bridge yet, so this
-- still resolves false on Xbox and the launcher still says so, but the
-- day the backend grows one, nothing here or in RomImporter has to change.
canFetchRemote = (not nx and not uwp) or nativeHttp,
}
end
@@ -52,6 +65,10 @@ function Platform.networkValidated()
return Platform.detect().networkValidated
end
function Platform.canFetchRemote()
return Platform.detect().canFetchRemote
end
-- Tests may swap love.system between cases.
function Platform._resetForTests()
cached = nil