diff --git a/native/watchos/src/BloomWatchApp.swift b/native/watchos/src/BloomWatchApp.swift index 8d29239..5542aeb 100644 --- a/native/watchos/src/BloomWatchApp.swift +++ b/native/watchos/src/BloomWatchApp.swift @@ -22,6 +22,7 @@ import ImageIO @_silgen_name("bloom_watchos_touch") func bloom_watchos_touch(_ idx: Int64, _ x: Double, _ y: Double, _ active: Int64) @_silgen_name("bloom_watchos_set_screen") func bloom_watchos_set_screen(_ w: Double, _ h: Double) @_silgen_name("bloom_watchos_set_bundle_path") func bloom_watchos_set_bundle_path(_ path: UnsafePointer) +@_silgen_name("bloom_watchos_set_language") func bloom_watchos_set_language(_ code: Double) // Outbound (Rust → Swift) — draw list snapshot @_silgen_name("bloom_watchos_frame_count") func bloom_watchos_frame_count() -> UInt64 @@ -238,6 +239,15 @@ struct BloomWatchApp: App { if let res = Bundle.main.resourcePath { res.withCString { bloom_watchos_set_bundle_path($0) } } + // Report the user's preferred language before the game thread starts: + // packed 2-letter primary subtag (c0*256 + c1, lowercased; script + // subtags dropped, zh-Hans -> "zh") per the bloom_get_language contract. + let lang = Locale.preferredLanguages.first ?? "en" + let primary = lang.split(separator: "-").first.map(String.init) ?? "en" + let scalars = Array(primary.lowercased().unicodeScalars) + if scalars.count >= 2, scalars[0].isASCII, scalars[1].isASCII { + bloom_watchos_set_language(Double(scalars[0].value * 256 + scalars[1].value)) + } // Spawn the game thread. _perry_user_main blocks in runGame's while // loop, so this must not run on the main thread. let t = Thread(block: { _perry_user_main() }) diff --git a/native/watchos/src/lib.rs b/native/watchos/src/lib.rs index 04d2993..3f65b04 100644 --- a/native/watchos/src/lib.rs +++ b/native/watchos/src/lib.rs @@ -102,6 +102,7 @@ const MAX_TOUCH: usize = 4; struct WatchState { crown_bits: AtomicU64, // f64 bits + language_code: AtomicU64, // packed 2-letter code (c0*256+c1), set by Swift at launch touch_x: [AtomicU64; MAX_TOUCH], touch_y: [AtomicU64; MAX_TOUCH], touch_active: [AtomicU64; MAX_TOUCH], @@ -117,6 +118,7 @@ fn state() -> &'static WatchState { static S: OnceLock = OnceLock::new(); S.get_or_init(|| WatchState { crown_bits: AtomicU64::new(0), + language_code: AtomicU64::new(25966), // "en" until Swift reports the locale touch_x: std::array::from_fn(|_| AtomicU64::new(0)), touch_y: std::array::from_fn(|_| AtomicU64::new(0)), touch_active: std::array::from_fn(|_| AtomicU64::new(0)), @@ -162,6 +164,17 @@ fn consume_crown() -> f64 { #[no_mangle] pub extern "C" fn bloom_watchos_crown_delta(delta: f64) { add_crown(delta); } +/// Swift reports the user's preferred language at launch as a packed +/// 2-letter ISO-639 primary subtag (c0*256 + c1, lowercased), matching +/// the bloom_get_language contract on the other platforms. +#[no_mangle] +pub extern "C" fn bloom_watchos_set_language(code: f64) { + let c = code as u64; + if c > 0 { + state().language_code.store(c, Ordering::Release); + } +} + #[no_mangle] pub extern "C" fn bloom_watchos_touch(index: i64, x: f64, y: f64, active: i64) { let s = state(); @@ -252,7 +265,9 @@ pub extern "C" fn bloom_get_platform() -> f64 { 8.0 } /// Preferred OS language packed as `c0*256+c1`. TODO: real per-OS detection; returns "en" for now. #[no_mangle] -pub extern "C" fn bloom_get_language() -> f64 { 25966.0 } +pub extern "C" fn bloom_get_language() -> f64 { + state().language_code.load(Ordering::Acquire) as f64 +} #[no_mangle] pub extern "C" fn bloom_get_crown_rotation() -> f64 { consume_crown() }