diff --git a/Cargo.lock b/Cargo.lock
index c3a427ac..4a0e4a52 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -121,6 +121,18 @@ dependencies = [
"libc",
]
+[[package]]
+name = "alsa"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c88dbbce13b232b26250e1e2e6ac18b6a891a646b8148285036ebce260ac5c3"
+dependencies = [
+ "alsa-sys",
+ "bitflags 2.11.1",
+ "cfg-if",
+ "libc",
+]
+
[[package]]
name = "alsa-sys"
version = "0.3.1"
@@ -508,6 +520,7 @@ dependencies = [
"bytemuck",
"coreaudio-rs 0.13.0",
"cosmic-text",
+ "cpal 0.17.1",
"crossbeam-channel",
"fontdb",
"half",
@@ -862,7 +875,7 @@ dependencies = [
"bevy_reflect",
"bevy_transform",
"coreaudio-sys",
- "cpal",
+ "cpal 0.15.3",
"rodio",
"tracing",
]
@@ -2641,14 +2654,14 @@ version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779"
dependencies = [
- "alsa",
+ "alsa 0.9.1",
"core-foundation-sys",
"coreaudio-rs 0.11.3",
"dasp_sample",
"jni 0.21.1",
"js-sys",
"libc",
- "mach2",
+ "mach2 0.4.3",
"ndk 0.8.0",
"ndk-context",
"oboe",
@@ -2658,6 +2671,36 @@ dependencies = [
"windows 0.54.0",
]
+[[package]]
+name = "cpal"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b1f9c7312f19fc2fa12fd7acaf38de54e8320ba10d1a02dcbe21038def51ccb"
+dependencies = [
+ "alsa 0.10.0",
+ "coreaudio-rs 0.13.0",
+ "dasp_sample",
+ "jni 0.21.1",
+ "js-sys",
+ "libc",
+ "mach2 0.5.0",
+ "ndk 0.9.0",
+ "ndk-context",
+ "num-derive",
+ "num-traits",
+ "objc2 0.6.4",
+ "objc2-audio-toolbox",
+ "objc2-avf-audio",
+ "objc2-core-audio",
+ "objc2-core-audio-types",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.2",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "windows 0.62.2",
+]
+
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -4247,6 +4290,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "mach2"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "malloc_buf"
version = "0.0.6"
@@ -4672,6 +4724,16 @@ dependencies = [
"objc2-foundation 0.3.2",
]
+[[package]]
+name = "objc2-avf-audio"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13a380031deed8e99db00065c45937da434ca987c034e13b87e4441f9e4090be"
+dependencies = [
+ "objc2 0.6.4",
+ "objc2-foundation 0.3.2",
+]
+
[[package]]
name = "objc2-cloud-kit"
version = "0.2.2"
@@ -4706,6 +4768,7 @@ dependencies = [
"objc2 0.6.4",
"objc2-core-audio-types",
"objc2-core-foundation",
+ "objc2-foundation 0.3.2",
]
[[package]]
@@ -4737,7 +4800,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
"bitflags 2.11.1",
+ "block2 0.6.2",
"dispatch2",
+ "libc",
"objc2 0.6.4",
]
@@ -4804,6 +4869,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
dependencies = [
"bitflags 2.11.1",
+ "block2 0.6.2",
+ "libc",
"objc2 0.6.4",
"objc2-core-foundation",
]
@@ -5639,7 +5706,7 @@ version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ceb6607dd738c99bc8cb28eff249b7cd5c8ec88b9db96c0608c1480d140fb1"
dependencies = [
- "cpal",
+ "cpal 0.15.3",
"lewton",
]
diff --git a/crates/benilla-app/Cargo.toml b/crates/benilla-app/Cargo.toml
index ab73e9c5..dba96a5c 100644
--- a/crates/benilla-app/Cargo.toml
+++ b/crates/benilla-app/Cargo.toml
@@ -99,9 +99,11 @@ cosmic-text = "0.16"
# `fontdb` in the graph.
fontdb = "0.23"
# kira renders the mix; the device it plays through is OURS (`sound::output`, decision 1857) —
-# kira's own cpal backend is not enabled, and cpal is out of the graph on our path with it: on
-# macOS it could neither report a lost default device nor rebuild on one, and it discarded the
-# IO-cycle timestamp the deadline meters need.
+# kira's own `cpal` backend is not enabled on any target. On macOS that took cpal out of the graph
+# entirely: it could neither report a lost default device nor rebuild on one, and it discarded the
+# IO-cycle timestamp the deadline meters need. Off macOS cpal is back (1920) but as OUR device
+# layer under the same backend, not as kira's — the ring, the render thread and the meters are the
+# same code on every platform.
kira = { version = "0.12", default-features = false, features = ["mp3", "wav", "pcm", "adpcm"] }
# The realtime-audio allocation tripwire (decision 1857): `assert_no_alloc(|| …)` around the output
# IO callback and the render pass aborts a DEBUG build on any allocation inside them, so a
@@ -167,3 +169,15 @@ objc2-app-kit = { version = "0.2", features = [
"NSEvent",
] }
objc2-foundation = { version = "0.2", features = ["NSArray", "NSData", "NSGeometry"] }
+
+# Everywhere else: the same device layer over cpal (decision 1920, `sound::output::cpal`). 1857
+# owns the mix-ahead ring, the render thread and the meters on every platform; only the last
+# hop — find the default device, open a stream on it, hand back one buffer per cycle — is
+# platform code, and off macOS that hop is ALSA/PipeWire, WASAPI or the host's own API. cpal is
+# the layer that already speaks all of them; on macOS it is still not in the graph, because
+# there its defects (a no-op error callback on the default device, a pinned `DefaultOutput`
+# unit, the discarded IO-cycle timestamp) are exactly what 1857 went direct to escape. On an
+# unsupported target cpal compiles to its Null host, which reports no device — the same silent
+# posture the old `stub.rs` had, without a second file to rot.
+[target.'cfg(not(target_os = "macos"))'.dependencies]
+cpal = "0.17"
diff --git a/crates/benilla-app/assets/ui/QuestFrame.xml b/crates/benilla-app/assets/ui/QuestFrame.xml
index ffaeb690..ca96cf9e 100644
--- a/crates/benilla-app/assets/ui/QuestFrame.xml
+++ b/crates/benilla-app/assets/ui/QuestFrame.xml
@@ -206,7 +206,7 @@
local t = { "QuestDescription", "QuestObjectiveText", "QuestDetailRewardTitleText",
"QuestDetailItemChooseText", "QuestDetailItemReceiveText" }
for i = 1, MAX_NUM_ITEMS do table.insert(t, "QuestDetailItem" .. i) end
- for i = 1, 3 do table.insert(t, "QuestDetailMoneyCoin" .. i) end
+ table.insert(t, "QuestDetailMoneyFrame")
return t
end)(),
["QuestProgressScrollFrame"] = (function()
@@ -220,7 +220,7 @@
local t = { "QuestRewardText", "QuestRewardRewardTitleText",
"QuestRewardItemChooseText", "QuestRewardItemReceiveText" }
for i = 1, MAX_NUM_ITEMS do table.insert(t, "QuestRewardItem" .. i) end
- for i = 1, 3 do table.insert(t, "QuestRewardMoneyCoin" .. i) end
+ table.insert(t, "QuestRewardMoneyFrame")
return t
end)(),
}
@@ -358,6 +358,16 @@
HideUIPanel(getglobal("QuestFrame"))
end
+ -- The reference's own `QuestMoneyFrame_OnLoad` (QuestFrame.lua:306-309), which lives in
+ -- THIS file in the reference too — so it is home, not a squatter. Its whole body is these
+ -- two calls. Needed because 1928 put the two reward panels on `MoneyFrameTemplate`, whose
+ -- instances call it from their OnLoad exactly as the reference's do. Both callees are our
+ -- `MoneyFrame.xml`'s, which the manifest loads at entry 141 — far above this file.
+ function QuestMoneyFrame_OnLoad()
+ MoneyFrame_OnLoad()
+ MoneyFrame_SetType("STATIC")
+ end
+
-- ── The shared reward grid — `QuestFrameItems_Update(questState)`, the REFERENCE'S OWN
-- NAME and the reference's own algorithm (QuestFrame.lua:311-522), ported line-for-line.
--
@@ -454,10 +464,17 @@
end
end
+ -- The reference's own tail (QuestFrame.lua:350-356), on the reference's own widget.
+ -- `QuestFrame_SetAsLastShown` is deliberately NOT called: it anchors a SPACER FRAME
+ -- that this client does not have — our panels size their scroll child from
+ -- `BENILLA_QUEST_PANEL_WIDGETS` instead (see 1928). The money half converges here; the
+ -- sizing model stays ours until this window migrates.
+ local moneyFrame = getglobal(questState .. "MoneyFrame")
if money and money > 0 then
- BenillaMoney_Set(questState .. "Money", money, true)
+ moneyFrame:Show()
+ MoneyFrame_Update(questState .. "MoneyFrame", money)
else
- BenillaMoney_Clear(questState .. "Money")
+ moneyFrame:Hide()
end
end
@@ -1154,9 +1171,7 @@
-
-
-
+ QuestMoneyFrame_OnLoad()
@@ -1292,9 +1307,7 @@
-
-
-
+ QuestMoneyFrame_OnLoad()