diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionFiles.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionFiles.kt
index 4274ad23f..5cf7348f0 100644
--- a/app/src/main/java/com/droiddeck/launcher/session/SessionFiles.kt
+++ b/app/src/main/java/com/droiddeck/launcher/session/SessionFiles.kt
@@ -62,8 +62,8 @@ object SessionFiles {
"usr/local/bin/droiddeck-steam-shortcuts" to "usr/local/bin/droiddeck-steam-shortcuts",
// Asks the app for an Epic game's sign-in code at launch, from the compat tool (StoreLaunchRequests).
"usr/local/bin/droiddeck-store-launch" to "usr/local/bin/droiddeck-store-launch",
- // A game's web links to Android's browser (winebrowser -> xdg-open), on game PATHs only.
- "usr/local/lib/droiddeck/browser/xdg-open" to "usr/local/lib/droiddeck/browser/xdg-open",
+ // A game's web links to Android's browser (Wine's winebrowser starts it).
+ "usr/local/bin/droiddeck-open-url" to "usr/local/bin/droiddeck-open-url",
"usr/local/bin/droiddeck-steam-games" to "usr/local/bin/droiddeck-steam-games",
"usr/local/bin/droiddeck-pad-defaults" to "usr/local/bin/droiddeck-pad-defaults",
// Flatpak: the bwrap stand-in, the store's helper and setup, and the front end's launcher.
diff --git a/app/src/main/java/com/droiddeck/launcher/ui/FrontEndSetup.kt b/app/src/main/java/com/droiddeck/launcher/ui/FrontEndSetup.kt
index f2c408c0b..ce5503bec 100644
--- a/app/src/main/java/com/droiddeck/launcher/ui/FrontEndSetup.kt
+++ b/app/src/main/java/com/droiddeck/launcher/ui/FrontEndSetup.kt
@@ -505,6 +505,9 @@ internal fun LaunchSettings(s: FrontEndState, a: FrontEndActions, host: MenuHost
}
}
+/** Epic's account page behind its sign-in: signing in there presents any step the account still owes. */
+private const val EPIC_RESOLVE_URL = "https://www.epicgames.com/id/login?redirectUrl=https%3A%2F%2Fwww.epicgames.com%2Faccount%2Fpersonal"
+
/** The Epic card: sign-in, offline and overlay switches, read from and written to the game's sidecar. */
@Composable
private fun EpicLaunchCard(host: MenuHost, folder: java.io.File) {
@@ -535,6 +538,14 @@ private fun EpicLaunchCard(host: MenuHost, folder: java.io.File) {
MenuItem(stringResource(R.string.epic_eos), checked = options.eos, focusRequester = first) { set { it.copy(eos = !it.eos) } }
MenuItem(stringResource(R.string.epic_offline), checked = options.offline) { set { it.copy(offline = !it.offline) } }
MenuItem(stringResource(R.string.epic_overlay), checked = options.overlay) { set { it.copy(overlay = !it.overlay) } }
+ // Epic asks some accounts to accept something once (privacy policy, EULA) before a game may
+ // sign in - EOS's "corrective action". Signing in on Epic's site shows it.
+ MenuItem(stringResource(R.string.epic_resolve), checked = false) {
+ host.open = null
+ runCatching {
+ appContext.startActivity(android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse(EPIC_RESOLVE_URL)).addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK))
+ }
+ }
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c2abb6201..08ae047f7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2020,6 +2020,7 @@
Epic sign-in (EOS)
Launch offline
Epic overlay
+ Resolve Epic sign-in
installed
in your library
store
diff --git a/docs/progress-log.md b/docs/progress-log.md
index 0392a353c..37f42d81f 100644
--- a/docs/progress-log.md
+++ b/docs/progress-log.md
@@ -144,6 +144,21 @@ without it and Setup says "Stores engine not built").
(`token`, `__token__`, `f_token`, `hdnts`, access/refresh/id tokens, `code=`) and Authorization /
Cookie headers are blanked outside URLs too. `StoreLogTest` covers a GOG secure link and an Epic
Akamai URL. The engine's own logcat lines (tag `EpicNative` etc.) are written on the native side.
+- **Why EOS's browser flow never reached Android:** the hand-off script was never in the APK. The
+ build copies only `usr/local/bin/droiddeck-*`, `steam-compatibility` and `usr/bin/**` of the overlay
+ into the assets, so `usr/local/lib/droiddeck/browser/xdg-open` was skipped, the session staged
+ nothing, the wrapper did not put it on the PATH, and Wine's winebrowser ran the runtime's own
+ `/usr/bin/xdg-open`, which has no browser to open in a Steam session. GE-Proton 11-7's winebrowser
+ (Metalstorm's tool) is Wine's own - registry `Browsers` list, then `xdg-open` by name - with no
+ steam://openurl route. The script is now `usr/local/bin/droiddeck-open-url`: named as Wine's
+ browser in the prefix (`"Browsers"="/usr/local/bin/droiddeck-open-url,xdg-open"`, an absolute path
+ that does not depend on Wine's PATH), linked as `xdg-open` first on the game's PATH, and reading
+ the session channel from `/tmp/droiddeck-browser/launch-dir` when Wine does not pass
+ `BL_LAUNCH_DIR` on. Whether EOS calls ShellExecute at all in its browser flow (rather than handing
+ a verification URI and code to the game to show) is what the next run's `droiddeck xdg-open:` line,
+ or `BL_DEBUG_BROWSER=1` in droiddeck-env (`WINEDEBUG=+shell,+winebrowser`), will show.
+- **Resolve Epic sign-in** (the Epic card): opens Epic's account page behind its sign-in in Android's
+ browser, where a pending corrective action (privacy policy, EULA) is presented.
- **Epic overlay off by default** (device A/B, Metalstorm: overlay pointer set → exits while
loading, twice; off → signs in to its Starform account and runs at 118 fps). `EpicOptions.overlay`
defaults to false; the sidecar's `epic` block carries `"v": 2`, and an older block (the old default
@@ -186,7 +201,7 @@ without it and Setup says "Stores engine not built").
(`STEAM_COMPAT_DATA_PATH/pfx/user.reg`, appended as DirectAudio's key is; a prefix Proton has not
created yet gets it on the next launch). Logged as `epic overlay installed= prefix=`.
A game's web links now open on Android: Wine's browser is set to xdg-open, and on a game's PATH
- `/usr/local/lib/droiddeck/browser/xdg-open` hands http(s) addresses to the app, which opens them
+ `droiddeck-open-url` hands http(s) addresses to the app, which opens them
with Android's browser - so a device-code sign-in can be finished on the phone.
- **Epic sign-in from Steam's own Play button (device, Metalstorm "Guest Account"):** the game was
started from the Steam client, so the app never minted its exchange code - the code was only
diff --git a/tools/linuxfs/overlay/usr/local/lib/droiddeck/browser/xdg-open b/tools/linuxfs/overlay/usr/local/bin/droiddeck-open-url
similarity index 59%
rename from tools/linuxfs/overlay/usr/local/lib/droiddeck/browser/xdg-open
rename to tools/linuxfs/overlay/usr/local/bin/droiddeck-open-url
index bc8c2c7fe..69d5c65aa 100755
--- a/tools/linuxfs/overlay/usr/local/lib/droiddeck/browser/xdg-open
+++ b/tools/linuxfs/overlay/usr/local/bin/droiddeck-open-url
@@ -1,11 +1,15 @@
#!/usr/bin/python3
-"""xdg-open for games: a web address a game opens (Wine's winebrowser runs xdg-open) goes to
-Android's browser through the app, so an EOS device sign-in or a store page can be finished on
-the phone. On a game's PATH only (droiddeck-proton puts this directory first); anything that is not
-http(s), or with no session channel, goes on to the system's own xdg-open."""
+"""A game's web links to Android: Wine's winebrowser starts this (named as the browser in the
+prefix's HKCU\\Software\\Wine\\WineBrowser, and as xdg-open first on a game's PATH), and an
+http(s) address goes to the app, which opens it in Android's browser - so an EOS sign-in step or a
+store page can be finished on the phone. Anything else goes on to the system's own xdg-open.
+
+The session channel is $BL_LAUNCH_DIR/stores; Wine may not pass that variable on to what it
+starts, so droiddeck-proton also leaves it in LAUNCH_DIR_FILE."""
import json
import os
+LAUNCH_DIR_FILE = "/tmp/droiddeck-browser/launch-dir"
import sys
import time
from pathlib import Path
@@ -36,14 +40,19 @@ def main(argv):
if url.startswith("steam://openurl/"):
url = url[len("steam://openurl/"):]
launch_dir = os.environ.get("BL_LAUNCH_DIR", "")
+ if not launch_dir:
+ try:
+ launch_dir = open(LAUNCH_DIR_FILE).read().strip()
+ except OSError:
+ launch_dir = ""
print("droiddeck xdg-open: %s" % where(url), file=sys.stderr, flush=True)
if url.startswith(("https://", "http://")) and launch_dir:
hand_over(url, launch_dir)
return 0
- here = os.path.dirname(os.path.abspath(__file__))
+ here = os.path.dirname(os.path.realpath(sys.argv[0]))
for d in os.environ.get("PATH", "").split(os.pathsep):
candidate = os.path.join(d, "xdg-open")
- if os.path.abspath(d) != here and os.access(candidate, os.X_OK):
+ if os.access(candidate, os.X_OK) and os.path.realpath(candidate) != os.path.realpath(__file__) and os.path.abspath(d) != here:
os.execv(candidate, [candidate] + argv)
return 1
diff --git a/tools/linuxfs/overlay/usr/local/bin/droiddeck-store-launch b/tools/linuxfs/overlay/usr/local/bin/droiddeck-store-launch
index fdc6672e1..2c5c0d473 100755
--- a/tools/linuxfs/overlay/usr/local/bin/droiddeck-store-launch
+++ b/tools/linuxfs/overlay/usr/local/bin/droiddeck-store-launch
@@ -21,8 +21,8 @@ an earlier launch is never used.
The game's prefix also gets the EOS overlay's pointer, HKCU\Software\Epic Games\EOS "OverlayPath"
(the mechanism from Legendary's lfs/eos.py), naming the one copy the app keeps at OVERLAY_DIR; with
it the game's EOS SDK can show Epic's own sign-in steps in game (a consent Epic asks for once per
-product) instead of waiting on a browser. Wine's browser is pointed at xdg-open, which this
-runtime's /usr/local/lib/droiddeck/browser/xdg-open hands to Android.
+product) instead of waiting on a browser. Wine's browser is set to droiddeck-open-url, which hands
+web addresses to Android.
droiddeck-store-launch
"""
@@ -40,6 +40,7 @@ TIMEOUT = 5.0
STALE_SECONDS = 300
OVERLAY_DIR = "/root/.local/share/droiddeck/epic-overlay"
SERVER_WAIT = 8.0
+BROWSER = "/usr/local/bin/droiddeck-open-url"
OVERLAY_DLL = "EOSOVH-Win64-Shipping.dll"
EOS_KEY = "[Software\\\\Epic Games\\\\EOS]"
BROWSER_KEY = "[Software\\\\Wine\\\\WineBrowser]"
@@ -228,7 +229,9 @@ def provision_prefix(compat_data, overlay_dir=OVERLAY_DIR, overlay=False, wait=S
return "prefix-not-created"
# A wineserver still up for this prefix would write its own copy of user.reg over these lines.
busy = not wait_for_server(pfx, wait)
- set_value(reg, BROWSER_KEY, "Browsers", "xdg-open")
+ # winebrowser starts the first entry it can; an absolute path does not depend on the PATH Wine
+ # hands it.
+ set_value(reg, BROWSER_KEY, "Browsers", BROWSER + ",xdg-open")
if not overlay:
remove_value(reg, EOS_KEY, "OverlayPath")
state = "overlay-off" if not has_value(reg, EOS_KEY, "OverlayPath") else "overlay-still-set"
diff --git a/tools/linuxfs/overlay/usr/local/bin/steam-compatibility b/tools/linuxfs/overlay/usr/local/bin/steam-compatibility
index a2a9b542f..9698b0f20 100755
--- a/tools/linuxfs/overlay/usr/local/bin/steam-compatibility
+++ b/tools/linuxfs/overlay/usr/local/bin/steam-compatibility
@@ -103,9 +103,18 @@ TOOL_MANIFEST_VDF = """"manifest"
BL_STORE_SETUP = """
bl_store_launch() {
[ "${verb:-}" = waitforexitandrun ] || return 0
- # A web address the game opens (Wine's winebrowser runs xdg-open) goes to Android's browser.
- if [ -x /usr/local/lib/droiddeck/browser/xdg-open ]; then
- PATH="/usr/local/lib/droiddeck/browser:$PATH"; export PATH
+ # A web address the game opens (Wine's winebrowser runs xdg-open) goes to Android's browser:
+ # droiddeck-open-url as xdg-open, first on the game's PATH, with the session channel left in a
+ # file in case Wine does not pass BL_LAUNCH_DIR on. (The prefix also names it as Wine's browser.)
+ if [ -x /usr/local/bin/droiddeck-open-url ]; then
+ mkdir -p /tmp/droiddeck-browser
+ ln -sfn /usr/local/bin/droiddeck-open-url /tmp/droiddeck-browser/xdg-open
+ [ -n "${BL_LAUNCH_DIR:-}" ] && printf '%s\n' "$BL_LAUNCH_DIR" > /tmp/droiddeck-browser/launch-dir
+ PATH="/tmp/droiddeck-browser:$PATH"; export PATH
+ fi
+ # BL_DEBUG_BROWSER=1 (droiddeck-env): Wine traces how the game opens a web address.
+ if [ "${BL_DEBUG_BROWSER:-}" = 1 ]; then
+ WINEDEBUG="${WINEDEBUG:+$WINEDEBUG,}+shell,+winebrowser"; export WINEDEBUG
fi
[ -x /usr/local/bin/droiddeck-store-launch ] || return 0
/usr/local/bin/droiddeck-store-launch "$@" || true
diff --git a/tools/tests/test_store_launch.py b/tools/tests/test_store_launch.py
index e55880f99..e83bfbf9f 100644
--- a/tools/tests/test_store_launch.py
+++ b/tools/tests/test_store_launch.py
@@ -132,7 +132,7 @@ class Prefix(unittest.TestCase):
text = self.reg.read_text()
self.assertIn('[Software\\\\Epic Games\\\\EOS] ', text)
self.assertIn('"OverlayPath"="Z:' + str(self.overlay).replace('/', '\\\\') + '"', text)
- self.assertIn('"Browsers"="xdg-open"', text)
+ self.assertIn('"Browsers"="/usr/local/bin/droiddeck-open-url,xdg-open"', text)
# A second launch adds nothing: the last word already says so.
store.provision_prefix(str(self.compat), str(self.overlay), overlay=True)
self.assertEqual(text, self.reg.read_text())
@@ -178,7 +178,7 @@ class Choices(unittest.TestCase):
class Browser(unittest.TestCase):
def test_a_web_address_is_handed_to_the_app(self):
- xdg = load_path(BIN.parent / 'lib/droiddeck/browser/xdg-open', 'xdg_open')
+ xdg = load_path(BIN / 'droiddeck-open-url', 'open_url')
with tempfile.TemporaryDirectory() as root:
os.environ['BL_LAUNCH_DIR'] = root
try: