From 977b193e0029708f36ced55c60caf871bd88b13e Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Thu, 17 Sep 2026 16:57:36 +0200 Subject: [PATCH] 0.1.0-2-beta.1.fix.3 --- .../sasedev/games/common/NativeBridge.java | 12 +- .../games/common/SaseGameActivity.java | 25 +++- Android/game-reflex-poc/build.gradle | 4 +- Android/game-snake-poc/build.gradle | 4 +- Cargo.toml | 3 +- .../apps/game-android-entrypoint/Cargo.toml | 2 + .../apps/game-android-entrypoint/src/lib.rs | 16 ++- .../apps/game-reflex-poc-tauri/package.json | 2 +- .../game-reflex-poc-tauri/tauri.conf.json | 2 +- crates/common/game-logging-lib/Cargo.toml | 3 + crates/common/game-logging-lib/src/lib.rs | 8 +- crates/common/game-logging-lib/src/runtime.rs | 39 ++++-- crates/engines/engine-v1-sdl/src/lib.rs | 4 +- crates/engines/engine-v1-sdl/src/runtime.rs | 27 +++- .../engines/engine-v1-sdl/unit_tests/swipe.rs | 9 +- deltas/0.1.0/2-beta.1.fix.3.md | 117 ++++++++++++++++++ docs/development/007-ANDROID_JNI_BRIDGE.md | 21 +++- docs/rules/RULES_PROJECT.md | 5 +- docs/rules/RULES_RUST.md | 4 +- docs/testing/002-BETA_VALIDATION_MATRIX.md | 20 ++- scripts/audit_rust_general_rules.py | 1 + 21 files changed, 289 insertions(+), 39 deletions(-) create mode 100644 deltas/0.1.0/2-beta.1.fix.3.md diff --git a/Android/common/src/main/java/com/sasedev/games/common/NativeBridge.java b/Android/common/src/main/java/com/sasedev/games/common/NativeBridge.java index 08c5cf3..b4e46ef 100644 --- a/Android/common/src/main/java/com/sasedev/games/common/NativeBridge.java +++ b/Android/common/src/main/java/com/sasedev/games/common/NativeBridge.java @@ -1,11 +1,11 @@ // file: Android/common/src/main/java/com/sasedev/games/common/NativeBridge.java -// version: 2 +// version: 3 package com.sasedev.games.common; /** Stable Java/JNI platform bridge contract shared by Android game applications. */ public final class NativeBridge { - private static final int CONTRACT_VERSION = 1; + private static final int CONTRACT_VERSION = 2; private NativeBridge() { } @@ -26,6 +26,9 @@ public final class NativeBridge { */ public static native int nativeContractVersion(); + /** Sends a platform-native Back request to the Rust SDL runtime. */ + private static native void nativeRequestPlatformBack(); + /** Verifies that the loaded native bridge matches the Java contract. */ public static void requireCompatibleContract() { final int nativeVersion = nativeContractVersion(); @@ -34,4 +37,9 @@ public final class NativeBridge { "Native bridge contract mismatch: java=" + CONTRACT_VERSION + ", native=" + nativeVersion); } } + + /** Requests Back through the common Rust engine quit policy. */ + public static void requestPlatformBack() { + nativeRequestPlatformBack(); + } } diff --git a/Android/common/src/main/java/com/sasedev/games/common/SaseGameActivity.java b/Android/common/src/main/java/com/sasedev/games/common/SaseGameActivity.java index 09e2f51..4f95414 100644 --- a/Android/common/src/main/java/com/sasedev/games/common/SaseGameActivity.java +++ b/Android/common/src/main/java/com/sasedev/games/common/SaseGameActivity.java @@ -1,8 +1,9 @@ // file: Android/common/src/main/java/com/sasedev/games/common/SaseGameActivity.java -// version: 4 +// version: 5 package com.sasedev.games.common; +import android.os.Build; import android.os.Bundle; /** @@ -17,6 +18,16 @@ public abstract class SaseGameActivity extends org.libsdl.app.SDLActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); NativeBridge.requireCompatibleContract(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + Api33BackHandler.register(this); + } + } + + /** {@inheritDoc} */ + @SuppressWarnings("deprecation") + @Override + public void onBackPressed() { + NativeBridge.requestPlatformBack(); } /** {@inheritDoc} */ @@ -31,4 +42,16 @@ public abstract class SaseGameActivity extends org.libsdl.app.SDLActivity { * @return game identifier */ public abstract String gameId(); + + /** API 33+ predictive Back bridge isolated from pre-33 class loading. */ + private static final class Api33BackHandler { + private Api33BackHandler() { + } + + static void register(SaseGameActivity activity) { + activity.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT, + NativeBridge::requestPlatformBack); + } + } } diff --git a/Android/game-reflex-poc/build.gradle b/Android/game-reflex-poc/build.gradle index c7b1a66..f94ad9d 100644 --- a/Android/game-reflex-poc/build.gradle +++ b/Android/game-reflex-poc/build.gradle @@ -1,5 +1,5 @@ // file: Android/game-reflex-poc/build.gradle -// version: 19 +// version: 20 plugins { id 'com.android.application' @@ -18,7 +18,7 @@ android { minSdk 21 targetSdk 36 versionCode 1 - versionName '0.1.0-2-beta.1.fix.2' + versionName '0.1.0-2-beta.1.fix.3' } compileOptions { diff --git a/Android/game-snake-poc/build.gradle b/Android/game-snake-poc/build.gradle index e4f1f77..9d45e5c 100644 --- a/Android/game-snake-poc/build.gradle +++ b/Android/game-snake-poc/build.gradle @@ -1,5 +1,5 @@ // file: Android/game-snake-poc/build.gradle -// version: 19 +// version: 20 plugins { id 'com.android.application' @@ -18,7 +18,7 @@ android { minSdk 21 targetSdk 36 versionCode 1 - versionName '0.1.0-2-beta.1.fix.2' + versionName '0.1.0-2-beta.1.fix.3' } compileOptions { diff --git a/Cargo.toml b/Cargo.toml index 238ad8a..ffdc5f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ ] [workspace.package] -version = "0.1.0-2-beta.1.fix.2" +version = "0.1.0-2-beta.1.fix.3" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/games" @@ -30,6 +30,7 @@ publish = false serde = { version = "1", features = ["derive"] } sdl3 = "^0.20" tracing = "0.1.44" +tracing-android = "0.2.0" tracing-appender = "0.2.5" tracing-subscriber = "0.3.23" tauri = "2" diff --git a/crates/apps/game-android-entrypoint/Cargo.toml b/crates/apps/game-android-entrypoint/Cargo.toml index b007a53..4792d13 100644 --- a/crates/apps/game-android-entrypoint/Cargo.toml +++ b/crates/apps/game-android-entrypoint/Cargo.toml @@ -21,6 +21,8 @@ snake = ["dep:game-snake-poc"] [dependencies] engine-v1-common = { path = "../../engines/engine-v1-common" } engine-v1-sdl = { path = "../../engines/engine-v1-sdl" } +game-logging-lib = { path = "../../common/game-logging-lib" } +tracing.workspace = true game-reflex-poc = { path = "../../games/game-reflex-poc", optional = true } game-snake-poc = { path = "../../games/game-snake-poc", optional = true } diff --git a/crates/apps/game-android-entrypoint/src/lib.rs b/crates/apps/game-android-entrypoint/src/lib.rs index 6733df2..c7c3830 100644 --- a/crates/apps/game-android-entrypoint/src/lib.rs +++ b/crates/apps/game-android-entrypoint/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/apps/game-android-entrypoint/src/lib.rs -// version: 2 +// version: 3 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -21,7 +21,13 @@ extern "C" fn sdl_main(_argc: core::ffi::c_int, _argv: *mut *mut core::ffi::c_ch #[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")] extern "C" fn native_contract_version(_environment: *mut core::ffi::c_void, _class: *mut core::ffi::c_void) -> core::ffi::c_int { - return 1; + return 2; +} + +#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeRequestPlatformBack")] +extern "C" fn native_request_platform_back(_environment: *mut core::ffi::c_void, _class: *mut core::ffi::c_void) { + engine_v1_sdl::request_platform_back(); + return; } #[cfg(feature = "reflex")] @@ -48,9 +54,15 @@ fn run_game(runtime: &engine_v1_sdl::SdlRuntime, game: &mut G) -> core::ffi:: where G: engine_v1_common::EngineGame, { + let logging_guard = game_logging_lib::init_console_tracing(); + match &logging_guard { + std::result::Result::Ok(_) => tracing::info!(target: "games::android", "Android tracing initialized"), + std::result::Result::Err(error) => eprintln!("Android tracing initialization skipped: {error}"), + } match runtime.run(game) { std::result::Result::Ok(()) => return 0, std::result::Result::Err(error) => { + tracing::error!(target: "games::android", error = %error, "Android SDL3 runtime failed"); eprintln!("Android SDL3 runtime error: {error}"); return 1; }, diff --git a/crates/apps/game-reflex-poc-tauri/package.json b/crates/apps/game-reflex-poc-tauri/package.json index e3d4200..268b2fe 100644 --- a/crates/apps/game-reflex-poc-tauri/package.json +++ b/crates/apps/game-reflex-poc-tauri/package.json @@ -1,7 +1,7 @@ { "name": "game-reflex-poc-tauri", "private": true, - "version": "0.1.0-2-beta.1.fix.2", + "version": "0.1.0-2-beta.1.fix.3", "type": "module", "scripts": { "dev": "vite", diff --git a/crates/apps/game-reflex-poc-tauri/tauri.conf.json b/crates/apps/game-reflex-poc-tauri/tauri.conf.json index 639dc61..acf7bd7 100644 --- a/crates/apps/game-reflex-poc-tauri/tauri.conf.json +++ b/crates/apps/game-reflex-poc-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Reflex POC Tauri", - "version": "0.1.0-2-beta.1.fix.2", + "version": "0.1.0-2-beta.1.fix.3", "identifier": "com.sasedev.games.reflex.tauri", "build": { "beforeDevCommand": { diff --git a/crates/common/game-logging-lib/Cargo.toml b/crates/common/game-logging-lib/Cargo.toml index f0fd715..b6b064b 100644 --- a/crates/common/game-logging-lib/Cargo.toml +++ b/crates/common/game-logging-lib/Cargo.toml @@ -17,3 +17,6 @@ tracing-subscriber = { workspace = true, features = ["fmt"] } [lints] workspace = true + +[target.'cfg(target_os = "android")'.dependencies] +tracing-android.workspace = true diff --git a/crates/common/game-logging-lib/src/lib.rs b/crates/common/game-logging-lib/src/lib.rs index 0395312..4cc3101 100644 --- a/crates/common/game-logging-lib/src/lib.rs +++ b/crates/common/game-logging-lib/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/common/game-logging-lib/src/lib.rs -// version: 1 +// version: 2 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -10,11 +10,11 @@ mod runtime; mod test_support; -/// Re-export of the console tracing initialization error. +/// Re-export of the platform tracing initialization error. pub use self::runtime::LoggingInitError; -/// Re-export of the guard keeping the non-blocking tracing writer alive. +/// Re-export of the guard keeping platform tracing resources alive. pub use self::runtime::LoggingWorkerGuard; -/// Re-export of the standard console tracing initializer. +/// Re-export of the platform tracing initializer. pub use self::runtime::init_console_tracing; /// Re-export of the scoped tracing helper intended for tests. pub use self::test_support::with_test_tracing; diff --git a/crates/common/game-logging-lib/src/runtime.rs b/crates/common/game-logging-lib/src/runtime.rs index ff19c84..2da4548 100644 --- a/crates/common/game-logging-lib/src/runtime.rs +++ b/crates/common/game-logging-lib/src/runtime.rs @@ -1,31 +1,50 @@ // file: crates/common/game-logging-lib/src/runtime.rs -// version: 1 +// version: 2 -/// Error returned when the process-global tracing subscriber is already configured. +/// Error returned when the process-global tracing subscriber cannot be configured. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct LoggingInitError; impl std::fmt::Display for LoggingInitError { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - return formatter.write_str("the process-global tracing subscriber is already configured"); + return formatter.write_str("the process-global tracing subscriber could not be configured"); } } impl std::error::Error for LoggingInitError {} -/// Guard keeping the non-blocking tracing writer alive for the application lifetime. +/// Guard keeping platform tracing resources alive for the application lifetime. pub struct LoggingWorkerGuard { + #[cfg(not(target_os = "android"))] _worker_guard: tracing_appender::non_blocking::WorkerGuard, } -/// Initializes a process-global non-blocking tracing subscriber writing formatted events to standard error. +/// Initializes process-global tracing for the current platform. +/// +/// Native desktop/Tauri processes write formatted events to standard error. +/// Android processes write directly to logcat through the Android NDK logging API. /// /// The returned guard must remain alive for as long as events may still be emitted. pub fn init_console_tracing() -> std::result::Result { - let (writer, worker_guard) = tracing_appender::non_blocking(std::io::stderr()); - let subscriber = tracing_subscriber::fmt().with_target(true).with_writer(writer).finish(); - if tracing::subscriber::set_global_default(subscriber).is_err() { - return std::result::Result::Err(LoggingInitError); + #[cfg(target_os = "android")] + { + let layer = match tracing_android::layer("games.sasedev") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::result::Result::Err(LoggingInitError), + }; + let subscriber = tracing_subscriber::layer::SubscriberExt::with(tracing_subscriber::registry(), layer); + if tracing::subscriber::set_global_default(subscriber).is_err() { + return std::result::Result::Err(LoggingInitError); + } + return std::result::Result::Ok(LoggingWorkerGuard {}); + } + #[cfg(not(target_os = "android"))] + { + let (writer, worker_guard) = tracing_appender::non_blocking(std::io::stderr()); + let subscriber = tracing_subscriber::fmt().with_target(true).with_writer(writer).finish(); + if tracing::subscriber::set_global_default(subscriber).is_err() { + return std::result::Result::Err(LoggingInitError); + } + return std::result::Result::Ok(LoggingWorkerGuard { _worker_guard: worker_guard }); } - return std::result::Result::Ok(LoggingWorkerGuard { _worker_guard: worker_guard }); } diff --git a/crates/engines/engine-v1-sdl/src/lib.rs b/crates/engines/engine-v1-sdl/src/lib.rs index 6dfb86b..fd70f66 100644 --- a/crates/engines/engine-v1-sdl/src/lib.rs +++ b/crates/engines/engine-v1-sdl/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/engines/engine-v1-sdl/src/lib.rs -// version: 3 +// version: 4 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -12,3 +12,5 @@ mod unit_tests; /// Re-export of the minimal SDL3 runtime used by Desktop POC runners. pub use self::runtime::SdlRuntime; +/// Re-export of the platform-native Back request bridge. +pub use self::runtime::request_platform_back; diff --git a/crates/engines/engine-v1-sdl/src/runtime.rs b/crates/engines/engine-v1-sdl/src/runtime.rs index 1904a00..cd05639 100644 --- a/crates/engines/engine-v1-sdl/src/runtime.rs +++ b/crates/engines/engine-v1-sdl/src/runtime.rs @@ -1,5 +1,5 @@ // file: crates/engines/engine-v1-sdl/src/runtime.rs -// version: 11 +// version: 12 /// Minimal SDL3 runtime used by Desktop and Android POC runners. pub struct SdlRuntime { @@ -9,6 +9,17 @@ pub struct SdlRuntime { frame_duration: std::time::Duration, } +static PLATFORM_BACK_REQUESTED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + +/// Requests the active SDL runtime to evaluate a platform-native Back action. +/// +/// Platform adapters use this signal when the host operating system does not expose Back +/// as a normal SDL keyboard event. +pub fn request_platform_back() { + PLATFORM_BACK_REQUESTED.store(true, std::sync::atomic::Ordering::Release); + return; +} + impl SdlRuntime { /// Creates a minimal SDL3 runtime configuration. #[must_use] @@ -21,10 +32,6 @@ impl SdlRuntime { where G: engine_v1_common::EngineGame, { - #[cfg(target_os = "android")] - { - let _ = sdl3::hint::set(sdl3::hint::names::ANDROID_TRAP_BACK_BUTTON, "1"); - } let sdl = match sdl3::init() { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), @@ -47,6 +54,12 @@ impl SdlRuntime { let mut pointer = engine_v1_common::PointerState::inactive(); tracing::info!(title = %self.title, width = self.width, height = self.height, "SDL3 runtime started"); 'running: loop { + if take_platform_back_request() { + tracing::info!(source = "platform_back", "SDL3 platform Back requested"); + if quit_requested(game, engine_v1_common::QuitSource::PlatformBack) { + break 'running; + } + } let mut input = engine_v1_common::InputState::none().with_pointer(pointer); for event in events.poll_iter() { match event { @@ -166,6 +179,10 @@ fn render_scene(canvas: &mut sdl3::render::WindowCanvas, scene: engine_v1_common const SWIPE_DIRECTION_THRESHOLD: f32 = 0.04; +fn take_platform_back_request() -> bool { + return PLATFORM_BACK_REQUESTED.swap(false, std::sync::atomic::Ordering::AcqRel); +} + fn quit_requested(game: &mut G, source: engine_v1_common::QuitSource) -> bool where G: engine_v1_common::EngineGame, diff --git a/crates/engines/engine-v1-sdl/unit_tests/swipe.rs b/crates/engines/engine-v1-sdl/unit_tests/swipe.rs index befcf06..639bf41 100644 --- a/crates/engines/engine-v1-sdl/unit_tests/swipe.rs +++ b/crates/engines/engine-v1-sdl/unit_tests/swipe.rs @@ -1,5 +1,5 @@ // file: crates/engines/engine-v1-sdl/unit_tests/swipe.rs -// version: 3 +// version: 4 fn pointer(x: f32, y: f32) -> engine_v1_common::PointerState { return engine_v1_common::PointerState::normalized(true, x, y); @@ -48,3 +48,10 @@ fn runtime_respects_game_quit_decision() { assert!(!super::quit_requested(&mut game, engine_v1_common::QuitSource::PlatformBack)); assert_eq!(game.requests, 1); } + +#[test] +fn platform_back_bridge_is_consumed_once() { + super::request_platform_back(); + assert!(super::take_platform_back_request()); + assert!(!super::take_platform_back_request()); +} diff --git a/deltas/0.1.0/2-beta.1.fix.3.md b/deltas/0.1.0/2-beta.1.fix.3.md new file mode 100644 index 0000000..fa3bfba --- /dev/null +++ b/deltas/0.1.0/2-beta.1.fix.3.md @@ -0,0 +1,117 @@ + + + +# Delta 0.1.0-2-beta.1.fix.3 + +## Base + +Base déclarée : `0.1.0-2-beta.1.fix.2`. + +## Échecs observés sur AVD API 36 + +Le gameplay Reflex et Snake fonctionne en x86_64, mais : + +- le bouton Back de l'AVD ne ferme pas le runtime ; +- `logcat` ne montre aucun événement `tracing` du moteur. + +## Back Android 16 + +Le projet cible API 36. + +Sur Android 16, `onBackPressed()` et `KEYCODE_BACK` ne constituent plus la voie de référence. Le common Android enregistre désormais `OnBackInvokedCallback` sur API 33+. + +La chaîne devient : + +```text +Android Back +-> SaseGameActivity +-> NativeBridge.requestPlatformBack() +-> JNI nativeRequestPlatformBack() +-> engine-v1-sdl::request_platform_back() +-> QuitSource::PlatformBack +-> EngineGame::quit_requested() +``` + +Le mapping SDL `AcBack` reste accepté comme événement SDL générique, mais Android ne dépend plus du hint `SDL_ANDROID_TRAP_BACK_BUTTON`. + +## Logging Android + +`game-android-entrypoint` initialise désormais `game-logging-lib`. + +Sur Android, `game-logging-lib` utilise `tracing-android` pour écrire directement vers logcat avec le tag : + +```text +games.sasedev +``` + +Desktop et Tauri conservent le writer stderr existant. + +## Contrat JNI + +Le contrat Java/JNI passe de `1` à `2`. + +Nouvel export autorisé et audité : + +```text +Java_com_sasedev_games_common_NativeBridge_nativeRequestPlatformBack +``` + +## Validation statique/Rust + +```bash +cargo fmt --all +cargo fmt --all -- --check + +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates Android deltas history +python3 scripts/audit_distribution_layout.py + +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings + +cargo test -p engine-v1-sdl --all-targets --all-features +cargo test -p game-logging-lib --all-targets --all-features +``` + +## Android ARM64 + +```bash +python3 scripts/build_android_rust.py reflex --abi arm64-v8a +python3 scripts/build_android_rust.py snake --abi arm64-v8a + +cd Android +gradle :game-reflex-poc:assembleDebug +gradle :game-snake-poc:assembleDebug +cd .. +``` + +## Android x86_64 + +```bash +python3 scripts/build_android_rust.py reflex --abi x86_64 +python3 scripts/build_android_rust.py snake --abi x86_64 + +cd Android +gradle :game-reflex-poc:assembleDebug +gradle :game-snake-poc:assembleDebug +cd .. +``` + +## Smoke logcat correct + +Vider logcat avant le lancement : + +```bash +adb -s emulator-5554 logcat -c +adb -s emulator-5554 shell am start -n com.sasedev.games.reflex/.ReflexActivity +adb -s emulator-5554 logcat | grep -iE 'games\.sasedev|com\.sasedev\.games|SDL|AndroidRuntime|FATAL|panic' +``` + +Attendu : + +- événement d'initialisation tracing Android ; +- événement `SDL3 runtime started` ; +- sur Back, événement `SDL3 platform Back requested` ; +- événement `SDL3 runtime stopped`. + +Répéter pour Snake et, après validation AVD, refaire un smoke court ARM64 réel. diff --git a/docs/development/007-ANDROID_JNI_BRIDGE.md b/docs/development/007-ANDROID_JNI_BRIDGE.md index 1c96319..7cecb06 100644 --- a/docs/development/007-ANDROID_JNI_BRIDGE.md +++ b/docs/development/007-ANDROID_JNI_BRIDGE.md @@ -1,5 +1,5 @@ - + # Bridge Java/JNI Android @@ -14,7 +14,7 @@ L'input tactile de base ne passe pas par JNI : SDL3 fournit directement les év La baseline expose un unique appel Java vers Rust : ```text -NativeBridge.nativeContractVersion() -> 1 +NativeBridge.nativeContractVersion() -> 2 ``` `SaseGameActivity` vérifie ce contrat après `SDLActivity.onCreate()`. @@ -45,3 +45,20 @@ L'export JNI utilise un symbole nommé explicitement et reçoit deux pointeurs o Aucun bloc `unsafe` ni fonction `unsafe` n'est autorisé. Si un futur service nécessite de manipuler réellement `JNIEnv`, il devra faire l'objet d'une tranche distincte avec une politique FFI réévaluée plutôt que d'élargir implicitement l'exception actuelle. + +## Back Android + +Le contrat v2 ajoute un pont Java/JNI explicite pour Back : + +```text +SaseGameActivity +-> NativeBridge.requestPlatformBack() +-> nativeRequestPlatformBack() +-> engine-v1-sdl::request_platform_back() +-> EngineGame::quit_requested(QuitSource::PlatformBack) +``` + +Sur API 33+, `SaseGameActivity` enregistre un `OnBackInvokedCallback`. +Sur les versions antérieures, `onBackPressed()` reste le fallback. + +Cette voie remplace la dépendance au mapping variable du bouton Back vers `SDL_SCANCODE_AC_BACK`. diff --git a/docs/rules/RULES_PROJECT.md b/docs/rules/RULES_PROJECT.md index 1da0445..b7a957e 100644 --- a/docs/rules/RULES_PROJECT.md +++ b/docs/rules/RULES_PROJECT.md @@ -1,5 +1,5 @@ - + # Règles spécifiques games.sasedev @@ -72,3 +72,6 @@ - **GAME-PLATFORM-013** — Les demandes de sortie Tauri sont évaluées par la politique `EngineGame::quit_requested` avant toute fermeture native. - **GAME-BUILD-001** — Les artefacts générés Cargo, Tauri, Vite et caches frontend sont placés hors de la racine du dépôt, sous `../builds/sasedev-games/`; un répertoire racine `builds/` dans le dépôt est interdit. + +- **GAME-PLATFORM-014** — Android API 33+ traite Back via `OnBackInvokedCallback`; le bridge Java/JNI doit convertir cette action en `QuitSource::PlatformBack` et laisser `EngineGame::quit_requested` décider. +- **GAME-PLATFORM-015** — Les exécutables Android initialisent le subscriber partagé et envoient les événements `tracing` vers logcat ; `stderr` n'est pas la destination Android de référence. diff --git a/docs/rules/RULES_RUST.md b/docs/rules/RULES_RUST.md index 05b5446..4b077b5 100644 --- a/docs/rules/RULES_RUST.md +++ b/docs/rules/RULES_RUST.md @@ -1,5 +1,5 @@ - + # Règles Rust générales @@ -8,7 +8,7 @@ - **RUST-BASE-001** — L'édition Rust est Rust 2024. - **RUST-BASE-002** — Chaque `lib.rs` et `main.rs` active `missing_docs`, `unreachable_pub` et interdit `unsafe_code`, sauf exception FFI explicitement définie par cette règle. - **RUST-BASE-003** — Les lints communs sont déclarés au workspace et hérités par les crates ; une crate FFI exceptée peut déclarer localement le même profil avec uniquement le niveau `unsafe_code` ajusté. -- **RUST-BASE-004** — Les blocs `unsafe` et fonctions `unsafe` sont interdits. L’unique exception actuelle est `crates/apps/game-android-entrypoint/src/lib.rs`, autorisé à porter uniquement les attributs `#[unsafe(export_name = "SDL_main")]` et `#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")]` nécessaires aux frontières SDL Android et JNI. Cette exception n’autorise aucun déréférencement de pointeur brut, aucun autre attribut unsafe, aucun bloc `unsafe` et aucune fonction `unsafe`. +- **RUST-BASE-004** — Les blocs `unsafe` et fonctions `unsafe` sont interdits. L’unique exception actuelle est `crates/apps/game-android-entrypoint/src/lib.rs`, autorisé à porter uniquement les attributs `#[unsafe(export_name = "SDL_main")]` , `#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")]` et `#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeRequestPlatformBack")]` nécessaires aux frontières SDL Android et JNI. Cette exception n’autorise aucun déréférencement de pointeur brut, aucun autre attribut unsafe, aucun bloc `unsafe` et aucune fonction `unsafe`. - **RUST-BASE-005** — Tout fichier Rust possède les en-têtes `// file: ...` et `// version: N`. - **RUST-DEP-001** — Une dépendance tierce partagée déclare uniquement sa contrainte de version canonique sous `[workspace.dependencies]`, sauf exception normative explicitement documentée. - **RUST-DEP-002** — Les features d'une dépendance tierce sont activées dans le `Cargo.toml` de la crate qui en a réellement besoin, via `workspace = true`; elles ne sont pas activées globalement au workspace par commodité. diff --git a/docs/testing/002-BETA_VALIDATION_MATRIX.md b/docs/testing/002-BETA_VALIDATION_MATRIX.md index 402ee4b..cce5092 100644 --- a/docs/testing/002-BETA_VALIDATION_MATRIX.md +++ b/docs/testing/002-BETA_VALIDATION_MATRIX.md @@ -1,5 +1,5 @@ - + # Matrice de validation beta 0.1.0 @@ -128,3 +128,21 @@ cargo test --workspace --all-targets --all-features ``` Cette suite complète ne devient pas une gate de chaque futur correctif beta. + +## Logcat Android + +Le buffer doit être vidé avant le lancement du jeu, pas après : + +```bash +adb -s logcat -c +adb -s shell am start -n / +adb -s logcat | grep -iE 'games\.sasedev|com\.sasedev\.games|SDL|AndroidRuntime|FATAL|panic' +``` + +Android utilise un subscriber `tracing` dédié à logcat. + +## Back Android 16 + +Les applications ciblent API 36. Le Back système est donc traité via `OnBackInvokedCallback` sur API 33+ puis converti en `QuitSource::PlatformBack`. + +Le fallback `onBackPressed()` reste réservé aux versions Android antérieures. diff --git a/scripts/audit_rust_general_rules.py b/scripts/audit_rust_general_rules.py index de88080..2d81146 100755 --- a/scripts/audit_rust_general_rules.py +++ b/scripts/audit_rust_general_rules.py @@ -522,6 +522,7 @@ def audit_file(root: pathlib.Path, path: pathlib.Path) -> list[Violation]: if ffi_export_exception: required_export_attributes = ( '#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeContractVersion")]', + '#[unsafe(export_name = "Java_com_sasedev_games_common_NativeBridge_nativeRequestPlatformBack")]', '#[unsafe(export_name = "SDL_main")]', ) for export_attribute in required_export_attributes: