mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
@@ -267,9 +267,9 @@ jobs:
|
||||
cache: ${{ github.ref_type != 'tag' }}
|
||||
- name: Build
|
||||
run: |
|
||||
cargo install cargo-apk2 cargo-ndk
|
||||
cargo install cargo-apk cargo-ndk
|
||||
source <(cargo ndk-env --target ${{ matrix.target }})
|
||||
cargo apk2 build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --lib
|
||||
cargo apk build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --lib
|
||||
|
||||
collect-artifacts:
|
||||
name: Collect release artifacts
|
||||
|
||||
Generated
+37
-83
@@ -735,25 +735,6 @@ dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e58aa60e59d8dbfcc36138f5f18be5f24394d33b38b24f7fd0b1caa33095f22f"
|
||||
dependencies = [
|
||||
"block-sys",
|
||||
"objc2 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.5.1"
|
||||
@@ -2268,12 +2249,12 @@ dependencies = [
|
||||
"futures",
|
||||
"jni 0.22.4",
|
||||
"mimalloc",
|
||||
"open",
|
||||
"postcard",
|
||||
"rand 0.10.1",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rfd",
|
||||
"robius-open",
|
||||
"sdl3-image-sys",
|
||||
"sdl3-sys",
|
||||
"sdl3-ttf-sys",
|
||||
@@ -2894,16 +2875,6 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icrate"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb69199826926eb864697bddd27f73d9fddcffc004f5733131e15b465e30642"
|
||||
dependencies = [
|
||||
"block2 0.4.0",
|
||||
"objc2 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.2.0"
|
||||
@@ -3182,6 +3153,25 @@ version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
|
||||
|
||||
[[package]]
|
||||
name = "is-docker"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-wsl"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5"
|
||||
dependencies = [
|
||||
"is-docker",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
@@ -4326,6 +4316,17 @@ version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "5.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fbaa89d2ddc8473c78a3adf69eea8cffa28c483b8e02a971ef31527cd0fc92c"
|
||||
dependencies = [
|
||||
"is-wsl",
|
||||
"libc",
|
||||
"pathdiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.2.1"
|
||||
@@ -4450,6 +4451,12 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.13.0"
|
||||
@@ -5163,30 +5170,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "robius-android-env"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "087fcb3061ccc432658a605cb868edd44e0efb08e7a159b486f02804a7616bef"
|
||||
dependencies = [
|
||||
"jni 0.21.1",
|
||||
"ndk-context",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "robius-open"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243e2abbc8c1ca8ddc283056d4675b67e452fd527c3741c5318642da37840ff3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"icrate",
|
||||
"jni 0.21.1",
|
||||
"objc2 0.5.2",
|
||||
"robius-android-env",
|
||||
"windows 0.54.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rowan"
|
||||
version = "0.16.1"
|
||||
@@ -7324,16 +7307,6 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
|
||||
dependencies = [
|
||||
"windows-core 0.54.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
@@ -7377,16 +7350,6 @@ dependencies = [
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
|
||||
dependencies = [
|
||||
"windows-result 0.1.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
@@ -7500,15 +7463,6 @@ dependencies = [
|
||||
"windows-strings 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
|
||||
+2
-14
@@ -6,7 +6,7 @@ rust-version = "1.95.0"
|
||||
|
||||
[features]
|
||||
default = ["gui"]
|
||||
gui = ["dep:rfd", "dep:slint", "dep:robius-open", "dep:slint-build"]
|
||||
gui = ["dep:rfd", "dep:slint", "dep:open", "dep:slint-build"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
@@ -19,7 +19,6 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde-big-array = "0.5"
|
||||
sha2 = "0.11"
|
||||
robius-open = { version = "0.1", default-features = false, optional = true }
|
||||
sdl3-sys = { version = "0.6", features = ["build-from-source-static"] }
|
||||
sdl3-ttf-sys = { version = "0.6", features = ["build-static-vendored", "no-sdlttf-harfbuzz", "no-sdlttf-plutosvg"] }
|
||||
sdl3-image-sys = { version = "0.6", features = ["build-static-vendored"] }
|
||||
@@ -43,6 +42,7 @@ discord-rich-presence = "1.1"
|
||||
slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "unstable-winit-030", "backend-winit", "renderer-femtovg-wgpu", "accessibility"], optional = true }
|
||||
rfd = {version = "0.17", optional = true }
|
||||
dirs = "6.0"
|
||||
open = { version = "5.3", optional = true }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "backend-android-activity-06", "accessibility"], optional = true }
|
||||
@@ -74,22 +74,10 @@ osx_frameworks = ["/opt/homebrew/opt/molten-vk/lib/libMoltenVK.dylib"]
|
||||
osx_minimum_system_version = "15.0"
|
||||
|
||||
[package.metadata.android]
|
||||
use_aapt2 = true
|
||||
package = "io.github.gopher64.gopher64"
|
||||
build_targets = ["aarch64-linux-android"]
|
||||
resources = "data/android/res"
|
||||
|
||||
[[package.metadata.android.application.activity]]
|
||||
name = "android.app.NativeActivity"
|
||||
|
||||
[[package.metadata.android.application.activity.intent_filter]]
|
||||
actions = ["android.intent.action.MAIN"]
|
||||
categories = ["android.intent.category.LAUNCHER"]
|
||||
|
||||
[[package.metadata.android.application.activity.meta_data]]
|
||||
name = "android.app.lib_name"
|
||||
value = "gopher64"
|
||||
|
||||
[package.metadata.android.application]
|
||||
icon = "@mipmap/ic_launcher"
|
||||
label = "Gopher64"
|
||||
|
||||
+2
-2
@@ -293,7 +293,7 @@ pub async fn run() -> std::io::Result<()> {
|
||||
#[cfg(feature = "gui")]
|
||||
{
|
||||
retroachievements::init_client(false, false, false);
|
||||
ui::gui::app_window();
|
||||
ui::gui::app_window(false);
|
||||
retroachievements::shutdown_client();
|
||||
}
|
||||
}
|
||||
@@ -317,5 +317,5 @@ async fn android_main(app: slint::android::AndroidApp) {
|
||||
std::fs::create_dir_all(dirs.data_dir.join("saves")).unwrap();
|
||||
std::fs::create_dir_all(dirs.data_dir.join("states")).unwrap();
|
||||
|
||||
ui::gui::app_window();
|
||||
ui::gui::app_window(true);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#[cfg(target_os = "android")]
|
||||
pub mod android_input;
|
||||
pub mod android;
|
||||
pub mod audio;
|
||||
#[cfg(feature = "gui")]
|
||||
pub mod cheats;
|
||||
|
||||
@@ -1,5 +1,43 @@
|
||||
use jni::objects::JString;
|
||||
use jni::refs::Global;
|
||||
use jni::{Env, JavaVM, bind_java_type};
|
||||
|
||||
use crate::ui;
|
||||
|
||||
bind_java_type! {
|
||||
AndroidActivity => "android.app.Activity",
|
||||
type_map = {
|
||||
AndroidIntent => "android.content.Intent",
|
||||
},
|
||||
methods {
|
||||
fn start_activity(intent: AndroidIntent) -> (),
|
||||
},
|
||||
}
|
||||
|
||||
bind_java_type! {
|
||||
AndroidIntent => "android.content.Intent",
|
||||
type_map = {
|
||||
AndroidUri => "android.net.Uri",
|
||||
},
|
||||
fields {
|
||||
#[allow(non_snake_case)]
|
||||
static ACTION_VIEW: JString,
|
||||
},
|
||||
constructors {
|
||||
fn new(action: JString),
|
||||
},
|
||||
methods {
|
||||
fn set_data(uri: AndroidUri) -> AndroidIntent,
|
||||
},
|
||||
}
|
||||
|
||||
bind_java_type! {
|
||||
AndroidUri => "android.net.Uri",
|
||||
methods {
|
||||
static fn parse(uri_string: JString) -> AndroidUri,
|
||||
},
|
||||
}
|
||||
|
||||
bind_java_type! {
|
||||
pub AndroidInputDevice => "android.view.InputDevice",
|
||||
fields {
|
||||
@@ -30,7 +68,7 @@ pub struct ControllerInfo {
|
||||
|
||||
/// Lists connected gamepads and joysticks using the Android framework.
|
||||
pub fn list_controllers() -> Vec<ControllerInfo> {
|
||||
let Some(app) = crate::ui::ANDROID_APP.get() else {
|
||||
let Some(app) = ui::ANDROID_APP.get() else {
|
||||
eprintln!("Android app not initialized; cannot list controllers");
|
||||
return Vec::new();
|
||||
};
|
||||
@@ -104,3 +142,38 @@ fn list_controllers_on_jvm(env: &mut Env<'_>) -> jni::errors::Result<Vec<Control
|
||||
|
||||
Ok(controllers)
|
||||
}
|
||||
|
||||
/// Opens a URI in the user's default app via [`Intent::ACTION_VIEW`](https://developer.android.com/reference/android/content/Intent#ACTION_VIEW).
|
||||
pub fn open_uri(path: &str) {
|
||||
let Some(app) = ui::ANDROID_APP.get() else {
|
||||
eprintln!("Android app not initialized; cannot open URI");
|
||||
return;
|
||||
};
|
||||
|
||||
let path = path.to_string();
|
||||
app.clone().run_on_java_main_thread(Box::new(move || {
|
||||
let vm = unsafe { JavaVM::from_raw(app.vm_as_ptr().cast()) };
|
||||
if let Err(err) = vm.attach_current_thread(|env| open_uri_on_java_main(env, &app, &path)) {
|
||||
eprintln!("JNI error while opening URI: {err:?}");
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
fn open_uri_on_java_main(
|
||||
env: &mut Env<'_>,
|
||||
app: &slint::android::AndroidApp,
|
||||
path: &str,
|
||||
) -> jni::errors::Result<()> {
|
||||
let activity_ptr = app.activity_as_ptr().cast();
|
||||
let activity = unsafe { env.as_cast_raw::<Global<AndroidActivity>>(&activity_ptr)? };
|
||||
|
||||
let uri_string = JString::from_str(env, path.to_string())?;
|
||||
let uri = AndroidUri::parse(env, &uri_string)?;
|
||||
|
||||
let action_view = AndroidIntent::ACTION_VIEW(env)?;
|
||||
let intent = AndroidIntent::new(env, &action_view)?;
|
||||
intent.set_data(env, &uri)?;
|
||||
|
||||
activity.as_ref().start_activity(env, &intent)?;
|
||||
Ok(())
|
||||
}
|
||||
+19
-11
@@ -46,12 +46,18 @@ fn check_latest_version(weak: slint::Weak<AppWindow>) {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn open_uri(path: &str) {
|
||||
if let Err(e) = robius_open::Uri::new(path).open() {
|
||||
eprintln!("Error opening path: {:?}", e);
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub fn open_uri(path: impl AsRef<std::ffi::OsStr>) {
|
||||
if let Err(e) = open::that_detached(path) {
|
||||
eprintln!("Error opening path: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn open_uri(path: impl AsRef<std::ffi::OsStr>) {
|
||||
ui::android::open_uri(path.as_ref().to_str().unwrap())
|
||||
}
|
||||
|
||||
fn run_with_path(weak: slint::Weak<AppWindow>, path: std::path::PathBuf) {
|
||||
let weak2 = weak.clone();
|
||||
weak.upgrade_in_event_loop(move |handle| {
|
||||
@@ -88,8 +94,6 @@ fn file_dropped(app: &AppWindow) {
|
||||
}
|
||||
|
||||
fn local_game_window(app: &AppWindow, config: &ui::config::Config) {
|
||||
let dirs = ui::get_dirs();
|
||||
|
||||
app.set_recent_roms(slint::ModelRc::from(std::rc::Rc::new(
|
||||
slint::VecModel::from(
|
||||
config
|
||||
@@ -131,12 +135,15 @@ fn local_game_window(app: &AppWindow, config: &ui::config::Config) {
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
let saves_path = dirs.data_dir.join("saves");
|
||||
app.on_saves_folder_button_clicked(move || {
|
||||
open_uri(saves_path.to_str().unwrap());
|
||||
});
|
||||
#[cfg(not(target_os = "android"))]
|
||||
file_dropped(app);
|
||||
{
|
||||
let saves_path = ui::get_dirs().data_dir.join("saves");
|
||||
app.on_saves_folder_button_clicked(move || {
|
||||
open_uri(&saves_path);
|
||||
});
|
||||
|
||||
file_dropped(app);
|
||||
}
|
||||
}
|
||||
|
||||
fn input_profiles(config: &ui::config::Config) -> Vec<String> {
|
||||
@@ -501,8 +508,9 @@ fn about_window(app: &AppWindow) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app_window() {
|
||||
pub fn app_window(is_android: bool) {
|
||||
let app = AppWindow::new().unwrap();
|
||||
app.set_is_android(is_android);
|
||||
about_window(&app);
|
||||
ui::retroachievements::ra_window(&app);
|
||||
{
|
||||
|
||||
@@ -79,6 +79,7 @@ export component AppWindow inherits Window {
|
||||
in-out property controller_paths <=> ControllerData.controller_paths;
|
||||
in-out property selected_controller <=> ControllerData.selected_controller;
|
||||
in-out property game_running <=> State.game_running;
|
||||
in-out property is_android <=> State.is_android;
|
||||
in-out property recent_roms <=> State.recent_roms;
|
||||
in-out property cheat_game_name <=> CheatsData.cheat_game_name;
|
||||
in-out property cheat_game_crc <=> CheatsData.cheat_game_crc;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AboutData, NewVersionButton } from "about_page.slint";
|
||||
|
||||
export global State {
|
||||
in-out property <bool> game_running;
|
||||
in-out property <bool> is_android;
|
||||
in-out property <[{a:string, b:string}]> recent_roms;
|
||||
}
|
||||
|
||||
@@ -30,7 +31,7 @@ export component LocalGame inherits Page {
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalBox {
|
||||
if !State.is_android:HorizontalBox {
|
||||
alignment: center;
|
||||
Button {
|
||||
text: @tr("Open Saves Folder");
|
||||
|
||||
+2
-2
@@ -362,7 +362,7 @@ pub fn get_controller_names() -> Vec<String> {
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn get_controller_names() -> Vec<String> {
|
||||
let mut controllers: Vec<String> = ui::android_input::list_controllers()
|
||||
let mut controllers: Vec<String> = ui::android::list_controllers()
|
||||
.into_iter()
|
||||
.map(|c| c.name)
|
||||
.collect();
|
||||
@@ -391,7 +391,7 @@ pub fn get_controller_paths() -> Vec<String> {
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(feature = "gui")]
|
||||
pub fn get_controller_paths() -> Vec<String> {
|
||||
let mut controller_paths: Vec<String> = ui::android_input::list_controllers()
|
||||
let mut controller_paths: Vec<String> = ui::android::list_controllers()
|
||||
.into_iter()
|
||||
.map(|c| c.descriptor)
|
||||
.collect();
|
||||
|
||||
@@ -100,7 +100,7 @@ pub fn ra_window(app: &ui::gui::AppWindow) {
|
||||
});
|
||||
|
||||
app.on_ra_show_profile_clicked(move || {
|
||||
ui::gui::open_uri(&format!(
|
||||
ui::gui::open_uri(format!(
|
||||
"https://retroachievements.org/user/{}",
|
||||
retroachievements::get_username().unwrap_or_default()
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user