Skip to content

Futureboard Studio 2026.8.10 Beta 1.1

Latest

Choose a tag to compare

@arizkami arizkami released this 10 Aug 08:36
· 24 commits to main since this release

Futureboard Studio 2026.8.10 Beta 1.1

Point release — 10 August 2026

A one-day follow-up to 2026.8.9 Beta 1. Nineteen commits, mostly in three areas: built-in plug-in editors (MixStation and EQuz8 rebuilt, Windows editors now render on the GPU, and mouse input into them fixed), platform input (macOS MIDI, Linux ALSA capture), and release plumbing that the previous hotfix exposed as broken.

The updater fix matters if you are on 2026.8.9 Beta 1: that build could not see its own channel's releases. It can now.


Highlights

  • Windows plug-in editors render on the GPU — CEF's accelerated off-screen path copies D3D11 shared textures straight into the GPUI atlas, with no CPU readback and no per-frame image allocation
  • Knob drags no longer stick, and trackpads scroll — a drag that left the editor rect was cancelled mid-gesture, and a sub-pixel trackpad swipe rounded to nothing forever
  • macOS MIDI input works — physical keyboards produced nothing, and devices did not appear in the scan at all. Two independent macOS-only defects, both fixed
  • Linux audio capture — ALSA input uses the same multi-period ring geometry as playback, and device lists are readable instead of a wall of dmix/surround aliases
  • MixStation rebuilt as a signal-path rack: drag-reorderable modules, per-stage telemetry, per-module output trim, host analyser overlay
  • EQuz8 rebuilt on the MixStation UI stack — Pro-Q-style graph, Serum-style knobs, searchable preset popover, clearer bypass state
  • The updater can see beta releases again — a moving tag made every release unparseable, so the 8.9 hotfix was invisible to installed builds
  • Signed APAK packages with key verification, staged into distributable builds
  • Accessibility semantics across menu bar, title bar, sidebar, sliders, text inputs, and the welcome screen
  • Two inserts of the same plug-in are now distinguishable in editors that bind by instance

Built-in plug-in editors

Accelerated off-screen rendering on Windows

Built-in editors now render windowless on every platform; the Windows native child window is gone. On Windows, CEF's OnAcceleratedPaint hands the host a callback-scoped D3D11 shared texture, and that texture is copied straight into a GPUI-owned atlas tile — no CPU readback, no per-frame RenderImage allocation, no atlas churn from a new tile every frame.

  • SphereWebView gains an OsrAcceleratedFrameSink trait, accelerated surface construction, popup-plane state propagation, and a failure signal. CEF cannot switch an existing browser from accelerated to OnPaint, so a copy failure — or a GPUI device-loss that cleared the atlas — recreates the browser in software mode rather than leaving a blank page.
  • GPUI gains PlatformAtlas::copy_d3d11_shared_texture, D3D11ExternalImage, and Window::create_d3d11_external_image / paint_d3d11_external_image.
  • The DirectX atlas opens the shared resource, validates format, sample count and source bounds, copies with CopySubresourceRegion, and flushes while CEF's handle is still valid.
  • Fixed an atlas-space leak: tile removal decremented the reference count without ever deallocating the etagere slot, so the atlas could not reuse the space.

Linux, macOS, and the Windows fallback keep the software OnPaint framebuffer path.

Mouse input into off-screen editors

Three defects in the GPUI-to-CEF input translation, all of which get worse now that every platform renders windowless.

  • A drag that left the editor rect was cancelled. A move outside the browser rect was reported to the page as a leave, and Blink treats that as pointercancel on a captured pointer — so dragging a knob past the edge of the editor ended the gesture and left the control stuck. A move is now only flagged as leaving when no button is held, drag moves are never suppressed, and hover state is settled on release instead.
  • A precision trackpad never scrolled. Each wheel delta was rounded on its own, and a trackpad reports a fraction of a line per event, so a slow two-finger swipe produced an endless stream of zero deltas. The sub-pixel remainder is now carried between events and spent once it reaches a whole pixel; a direction change drops the stale carry so a reversal is not swallowed. Deltas are clamped, because Windows reports WHEEL_PAGESCROLL as u32::MAX lines, which would saturate the i32 handed to CEF.
  • Clicks and wheels landed wherever CEF last thought the cursor was. The browser's own pointer position decides what the page hit-tests and scrolls, so the position is now sent before a button press and before a wheel event — the same order a real platform delivers.

Also: SCROLL_LINE_HEIGHT 20 → 40 logical pixels, matching Chromium's kPixelsPerLineStep, so one notch moves an embedded editor as far as it moves the same page in a browser window. click_count is clamped to CEF's 1..=3 contract; GPUI keeps counting past 3 while a fast click repeats in place, and Blink has no defined behaviour above that.

MixStation

Rebuilt as a signal-path rack over the real parameter bridge — drag-reorderable rows, portalled popovers, a factory preset browser.

  • Every curve is drawn from the authoritative parameter values through response.ts, a mirror of dsp.rs, so the graphics describe the processing that actually runs. Locked to the Rust suite's own assertions by test.
  • The host analyser overlay now sits behind the EQ. The spectrum channel and its ~30 Hz publish already existed for every built-in; MixStation's editor simply never listened.
  • DSP: per-module output trim (6 wire params, PARAM_COUNT 33 → 39), keyed by module rather than rack slot, so reordering carries a stage's trim with it. Legacy state loads at unity via serde defaults.
  • Shared bridge BRIDGE_LAYOUT_VERSION 8 → 9: per-stage telemetry added to BuiltinMeterFrame. Built-ins with no user-ordered rack publish zeros, and the native side omits the field entirely, so the meter payload is unchanged for every other editor.
  • Instance binding: the display metadata native already sends is now consumed, so two inserts of the same plug-in are distinguishable. The approved binding is mirrored into a hash route, which never binds on its own — a hash edit or back/forward posts requestSelectInstance and native decides. Editor-local view state resets on rebind.

EQuz8

Rewritten UI and then aligned with the MixStation editor stack: Mona Sans, Tailwind tokens, Phosphor icons, motion, BypassSwitch, and a searchable portalled PresetMenu. Full d3 for scales, monotone curves and area fills; anime.js for intro, band-select pulse, rack flash, knob snaps and bypass tweening. Embedded as a single-file CEF bundle via vite-plugin-singlefile. Parameter bridge and DSP behaviour unchanged.

A powered-off stage is now clearly desaturated and dimmed in CSS, with anime.js limited to a brief brightness flash so it cannot override the steady state. React module resolution in the editor build is fixed.

Transport keys

Space drives play/pause from inside a built-in editor again: the OSR shell and surface claim transport keys through more XKB/IM Space aliases, and MixStation falls back through globalCommand when native capture misses.


Platform input

macOS MIDI

Physical MIDI keyboards did not work on macOS, and devices did not appear in the scan at all. Two independent defects, both macOS-only.

Discovery. shared_client() used std::sync::Once, and call_once marks the Once completed even when the closure returns early. One failed MIDIClientCreate latched CLIENT at 0 for the process, and without a client CoreMIDI reports zero sources no matter what is plugged in. Every recovery path above it was dead code — the resilient scan retries, the two-second startup rescan, the Preferences refresh button, the 500 ms device sync. The first attempt runs on a background scan thread during splash, exactly when MIDIServer is most likely unreachable, so the failure that got latched was the transient one the retries existed to absorb. Creation is now mutex-guarded and retried until it succeeds.

Delivery. MIDIPacket is declared under #pragma pack(4) on every Apple target, so the natural #[repr(C)] layout of { u64, u16, [u8; 256] } was wrong: 8-byte alignment pushed the first packet of a MIDIPacketList from offset 4 to 8. Every field read four bytes late — for a Note On, length landed on the velocity byte and the payload came from past the end of the message, so decoding returned None and the note was dropped in the callback. Offsets now come from the header and are pinned by offset_of! assertions that fail the build if the layout drifts.

Also in the input path:

  • Coalesced packets are split into messages. A CoreMIDI packet is a byte stream, so a chord shares one packet and only the first message was decoded. The splitter handles running status across packet boundaries, interleaved System Real-Time bytes, System Common, and SysEx.
  • An endpoint is never dropped for unreadable metadata. Naming falls back kMIDIPropertyDisplayNamekMIDIPropertyName → manufacturer/model → a synthetic name from the unique ID. Long names no longer fail: CFStringGetCString is all-or-nothing and the 512-byte buffer silently dropped the device.
  • One input port off the shared client instead of a client per device, with ordered teardown.
  • Running status carried in an AtomicU8, since the box is owned by the control thread and touched by CoreMIDI's read thread.
  • Hot-plug notifications only fire on the run loop of the creating thread, which is often a background scan thread whose run loop never runs — endpoint-count comparison is the safety net.
  • The outgoing packet-list buffer is aligned; it was a byte-aligned [u8; 512].

Diagnostics: counters at the input boundary separate "backend delivered nothing" from "bytes arrived but did not decode" from "decoded but no track claimed it", so an empty scan names its own layer without an env var. FUTUREBOARD_MIDI_INPUT_DEBUG and FUTUREBOARD_MIDI_SETTINGS_DEBUG add tracing.

Windows and Linux are untouched past the diagnostic counters.

Not verified on hardware. No Mac or MIDI keyboard was available. The discovery cause is proven by inspection (a latched Once cannot retry) and the decode cause by compiler assertion and a numeric replay of both traversals, but neither has been observed working end to end. 18 tests were added — 13 that run everywhere, 5 macOS-only.

Linux ALSA

Capture now uses the same multi-period ring geometry and realtime promotion as playback, input sample rate matches the open output where possible, and ALSA endpoints are presented with short card-based labels while dmix/surround plug-in aliases are filtered out.


Packaging and release plumbing

Signed APAK packages

APAK gains signing and key verification across SpherePackageInstaller and the installer CLI/GUI, with the template and example key material updated. The APAK tools are now staged into distributable packages by xtask, with build, staging and validation all aware of them.

The updater could not see beta releases

The updater read a release's version from its tag alone, so a moving tag sank the whole release: Version::parse("beta") fails, release_version returned None, and release_matches_channel dropped it for every channel. 2026.8.9 Beta 1 was published on beta, so installed builds could not see the hotfix at all. nightly escaped only through a special case that scanned the release name.

The version now falls back to the asset this machine would actually install, so the version offered is the version that arrives, and a leftover bundle from an older build cannot advertise its own. Assets are preferred over the release name — this release's predecessor was titled "Futureboard Studio 2026.8.9 Beta 1", whose only parseable token is 2026.8.9, which sorts above 2026.8.9-beta1 and would have offered users a permanent update to their own build. Platform tails are listed rather than inferred, because semver accepts hyphens inside a prerelease identifier.

This cannot reach anything already installed on 2026.8.8 Beta 1, which ships the tag-only logic. Only publishing at a version tag does that.

Release workflow

  • The cron is gone. It fired for a release already dispatched by hand, turning one hotfix into two full three-runner builds, and its companion publish job had been deleted, so a scheduled run could only stage a draft nobody published. Release moments are decided by hand, so the trigger is too.
  • The Linux leg no longer reships a stale AppImage. target/appimage is restored from the build cache and images are named after the version, so last release's file survived under a name the current run never overwrote and the upload glob swept it in — which is how 2026.8.9 Beta 1 shipped a 2026.8.8 Beta 1 AppImage beside its own. The directory is cleared first, as the Windows installer step already did. daily-build.yml carried the same defect.
  • The asset check now demands exactly one match per platform at the version being built, and the workflow warns when the target tag is one that installed updaters cannot resolve.
  • The draft report no longer claims a release "stays a draft" when the tag already points at published assets.
  • cargo fmt --all -- --check had been failing on main since 1a2f409 — the only reason CI was red there. Fixed.

Accessibility

Accessibility semantics added across the shell: menu bar, title bar, sidebar, sliders, text inputs, the welcome screen, and the studio render/transport layers, with supporting div and a11y work in GPUI.


Changes since 2026.8.9 Beta 1

19 commits, 2026.8.9-beta1..fd4130a0.

Commit Summary
fd4130a0 Fix pointer and wheel input in off-screen plugin editors
b433a9dc Add accelerated CEF off-screen rendering on Windows
aab52241 Add accessibility semantics to UI controls
dab07e97 Fix instance routing and meter redraws
d2cbefff Fix React resolution in EQuz8 editor; bump version to 2026.8.10-beta1.1
154cdb24 Rewrite EQUZ8 editor on MixStation UI patterns with preset popover
3bc22e17 Strengthen EQUZ8 bypass visuals
dd387cd4 Rewrite EQUZ8 editor UI toward Pro-Q 4 / Serum with d3 and anime.js
ad7e31c3 Add UI/UX design skill data to the repo
a62f1ede MixStation: rack editor, per-stage telemetry, instance route
a3fd9e4e Fix Linux ALSA input buffers and make device lists readable
df3e07a2 Rewrite the MixStation editor UI
b6bbb426 Fix Space play/pause inside built-in plugin editors
635bea4d Stage APAK tools in distributable packages
d8e3854f Add signed APAK packages and key verification
ff6093ce Fix macOS MIDI input: retry the CoreMIDI client, read packets at the real ABI
cfdc9c2d Resolve a release version when the tag does not carry one
7d1545bb Apply rustfmt to build.rs and frame_scheduler.rs
1adc0a3b Stop the release cron and the stale AppImage it would reship

Full changelog: 2026.8.9-beta1...2026.8.10-beta1.1


Known gaps in verification

  • macOS MIDI input is unverified on hardware (see above).
  • Windows accelerated OSR is verified by cargo check and unit tests over the frame/failure path; it has not been confirmed visually in a running editor in this cycle.
  • The editor pointer and wheel fixes are covered by unit tests over the scroll-carry and click-count logic; the drag and trackpad behaviour has not been exercised by hand in a running editor.