fix broken xdg-open on forced X11

This commit is contained in:
olcxja 2026-10-09 11:59:58 +02:00
commit 3a25325f20
2 changed files with 37 additions and 3 deletions

View file

@ -217,6 +217,7 @@ function writeIfChanged(file, text, mode) {
// what the seeded desktop entry starts. A scheme goes to the host's portal
// (chooser included), a path to the request file the launcher watches - the
// portal's answer is unreadable (development.md), the call is all we can trust.
// Mounted over the app's own xdg-open as well: see bundleOpenMounts.
function handOffScript({ hostHome, fsRoot, request }) {
const cases = [
`file://"$HOME") arg="file://${hostHome}" ;;`,
@ -234,8 +235,23 @@ function handOffScript({ hostHome, fsRoot, request }) {
return `#!/bin/sh
# Written by PLWL: what an app opens belongs to the host desktop.
arg="$1"
# the bundle's own xdg-open answers to gio-launch-desktop too (its bin links the
# two names to one file) and there the argument is not something to open but a
# program to start, so that name goes straight back to the command line
if [ "\${0##*/}" = "gio-launch-desktop" ]; then
export GIO_LAUNCHED_DESKTOP_FILE_PID=$$
exec "$@"
fi
[ -n "$arg" ] || exit 1
# a flag is not something to open: Java probes for xdg-open by running it with
# one, and handing that over would open garbage on the host desktop
case "$arg" in
-*) exit 2 ;;
esac
# a link is the host portal's from here on; anything else falls back
open_portal() {
command -v gdbus >/dev/null 2>&1 || return 1
@ -296,15 +312,29 @@ function writeMimeSeed(root, home) {
return writeIfChanged(path.join(root, MIME_REL), mime, 0o644)
}
// A bundle ships an xdg-open wrapper in its own bin/ (sharun's) and that bin is
// first in the app's PATH, so the wrapper - not /usr/bin - is what an app's
// opens really reach. It dies before delegating whenever the caller closes the
// child's streams, which is what Minecraft does right after Runtime.exec
// (measured: SIGPIPE, and no hand-off at all), so the launcher's shim is
// mounted over the bundle's own path as well. Names the bundle links to that
// file (its gio-launch-desktop) are answered by the shim itself - bwrap will
// not mount over a symlink, so they cannot be given a script of their own.
function bundleOpenMounts({ appDir, shim }) {
if (!appDir || !shim) return []
const wrapper = path.join(appDir, 'bin', SHIM_NAME)
return fs.existsSync(wrapper) ? [{ source: shim, target: wrapper }] : []
}
// Everything an opened thing needs: the hand-off script, the association naming
// it the default, and - in a sandbox - xdg-open replaced by the same script.
function urlRouting({ root, home, sandboxed, fsRoot = false }) {
function urlRouting({ root, home, sandboxed, fsRoot = false, appDir = '' }) {
const target = OPEN_BINS.find(bin => fs.existsSync(bin)) || ''
const opts = { hostHome: path.join(root, 'home'), fsRoot, request: requestPath(home) }
if (!writeUrlOpener(root, opts)) return null
writeMimeSeed(root, home)
const shim = sandboxed && target ? writeUrlShim(root, opts) : ''
return { target, shim }
return { target, shim, mounts: bundleOpenMounts({ appDir, shim }) }
}
function hashOf(file) {
@ -434,7 +464,7 @@ function buildLinuxEnv({ game, backend = 'env', fsRoot = false }) {
// the app's own environment comes last: it overrides everything above
for (const [k, v] of Object.entries(game.env || {})) env[k] = v
// an opened link or document leaves for the host desktop (see openrelay.js)
return { env, root, home, prefixName, prefixDir: prefixPath(prefixName), homeRedirects, routing: urlRouting({ root, home, sandboxed, fsRoot }) }
return { env, root, home, prefixName, prefixDir: prefixPath(prefixName), homeRedirects, routing: urlRouting({ root, home, sandboxed, fsRoot, appDir: writableAppDir(game) }) }
}
// Where a handed document goes inside the app's own home - a live bind, so a