Skip to content

Commit 0319ac9

Browse files
Dinnerbonetorokati44
authored andcommitted
Add a (super ugly) panic screen
1 parent fd9c1b6 commit 0319ac9

File tree

8 files changed

+277
-123
lines changed

8 files changed

+277
-123
lines changed

Cargo.lock

Lines changed: 1 addition & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ ruffle_video_software = { git = "https://github.com/ruffle-rs/ruffle.git", branc
4040
ruffle_frontend_utils = { git = "https://github.com/ruffle-rs/ruffle.git", branch = "master" }
4141

4242
log = "0.4.22"
43-
log-panics = { version = "2.1.0", features = ["with-backtrace"]}
4443

4544
# Redirect tracing to log
4645
tracing = {version = "0.1.40", features = ["log", "log-always"]}
46+
backtrace = "0.3.74"
4747

4848
url = "2.5.2"
4949
webbrowser = "1.0.1"

app/src/main/AndroidManifest.xml

Lines changed: 72 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,73 @@
1-
<?xml version="1.0" encoding="utf-8"?>
2-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3-
xmlns:tools="http://schemas.android.com/tools">
4-
5-
<uses-permission android:name="android.permission.INTERNET" />
6-
7-
<application
8-
android:allowBackup="true"
9-
android:dataExtractionRules="@xml/data_extraction_rules"
10-
android:fullBackupContent="@xml/backup_rules"
11-
android:icon="@mipmap/ic_launcher"
12-
android:label="@string/app_name"
13-
android:roundIcon="@mipmap/ic_launcher_round"
14-
android:supportsRtl="true"
15-
android:theme="@style/Theme.Ruffle"
16-
tools:targetApi="31">
17-
<activity
18-
android:name=".MainActivity"
19-
android:launchMode="singleTask"
20-
android:exported="true">
21-
22-
<intent-filter>
23-
<action android:name="android.intent.action.MAIN" />
24-
<category android:name="android.intent.category.LAUNCHER" />
25-
</intent-filter>
26-
</activity>
27-
<activity
28-
android:name=".PlayerActivity"
29-
android:exported="true"
30-
android:configChanges="orientation|keyboardHidden|screenSize"
31-
32-
android:screenOrientation="user"
33-
>
34-
<meta-data android:name="android.app.lib_name" android:value="ruffle_android" />
35-
36-
<meta-data
37-
android:name="android.support.PARENT_ACTIVITY"
38-
android:value=".MainActivity" />
39-
40-
<intent-filter>
41-
<action android:name="android.intent.action.VIEW"/>
42-
43-
<category android:name="android.intent.category.DEFAULT"/>
44-
<category android:name="android.intent.category.BROWSABLE"/>
45-
<category android:name="android.intent.category.OPENABLE" />
46-
47-
<data android:scheme="file"/>
48-
<data android:scheme="content"/>
49-
50-
<data android:mimeType="application/x-shockwave-flash"/>
51-
52-
<data android:pathSuffix="swf" />
53-
<data android:pathPattern="*.swf" />
54-
</intent-filter>
55-
56-
<intent-filter>
57-
<action android:name="android.intent.action.VIEW" />
58-
<category android:name="android.intent.category.BROWSABLE" />
59-
<category android:name="android.intent.category.DEFAULT" />
60-
<data android:scheme="http" />
61-
<data android:scheme="https" />
62-
<data android:host="*" />
63-
<data android:pathPattern=".*\\.swf" />
64-
</intent-filter>
65-
66-
</activity>
67-
</application>
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools">
4+
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
7+
<application
8+
android:allowBackup="true"
9+
android:dataExtractionRules="@xml/data_extraction_rules"
10+
android:fullBackupContent="@xml/backup_rules"
11+
android:icon="@mipmap/ic_launcher"
12+
android:label="@string/app_name"
13+
android:roundIcon="@mipmap/ic_launcher_round"
14+
android:supportsRtl="true"
15+
android:theme="@style/Theme.Ruffle"
16+
tools:targetApi="31">
17+
<activity
18+
android:name=".MainActivity"
19+
android:launchMode="singleTask"
20+
android:exported="true">
21+
22+
<intent-filter>
23+
<action android:name="android.intent.action.MAIN" />
24+
<category android:name="android.intent.category.LAUNCHER" />
25+
</intent-filter>
26+
</activity>
27+
<activity
28+
android:name=".PanicActivity"
29+
android:launchMode="singleTask"
30+
android:exported="false">
31+
</activity>
32+
<activity
33+
android:name=".PlayerActivity"
34+
android:exported="true"
35+
android:configChanges="orientation|keyboardHidden|screenSize"
36+
37+
android:screenOrientation="user"
38+
>
39+
<meta-data android:name="android.app.lib_name" android:value="ruffle_android" />
40+
41+
<meta-data
42+
android:name="android.support.PARENT_ACTIVITY"
43+
android:value=".MainActivity" />
44+
45+
<intent-filter>
46+
<action android:name="android.intent.action.VIEW"/>
47+
48+
<category android:name="android.intent.category.DEFAULT"/>
49+
<category android:name="android.intent.category.BROWSABLE"/>
50+
<category android:name="android.intent.category.OPENABLE" />
51+
52+
<data android:scheme="file"/>
53+
<data android:scheme="content"/>
54+
55+
<data android:mimeType="application/x-shockwave-flash"/>
56+
57+
<data android:pathSuffix="swf" />
58+
<data android:pathPattern="*.swf" />
59+
</intent-filter>
60+
61+
<intent-filter>
62+
<action android:name="android.intent.action.VIEW" />
63+
<category android:name="android.intent.category.BROWSABLE" />
64+
<category android:name="android.intent.category.DEFAULT" />
65+
<data android:scheme="http" />
66+
<data android:scheme="https" />
67+
<data android:host="*" />
68+
<data android:pathPattern=".*\\.swf" />
69+
</intent-filter>
70+
71+
</activity>
72+
</application>
6873
</manifest>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package rs.ruffle
2+
3+
import android.os.Bundle
4+
import androidx.activity.ComponentActivity
5+
import androidx.activity.compose.setContent
6+
import androidx.activity.enableEdgeToEdge
7+
import rs.ruffle.ui.theme.RuffleTheme
8+
9+
class PanicActivity : ComponentActivity() {
10+
override fun onCreate(savedInstanceState: Bundle?) {
11+
enableEdgeToEdge()
12+
super.onCreate(savedInstanceState)
13+
14+
setContent {
15+
RuffleTheme {
16+
PanicScreen(message = intent.getStringExtra("message") ?: "Unknown")
17+
}
18+
}
19+
}
20+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package rs.ruffle
2+
3+
import android.content.res.Configuration
4+
import androidx.compose.foundation.horizontalScroll
5+
import androidx.compose.foundation.layout.Arrangement
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.fillMaxWidth
9+
import androidx.compose.foundation.layout.padding
10+
import androidx.compose.foundation.layout.wrapContentSize
11+
import androidx.compose.foundation.rememberScrollState
12+
import androidx.compose.foundation.text.selection.SelectionContainer
13+
import androidx.compose.foundation.verticalScroll
14+
import androidx.compose.material3.MaterialTheme
15+
import androidx.compose.material3.Scaffold
16+
import androidx.compose.material3.Text
17+
import androidx.compose.runtime.Composable
18+
import androidx.compose.ui.Alignment
19+
import androidx.compose.ui.Modifier
20+
import androidx.compose.ui.tooling.preview.Preview
21+
import androidx.compose.ui.unit.dp
22+
import rs.ruffle.ui.theme.RuffleTheme
23+
24+
@Composable
25+
fun PanicScreen(message: String) {
26+
Scaffold { innerPadding ->
27+
Column(
28+
modifier = Modifier
29+
.padding(innerPadding)
30+
.fillMaxSize(),
31+
verticalArrangement = Arrangement.Center
32+
) {
33+
Text(
34+
modifier = Modifier
35+
.fillMaxWidth()
36+
.wrapContentSize(align = Alignment.Center),
37+
style = MaterialTheme.typography.headlineLarge,
38+
text = "Ruffle Panicked :("
39+
)
40+
SelectionContainer {
41+
Text(
42+
modifier = Modifier
43+
.wrapContentSize(align = Alignment.Center)
44+
.padding(horizontal = 8.dp, vertical = 20.dp)
45+
.verticalScroll(rememberScrollState())
46+
.horizontalScroll(rememberScrollState()),
47+
text = message,
48+
softWrap = false
49+
)
50+
}
51+
}
52+
}
53+
}
54+
55+
@Preview(name = "Panic - Light", uiMode = Configuration.UI_MODE_NIGHT_NO)
56+
@Preview(name = "Panic - Dark", uiMode = Configuration.UI_MODE_NIGHT_YES)
57+
@Composable
58+
fun PanicScreenPreview() {
59+
RuffleTheme {
60+
PanicScreen(
61+
message = """Error: panicked at core/src/display_object/movie_clip.rs:477:9:
62+
assertion `left == right` failed: Called replace_movie on a clip with LoaderInfo set
63+
left: Some(LoaderInfoObject(LoaderInfoObject { ptr: 0x31b30a8 }))
64+
right: None
65+
at n.wbg.__wbg_new_796382978dfd4fb0 (https://unpkg.com/@ruffle-rs/ruffle/core.ruffle.90db0a0ab193ed0c601b.js:1:83857)
66+
at ruffle_web.wasm.js_sys::Error::new::hfb561c222a4e70eb (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[12733]:0x98671a)
67+
at ruffle_web.wasm.core::ops::function::FnOnce::call_once{{vtable.shim}}::h8a2a563fa204b611 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[9789]:0x9164aa)
68+
at ruffle_web.wasm.std::panicking::rust_panic_with_hook::h33fe77d38d305ca3 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[6355]:0x8070ed)
69+
at ruffle_web.wasm.core::panicking::panic_fmt::hde8b7aa66e2831e1 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[9511]:0x9071fd)
70+
at ruffle_web.wasm.core::panicking::assert_failed_inner::hc95b7725cb4077cb (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[4402]:0x73cb5e)
71+
at ruffle_web.wasm.ruffle_core::display_object::movie_clip::MovieClip::replace_with_movie::haf940b0718ed269c (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[2052]:0x50a035)
72+
at ruffle_web.wasm.ruffle_core::loader::Loader::movie_loader::{{closure}}::h566c935379317178 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[1053]:0x2bc268)
73+
at ruffle_web.wasm.<ruffle_web::navigator::WebNavigatorBackend as ruffle_core::backend::navigator::NavigatorBackend>::spawn_future::{{closure}}::h13f3540dbe40e875 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[1520]:0x419980)
74+
at ruffle_web.wasm.wasm_bindgen_futures::queue::Queue::new::{{closure}}::hf37247571cf9bbf7 (wasm://wasm/ruffle_web.wasm-0321683a:wasm-function[3648]:0x6ba342)"""
75+
)
76+
}
77+
}

app/src/main/java/rs/ruffle/PlayerActivity.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import android.net.Uri
77
import android.os.Build
88
import android.os.Build.VERSION_CODES
99
import android.os.Bundle
10+
import android.util.Log
1011
import android.view.Menu
1112
import android.view.MenuItem
1213
import android.view.MotionEvent
@@ -230,6 +231,14 @@ class PlayerActivity : GameActivity() {
230231
}
231232

232233
override fun onCreate(savedInstanceState: Bundle?) {
234+
nativeInit { message ->
235+
Log.e("ruffle", "Handling panic: $message")
236+
startActivity(
237+
Intent(this, PanicActivity::class.java).apply {
238+
putExtra("message", message)
239+
}
240+
)
241+
}
233242
// When true, the app will fit inside any system UI windows.
234243
// When false, we render behind any system UI windows.
235244
WindowCompat.setDecorFitsSystemWindows(window, false)
@@ -264,11 +273,10 @@ class PlayerActivity : GameActivity() {
264273
init {
265274
// load the native activity
266275
System.loadLibrary("ruffle_android")
267-
nativeInit()
268276
}
269277

270278
@JvmStatic
271-
private external fun nativeInit()
279+
private external fun nativeInit(crashCallback: CrashCallback)
272280

273281
private fun <T> gatherAllDescendantsOfType(v: View, t: Class<*>): List<T> {
274282
val result: MutableList<T> = ArrayList()
@@ -282,4 +290,8 @@ class PlayerActivity : GameActivity() {
282290
return result
283291
}
284292
}
293+
294+
fun interface CrashCallback {
295+
fun onCrash(message: String)
296+
}
285297
}

src/java.rs

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -172,33 +172,31 @@ impl JavaInterface {
172172
}
173173

174174
pub fn init(env: &mut JNIEnv, class: &JClass) {
175-
JAVA_INTERFACE
176-
.set(JavaInterface {
177-
get_surface_width: env
178-
.get_method_id(class, "getSurfaceWidth", "()I")
179-
.expect("getSurfaceWidth must exist"),
180-
get_surface_height: env
181-
.get_method_id(class, "getSurfaceHeight", "()I")
182-
.expect("getSurfaceHeight must exist"),
183-
show_context_menu: env
184-
.get_method_id(class, "showContextMenu", "([Ljava/lang/String;)V")
185-
.expect("showContextMenu must exist"),
186-
get_swf_bytes: env
187-
.get_method_id(class, "getSwfBytes", "()[B")
188-
.expect("getSwfBytes must exist"),
189-
get_swf_uri: env
190-
.get_method_id(class, "getSwfUri", "()Ljava/lang/String;")
191-
.expect("getSwfUri must exist"),
192-
get_trace_output: env
193-
.get_method_id(class, "getTraceOutput", "()Ljava/lang/String;")
194-
.expect("getTraceOutput must exist"),
195-
get_loc_in_window: env
196-
.get_method_id(class, "getLocInWindow", "()[I")
197-
.expect("getLocInWindow must exist"),
198-
get_android_data_storage_dir: env
199-
.get_method_id(class, "getAndroidDataStorageDir", "()Ljava/lang/String;")
200-
.expect("getAndroidDataStorageDir must exist"),
201-
})
202-
.unwrap_or_else(|_| panic!("Init cannot be called more than once!"))
175+
let _ = JAVA_INTERFACE.set(JavaInterface {
176+
get_surface_width: env
177+
.get_method_id(class, "getSurfaceWidth", "()I")
178+
.expect("getSurfaceWidth must exist"),
179+
get_surface_height: env
180+
.get_method_id(class, "getSurfaceHeight", "()I")
181+
.expect("getSurfaceHeight must exist"),
182+
show_context_menu: env
183+
.get_method_id(class, "showContextMenu", "([Ljava/lang/String;)V")
184+
.expect("showContextMenu must exist"),
185+
get_swf_bytes: env
186+
.get_method_id(class, "getSwfBytes", "()[B")
187+
.expect("getSwfBytes must exist"),
188+
get_swf_uri: env
189+
.get_method_id(class, "getSwfUri", "()Ljava/lang/String;")
190+
.expect("getSwfUri must exist"),
191+
get_trace_output: env
192+
.get_method_id(class, "getTraceOutput", "()Ljava/lang/String;")
193+
.expect("getTraceOutput must exist"),
194+
get_loc_in_window: env
195+
.get_method_id(class, "getLocInWindow", "()[I")
196+
.expect("getLocInWindow must exist"),
197+
get_android_data_storage_dir: env
198+
.get_method_id(class, "getAndroidDataStorageDir", "()Ljava/lang/String;")
199+
.expect("getAndroidDataStorageDir must exist"),
200+
});
203201
}
204202
}

0 commit comments

Comments
 (0)