v1.9.0
This release adds controller stick calibration and a broad internal quality pass. Controllers: calibrate the analog stick range of each controller with live visual feedback, persisted per controller, plus fixes for SDL controllers — digital L2/R2 now drive analog triggers for cores like Flycast, and duplicate controllers after a reconnect are reconciled. While a game runs, the display and system stay awake. The live shader editor's Apply now survives relaunching the same game, and TV Mode keeps the system you selected after exiting a game. Under the hood, pause state was consolidated into a single enum (fixing scrub and recording bugs), core options follow a single five-layer precedence walk, controller mappings share one source of truth, and a new automated smoke-test harness boots 11 ROMs through the real launch pipeline to verify stability.
Highlights
- Stick calibration: Calibrate each controller's analog stick range per direction with a live calibration session and visual feedback, applied to all your games and persisted per controller.
- No sleep while gaming: The display and system are kept awake while a game is running, so play sessions are never interrupted by your Mac falling asleep.
- Live shader Apply persists: Shader changes applied with the in-game shader editor survive relaunching the same game instead of reverting to the stale snapshot.
- TV Mode keeps its system: After exiting a game, TV Mode stays on the system you selected rather than snapping back to Recent.
- SDL controller fixes: Digital L2/R2 buttons now drive the analog trigger state (fixing Dreamcast controls in Flycast), and duplicate controllers are reconciled when GameControllerKit reconnects a device SDL already accepted.
- Consolidated pause state: Rewind, scrub, and pause now share a single state machine — scrubbing over a paused game keeps it paused on exit, and gamepad navigation restores the prior pause state.
- Automated smoke tests: A new boot-smoke harness launches 11 ROMs (NES to MAME) through the real launch pipeline and asserts the first frame renders, with the slang shader audit still healthy.
Detailed Changes
- feat: implement stick calibration for controllers —
StickCalibrationandControllerCalibrationmodels for per-direction analog stick range,StickCalibrationSessionfor live capture, SDLInputManager applies calibration, runners (DOSRunner, ScummVMRunner) consume it, calibration UI in ControllerSettingsViewGroup, en/es/ja/pt translations, plus aROMLibrary.refreshAllbulk-refresh refactor - fix: prevent SDL duplicate when GCController registers a controller after reconnect — reconcile on GC/SDL connect notifications by closing the duplicate SDL handle; drop the never-decremented
acceptedSDLCountByNamecounter that made the name-based guard permanently fail - fix: preserve live shader Apply across game relaunch — merge playtime bookkeeping into the current library entry instead of the stale launch-time snapshot; add the missing
shader.applyAndSavekey to all translation files - fix: prevent display and system sleep while a game is running
- refactor: apply audit slices D, E and F-sl — launch-path work deduplication (cached
CoreCapabilities, save-list snapshot, generic XPC reply helper, normalized extension index), state/precedence consolidation (5-layer core-option walk, single controller mapping map, ShaderManager as the single slang-preset source, canonical RA match status, pending-theme cluster, ManualStatusController), and tooling (11-ROM boot-smoke matrix viaTRUCHI_SMOKE_ROM, slang audit, retired outdated MAME scripts) - refactor: remove dead LaunchConfig fields and single wizard flag —
slotToLoad/coreOptions/bezelFileName/hardcoreModedeleted;hasCompletedWizarddropped in favor ofhas_completed_onboarding - refactor: consolidate pause state into a single
PauseStateenum — replaces the manually-synced isPaused/isRewinding triple; scrub over paused keeps paused on exit, gamepad toolbar restores prior pause state, setPaused no-ops during scrub - refactor: apply audit slices A+C — single
AuthStateenum, recording session reset on all teardown paths, honor 404 negative-cache expiry, remove dead SequenceRunner engine and write-only flags; fix scrub/empty-recording bugs - fix: send analog trigger state for SDL digital L2/R2 buttons — pure SDL controllers only set the digital key state; cores like Flycast read Dreamcast triggers from the analog state; add l2/r2 to the flycast core button overrides and labels
- fix: keep TV mode on selected system after game exit — make
initialEntryIDa one-shot seed so rebuilds no longer snap row-1 selection back to the entry TV mode opened on - Bump marketing version to 1.9.0 and build number to 32 across
project.yml, both Info.plists, anddocs/_config.yml