From e295fd56c956cf33fa3696b9e85b299b21665f6b Mon Sep 17 00:00:00 2001
From: lxpollitt <630494+lxpollitt@users.noreply.github.com>
Date: Fri, 17 Apr 2026 20:12:28 +0100
Subject: [PATCH] =?UTF-8?q?Fix=20ADD=20button=20not=20opening=20file=20pic?=
=?UTF-8?q?ker=20in=20Safari=20Safari=20enforces=20the=20"user=20activatio?=
=?UTF-8?q?n"=20rule=20for=20opening=20native=20file=20pickers=20more=20st?=
=?UTF-8?q?rictly=20than=20Chrome:=20.click()=20on=20an=20=20must=20run=20in=20the=20same=20synchronous=20event=20?=
=?UTF-8?q?stack=20as=20the=20user-gesture=20event.=20HomeScreen.importPro?=
=?UTF-8?q?gram()=20was=20wrapping=20the=20call=20in=20Gdx.app.postRunnabl?=
=?UTF-8?q?e(...),=20which=20defers=20by=20one=20frame=20=E2=80=94=20long?=
=?UTF-8?q?=20enough=20for=20Safari=20to=20revoke=20the=20token=20and=20si?=
=?UTF-8?q?lently=20refuse=20the=20picker.=20Calling=20synchronously=20is?=
=?UTF-8?q?=20safe=20on=20all=20three=20platforms=20because=20each=20Dialo?=
=?UTF-8?q?gHandler.openFileDialog=20implementation=20handles=20its=20own?=
=?UTF-8?q?=20thread=20affinity=20internally=20(Desktop=20wraps=20for=20Sw?=
=?UTF-8?q?ing,=20Android=20uses=20runOnUiThread,=20GWT=20runs=20synchrono?=
=?UTF-8?q?usly).?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
core/src/main/java/emu/joric/HomeScreen.java | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/core/src/main/java/emu/joric/HomeScreen.java b/core/src/main/java/emu/joric/HomeScreen.java
index 189a659..44b3e2c 100644
--- a/core/src/main/java/emu/joric/HomeScreen.java
+++ b/core/src/main/java/emu/joric/HomeScreen.java
@@ -829,12 +829,20 @@ private void resetState() {
}
private void importProgram() {
- Gdx.app.postRunnable(new Runnable() {
- @Override
- public void run() {
- importProgramUsingOpenFileDialog();
- }
- });
+ // Invoked synchronously from the click handler so that Safari's
+ // "user activation" token is still valid when
+ // GwtDialogHandler.openFileDialog eventually calls .click() on the
+ // . Safari only allows the native file picker to
+ // open if .click() runs in the same synchronous event stack as the
+ // original user-gesture event; any intervening async step (e.g.
+ // Gdx.app.postRunnable, which defers to the next frame) causes
+ // Safari to revoke the token and silently refuse the picker.
+ //
+ // Each platform's DialogHandler openFileDialog implementation
+ // handles its own thread affinity internally (Desktop wraps in
+ // Gdx.app.postRunnable for Swing, Android uses runOnUiThread), so
+ // calling synchronously should be safe on all platforms.
+ importProgramUsingOpenFileDialog();
}
private void importProgramUsingOpenFileDialog() {