diff --git a/Cargo.lock b/Cargo.lock index 03f97493..c3a427ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,18 +121,6 @@ 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" @@ -308,6 +296,12 @@ dependencies = [ "libloading", ] +[[package]] +name = "assert_no_alloc" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ca83137a482d61d916ceb1eba52a684f98004f18e0cafea230fe5579c178a3" + [[package]] name = "assert_type_match" version = "0.1.1" @@ -501,6 +495,7 @@ version = "0.1.0" dependencies = [ "anyhow", "arboard", + "assert_no_alloc", "avian3d", "benilla-assets", "benilla-formats", @@ -511,8 +506,8 @@ dependencies = [ "bevy", "bevy_egui", "bytemuck", + "coreaudio-rs 0.13.0", "cosmic-text", - "cpal 0.17.1", "crossbeam-channel", "fontdb", "half", @@ -523,6 +518,8 @@ dependencies = [ "mint", "objc2 0.5.2", "objc2-app-kit 0.2.2", + "objc2-core-audio", + "objc2-core-audio-types", "objc2-foundation 0.2.2", "rand 0.8.6", "raw-window-handle", @@ -865,7 +862,7 @@ dependencies = [ "bevy_reflect", "bevy_transform", "coreaudio-sys", - "cpal 0.15.3", + "cpal", "rodio", "tracing", ] @@ -2644,14 +2641,14 @@ version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" dependencies = [ - "alsa 0.9.1", + "alsa", "core-foundation-sys", "coreaudio-rs 0.11.3", "dasp_sample", "jni 0.21.1", "js-sys", "libc", - "mach2 0.4.3", + "mach2", "ndk 0.8.0", "ndk-context", "oboe", @@ -2661,36 +2658,6 @@ 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" @@ -4111,7 +4078,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065632c16b39f4c00bc5514de0698474d1b6c36120743a154c78ca27ffc1ffe1" dependencies = [ "atomic-arena", - "cpal 0.17.1", "glam 0.33.2", "mint", "pastey", @@ -4281,15 +4247,6 @@ 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" @@ -4715,16 +4672,6 @@ 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" @@ -4759,7 +4706,6 @@ dependencies = [ "objc2 0.6.4", "objc2-core-audio-types", "objc2-core-foundation", - "objc2-foundation 0.3.2", ] [[package]] @@ -4791,9 +4737,7 @@ 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", ] @@ -4860,8 +4804,6 @@ 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", ] @@ -5697,7 +5639,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7ceb6607dd738c99bc8cb28eff249b7cd5c8ec88b9db96c0608c1480d140fb1" dependencies = [ - "cpal 0.15.3", + "cpal", "lewton", ] diff --git a/crates/benilla-app/Cargo.toml b/crates/benilla-app/Cargo.toml index 79482032..ab73e9c5 100644 --- a/crates/benilla-app/Cargo.toml +++ b/crates/benilla-app/Cargo.toml @@ -22,7 +22,7 @@ default = ["dev"] # re-exported here (1175). Their `dev` is default-on too, so without the round trip a # `--no-default-features` build gets the feature back through unification — and the binary # carries the build machine's source-tree path after all. Same trap as the shim's, one level down. -dev = ["benilla-assets/dev", "benilla-formats/dev", "benilla-world/dev", "dep:bevy_egui", "dep:libc"] +dev = ["benilla-assets/dev", "benilla-formats/dev", "benilla-world/dev", "dep:bevy_egui"] # Opt-in Tracy profiling: `cargo run --features tracy` streams per-system `info_span!` markers (on the # hot systems) + render-pass CPU timings to a Tracy capture (decision 0026 — the dev/player seam; # a `docs/howto/running.md` this once cited never landed). @@ -61,9 +61,10 @@ include_dir = "0.7" # `getrusage` — whole-process CPU time (all threads), the perf probes' `cpu_ms`/`cpu_pct` metric # (`perf::process_cpu_secs`). Wall-clock fps is unusable as a regression instrument on a machine # running parallel sessions' builds; CPU-per-frame is, and it is also the number the Mac report -# is about ("250 % CPU at 59 fps"). Already in the tree transitively; declared so the call site is -# an honest dependency. -libc = { version = "0.2", optional = true } +# is about ("250 % CPU at 59 fps"). Was dev-only; since 1857 the output backend needs it in every +# build too (`mach_absolute_time`, the host clock its meters read; `thread_policy_set`, the render +# thread's realtime policy). +libc = "0.2" # Resample varying-size layer BLPs to a uniform size for the terrain texture array; `png` so we can # encode the cursor image into data an NSImage can load (macOS hardware cursor). image = { version = "0.25", default-features = false, features = ["png"] } @@ -97,11 +98,16 @@ cosmic-text = "0.16" # public-in-crate types). Pinned to the exact version `cosmic-text` 0.16 depends on so there's one # `fontdb` in the graph. fontdb = "0.23" -# kira's own backend dependency, named directly so the mixer can hand cpal an explicit device buffer -# size instead of accepting `BufferSize::Default` (decision 1026). `CpalBackendSettings`' fields ARE -# cpal types, so this must stay the exact version kira resolves — one cpal in the graph. -cpal = "0.17" -kira = { version = "0.12", default-features = false, features = ["cpal", "mp3", "wav", "pcm", "adpcm"] } +# 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 = { 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 +# realtime-safety regression fails a test or a smoke instead of reaching an ear. Release builds +# compile it out (`disable_release`, the crate's default) and carry no allocator wrapper. +assert_no_alloc = "1.1" # kira's own decoder, named directly for one trait: the mixer feeds streams through its own # `MediaSource` so kira's per-stream decode thread runs OUR code and can promote its QoS (decision # 1109 — a bare-spawned decode thread starves under the world-entry burst and zero-fills the mix). @@ -141,6 +147,13 @@ smithay-clipboard = "0.7" # hide revert to the arrow); we bypass it with NSCursor + NSWindow::disableCursorRects. See # docs/knowledge/camera-control.md. [target.'cfg(target_os = "macos")'.dependencies] +# The audio device layer (decision 1857, `sound::output::coreaudio`): the HAL output unit through +# coreaudio-rs's `AudioUnit`, and the AudioObject property/listener API through the objc2 CoreAudio +# bindings — the two crates cpal itself stood on, now used directly. The versions are the ones the +# graph already carries; the feature sets are the ones cpal proved to compile against. +coreaudio-rs = { version = "0.13", default-features = false, features = ["audio_unit", "core_audio"] } +objc2-core-audio = { version = "0.3", default-features = false, features = ["std", "AudioHardware"] } +objc2-core-audio-types = { version = "0.3", default-features = false, features = ["std", "CoreAudioBaseTypes"] } objc2 = "0.5" objc2-app-kit = { version = "0.2", features = [ "NSApplication", diff --git a/crates/benilla-app/assets/ui/KeyBindingsPage.xml b/crates/benilla-app/assets/ui/KeyBindingsPage.xml index 438f1c11..bb5fe601 100644 --- a/crates/benilla-app/assets/ui/KeyBindingsPage.xml +++ b/crates/benilla-app/assets/ui/KeyBindingsPage.xml @@ -320,6 +320,10 @@ end FauxScrollFrame_Update(scroll, table.getn(page.items), KEYBINDINGS_DISPLAYED, KEYBINDINGS_ROW_HEIGHT) + -- The trough is OURS — the reference has no trough at all, so nothing in its kit shows + -- one. The owner pairs it with the bar the reference's `_Update` just decided about + -- (1860); `BenillaScrollBar_SetShown` used to do it and is off this path now. + BenillaScrollTrough_Follow(scroll:GetName()) KeyBindings_UpdateUnbindKey() end diff --git a/crates/benilla-app/assets/ui/OptionsFrame.xml b/crates/benilla-app/assets/ui/OptionsFrame.xml index 0fb88a6d..34096572 100644 --- a/crates/benilla-app/assets/ui/OptionsFrame.xml +++ b/crates/benilla-app/assets/ui/OptionsFrame.xml @@ -3471,10 +3471,34 @@ - - ScrollFrame_OnLoad() - this.updateFunc = KeyBindingsPage_Update - + ScrollFrame_OnLoad() + + FauxScrollFrame_OnVerticalScroll(KEYBINDINGS_ROW_HEIGHT, KeyBindingsPage_Update) + + + if not KeyBindingsPage.selected then + ScrollFrameTemplate_OnMouseWheel(arg1) + end + diff --git a/crates/benilla-app/assets/ui/ReputationFrame.xml b/crates/benilla-app/assets/ui/ReputationFrame.xml index 406e6c3b..2d5196b7 100644 --- a/crates/benilla-app/assets/ui/ReputationFrame.xml +++ b/crates/benilla-app/assets/ui/ReputationFrame.xml @@ -329,7 +329,11 @@ -- `offset * valueStep`, so this SetValue writes the value the Slider already holds and -- its change-gate (decision 0250) drops it before it could re-enter this function. -- Kept because it is the reference's line, not because it does anything. - if not FauxScrollFrame_Update(scrollFrame, numFactions, NUM_FACTIONS_DISPLAYED, REPUTATIONFRAME_FACTIONHEIGHT) then + local showBar = FauxScrollFrame_Update(scrollFrame, numFactions, NUM_FACTIONS_DISPLAYED, REPUTATIONFRAME_FACTIONHEIGHT) + -- Our trough is not the reference's business: it follows the bar the ref just decided + -- about (1860). + BenillaScrollTrough_Follow("ReputationListScrollFrame") + if not showBar then getglobal("ReputationListScrollFrameScrollBar"):SetValue(0) end local factionOffset = FauxScrollFrame_GetOffset(scrollFrame) @@ -435,9 +439,8 @@ function ReputationFrame_OnLoad() local frame = this frame:RegisterEvent("UPDATE_FACTION") - -- The faux kit's one wiring requirement: the owner names its own repaint so a drag of - -- the scroll bar re-binds the rows (ScrollTemplates.xml's `frame.updateFunc` contract). - getglobal("ReputationListScrollFrame").updateFunc = ReputationFrame_Update + -- The repaint is wired in the scroll frame's own `` below, which is + -- where the reference's kit puts it (1860). end function ReputationFrame_OnShow() @@ -744,11 +747,19 @@ + page. Its `` is what re-binds the rows (1860). --> - + + + FauxScrollFrame_OnVerticalScroll(REPUTATIONFRAME_FACTIONHEIGHT, ReputationFrame_Update) + + - - - - - - - - - - - ScrollFrame_OnLoad() - - + diff --git a/crates/benilla-app/assets/ui/TrainerFrame.xml b/crates/benilla-app/assets/ui/TrainerFrame.xml index 75225504..b569a6f5 100644 --- a/crates/benilla-app/assets/ui/TrainerFrame.xml +++ b/crates/benilla-app/assets/ui/TrainerFrame.xml @@ -492,8 +492,8 @@ frame:RegisterEvent("TRAINER_UPDATE") frame:RegisterEvent("TRAINER_CLOSED") frame:RegisterEvent("PLAYER_MONEY") - -- The faux-scroll frame repaints the list through this window's Update. - getglobal("BenillaTrainerListScrollFrame").updateFunc = BenillaTrainerFrame_Update + -- The list repaint is wired in the scroll frame's own `` below, the + -- reference's way; `updateFunc` was our kit's field and the reference has none (1860). end -- Push the three saved globals into the engine — the ref's ClassTrainerFrame @@ -508,6 +508,14 @@ SetTrainerServiceTypeFilter("used", TRAINER_FILTER_USED) end + -- The scroll frame's `` lives in its own chunk and cannot see this + -- file's `local BENILLA_TRAINER_ROW_HEIGHT`, so the pitch is handed over here rather than + -- written as a literal in the XML (1860). The reference has the same shape with globals: + -- `QUESTLOG_QUEST_HEIGHT` and friends are file-scope globals in its own FrameXML. + function BenillaTrainerList_OnVerticalScroll() + FauxScrollFrame_OnVerticalScroll(BENILLA_TRAINER_ROW_HEIGHT, BenillaTrainerFrame_Update) + end + function BenillaTrainerFrame_SetTitle(name) if name and name ~= "" then getglobal("BenillaTrainerTitleText"):SetText(name) @@ -518,6 +526,14 @@ function BenillaTrainerFrame_OnEvent(frame) if event == "TRAINER_SHOW" then + -- SHOW FIRST, then populate — the reference's own order, and not a style choice. + -- `FauxScrollFrame_Update` (the chain's since 1837) gates everything that sets the + -- bar's range on `frame:IsVisible()`, so a list repainted before its window is up + -- leaves `SetMinMaxValues` unset and both arrows dead. The reference relies on this + -- order hard enough to test it: `MerchantFrame.lua:22-27` does `ShowUIPanel(this)` + -- and then treats `not this:IsVisible()` as the panel system refusing, and closes. + -- Decision 1860. + ShowUIPanel(frame) -- Fresh session: top of the list, a first selection (ref ClassTrainerFrame_Show). BenillaTrainerFrame_ApplyFilter() BenillaTrainerFrame_SetTitle(arg1) @@ -526,7 +542,6 @@ selectFirstService() end BenillaTrainerFrame_Update() - ShowUIPanel(frame) elseif event == "TRAINER_UPDATE" then BenillaTrainerFrame_SetTitle(arg1) -- Re-anchor the selection if it fell off the (possibly shorter) list. @@ -793,13 +808,25 @@ - + + + BenillaTrainerList_OnVerticalScroll() + - +