Skip to content

Brandy: one-click Steam on Wine 11, modern UI, rebrand and audit fixes - #4

Merged
iStorry merged 7 commits into
mainfrom
feat/easy-steam
Oct 29, 2026
Merged

iStorry merged 7 commits into
mainfrom
feat/easy-steam

Conversation

@iStorry

@iStorry iStorry commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Turns the fork into Brandy: someone with no technical knowledge opens the app, clicks Set Up Steam, and ends up in Steam's sign-in window. Seven commits, each self-contained.

What changed

One-click Steam on Wine 11

  • The runtime moves from Wine 7.7, where the current Steam client crashes on launch, to Wine 11.0 (WineHQ's macOS build by Gcenx). scripts/build-whiskywine.sh assembles it from pinned, checksum-verified releases. BrandyWine 3.1.0 is already published as the whiskywine-3 release.
  • First run is one flow, with progress for each step and plain-language errors with Try Again:
    1. Rosetta.
    2. Download the runtime.
    3. Create a Steam bottle.
    4. Download Steam from Valve.
    5. Install it silently.
    6. Run Steam's first update.
  • After setup the window is a simple home screen with Open Steam and Repair Steam. Bottles, programs and Wine settings move behind Show advanced options in Settings.
  • Only Windows 10 and 11 remain. Older bottles load as Windows 10 instead of losing their name and pins, and their prefix is switched once.
  • Enhanced Sync is removed: MSync and ESync are CrossOver patches with no effect on Wine 11.

Steam's black window and duplicate launches

  • Steam's Chromium interface drew black on Wine's Mac driver. A small steamwebhelper.exe shim (scripts/steamwebhelper-shim.c, built into the runtime) starts it with --disable-gpu --in-process-gpu --disable-direct-composition and two more switches, following Carafe's approach on the same Wine builds. Brandy reinstalls the shim when Steam updates replace it.
  • Clicking Open Steam again while Steam was updating started a second copy. Brandy now detects a running steam.exe per bottle and shows its windows instead.

Modern UI

  • Sidebar, bottle page, configuration, programs and program pages are redesigned with stock SwiftUI.
  • Every setting has an explanation, programs show icons and folders, and actions live in the toolbar.

Rebrand to Brandy

  • New name, icon, bundle IDs (com.istorry.Brandy), strings in every language, CLI (brandy), README and issue templates.
  • Internal module names are unchanged.

Audit fixes

Testing

  • WhiskyKit tests grow from 24 to 36. SwiftLint --strict is clean, and Whisky and WhiskyCmd build in Release with no warnings.
  • Fresh install on Apple Silicon, macOS 15.7: setup, the Steam bottle and the Steam install all completed. Steam updated itself and opened its sign-in window. With the shim, the sign-in form renders.
  • Through Brandy's own code paths: the shim is installed, all five switches reach Chromium, and running-Steam detection works.
  • CLI: create, run, remove, add and delete were each exercised on a throwaway bottle.

Not covered

  • Signing in to Steam, downloading games, and DirectX 12 titles. The last needs Apple's Game Porting Toolkit, which isn't bundled.
  • Developer ID signing and notarization, and a Sparkle signing key for updates.

Clearer information with stock SwiftUI and AppKit controls, so the app
follows the system look, accent color and dark mode.

- Sidebar: each bottle shows its Windows version, or that its files are
  missing. Standard empty states for no bottles and no selection.
- Bottle page: a header with Windows version, Wine version and program
  count; pinned programs as tiles that show Run on hover; Programs and
  Configuration rows that preview their contents. Run and the bottle
  actions move from a bottom bar to the window toolbar.
- Configuration: every setting has a one-line explanation, DPI shows its
  value, sections no longer collapse, and Control Panel, Registry Editor
  and Wine Configuration get their own section.
- Programs: icons, names without .exe, the Windows folder each program
  is in, and Pinned and Hidden groups ("Blocklist" is now "Hidden").
- Program page: header with icon and location, Run and More in the
  toolbar, labelled Launch and Environment Variables sections.
- Icons are extracted once, off the main thread, and shared between
  screens.
- Remove BottomBar, which nothing uses anymore.

Fixes
- Opening Configuration wrote DPI 0 to the registry of any bottle whose
  DPI was never changed. DPI is now written only when changed in its
  sheet, and a stored 0 shows as the default 96.
- Deleting a bottle left a stub folder behind: loading a program wrote
  default settings, and saving recreated the bottle's folder. Loading no
  longer writes, and saving skips bottles whose folder is gone.
- Opening a bottle stopped pinning Start Menu programs after the first
  one that was already pinned.
scripts/build-whiskywine.sh assembles the Libraries folder from:
- Wine 11.0 stable, the official WineHQ macOS package (Gcenx/macOS_Wine_builds
  11.0_1), which already includes Mono, Gecko and MoltenVK
- DXVK-macOS 1.10.3, using the plain variant: Wine ignores DLLs marked as
  builtin when they are copied into system32, which is how Whisky enables DXVK
- winetricks and its verb list at a pinned commit

Each download is checked against a pinned SHA-256.

The publish script now uploads to a release per runtime major version
(whiskywine-3), so a build only downloads a runtime whose layout it supports,
and checks for Wine 11's `wine` launcher instead of `wine64`.
First run is now a single flow: Set Up Steam installs Rosetta, downloads the
Wine 11 runtime, creates a bottle, downloads Steam from Valve, installs it
silently and offers Open Steam. Each step reports progress, failures are
explained in plain language with Try Again resuming from the failed step, and
every step is skipped when already done, so the same flow repairs Steam.

After setup the window is a simple home screen with one Open Steam button and
Repair Steam. Bottles, programs and Wine settings move behind "Show advanced
options" in Settings; choosing "Set up without Steam" turns it on.

Wine 11
- Run the `wine` launcher; Wine 9+ has no `wine64`. The Terminal shortcut no
  longer needs aliases because Wine 11 ships winecfg, regedit and friends.
- Download runtime 3 from the whiskywine-3 release, and treat an older
  runtime as missing so setup replaces it.
- Find Start Menu shortcuts in the profile Wine names after the macOS user,
  as well as CrossOver's `crossover` profile.
- Remove Enhanced Sync: MSync and ESync are CrossOver patches with no effect
  on Wine 11.

Windows versions
- Offer only Windows 10 and 11. Bottles stored as XP to 8.1 load as Windows 10
  instead of failing to decode, which would have reset their name and pins,
  and their Wine prefix is switched to Windows 10 once.

Shared pieces
- WhiskyKit Download: validated downloads with progress and cancellation,
  used for both the runtime and the Steam installer.
- WhiskyKit SteamInstaller: Valve's official installer, silent install, and
  finding the bottle Steam is in.
- BottleVM.createBottle awaits creation; Program.launch awaits the program
  exiting, so the home screen knows when Steam is running.
- Remove the six setup screens the onboarding flow replaces, and their
  strings.
Brandy is distilled from wine, as this app is distilled from Whisky:
Wine -> Whisky -> Brandy. Everything users see changes; internal code module
names (WhiskyKit, WhiskyCmd, the Whisky target) stay to keep the diff focused.

- App: Brandy.app with a new icon, a brandy snifter of amber on mahogany
  drawn for every macOS size; bundle IDs com.istorry.Brandy,
  com.istorry.Brandy.Thumbnail and com.istorry.BrandyCmd. The Swift module
  keeps its name through PRODUCT_MODULE_NAME.
- Text: every string in every language says Brandy, and the runtime is
  BrandyWine. The upstream-import messages keep "Whisky" because they name
  the original app.
- Menus: Help links to this repository instead of upstream's website and
  Discord. The move-to-Applications check looks for Brandy.app.
- CLI: the command is `brandy`, installed as /usr/local/bin/brandy.
- Logs and paths: log headers read "Brandy Version", and container paths
  display as ~/Library/Containers/Brandy.
- Updates: the Sparkle feed points at this repository's releases instead of
  upstream's appcast, and upstream's signing key is removed. An own signing
  key is needed before the first update is published.
- README describes Brandy on Wine 11 and credits Whisky and the projects it
  builds on. Upstream's CodeWeavers affiliate block and its logos are removed.
- Issue templates ask for the Brandy version and drop the malformed link to
  upstream's Discord.
Black window
Steam's interface is Chromium (steamwebhelper.exe). On Wine 11 for macOS it
composites in a separate GPU process and presents through DirectComposition,
neither of which Wine's Mac driver supports, so every window stays black.
Steam forwards none of the Chromium switches that fix this; -cef-disable-gpu,
-cef-single-process and -cef-in-process-gpu were all tried, as was running
Steam's processes on DXVK.

- scripts/steamwebhelper-shim.c: a small Windows program that stands in for
  steamwebhelper.exe and starts Steam's helper with --no-sandbox --disable-gpu
  --disable-gpu-compositing --in-process-gpu --disable-direct-composition,
  following Carafe's approach on the same Wine builds.
- The runtime build compiles it with mingw-w64; runtime 3.1.0 includes it,
  and the app now requires 3.1 so setup replaces 3.0.
- SteamInstaller installs the shim, keeping Steam's helper as
  steamwebhelper_real.exe, and reinstalls it before each launch because Steam
  updates restore their own helper. Steam starts with -noverifyfiles so its
  file check does not undo it. Program.launch applies this to every way of
  opening Steam.
- Setup gains an "Updating Steam" step that runs Steam's first self-update
  with progress, stops it, and installs the shim, so the first Open Steam
  already draws and nobody waits on a silent button.

Duplicate launches
Clicking Open Steam again while Steam was still updating started a second
Steam that raced the first. "Steam is running" followed the launched process,
which exits as soon as it hands off to a running Steam.

- WineProcesses finds a bottle's Windows programs from the arguments and
  environment macOS records for each process (WINEPREFIX and the Windows
  program name), skipping the padding Wine leaves after rewriting arguments.
- SteamSession opens Steam at most once per bottle; when Steam is running the
  button reads Show Steam and brings its windows forward instead.

Quitting
- "Terminate Wine processes when Brandy closes" never ran: the stop was
  started on a detached task during termination, and the setting read as off
  until changed because its default was only declared in SettingsView. Quit
  now stops bottles synchronously and the default is registered at launch.
- Wine.killBottle threw nothing and dropped the stop's errors; it is replaced
  by an awaitable Wine.stopAllProcesses whose failures are logged.
CLI (Whisky-App#965, Whisky-App#1088, Whisky-App#1140, Whisky-App#1325)
- `run` started the program on a detached task and exited before it ran, and
  ignored the program's arguments. It now waits for the program and passes
  its arguments through.
- `create` made a folder and metadata but no Wine prefix (the setup was
  commented out). It now uses the same creation code as the app, with
  --windows win10|win11.
- `add` decoded the bottle folder as if it were Metadata.plist, so it always
  failed; it also allowed duplicates.
- `delete` removed the bottle from the list before deleting it, then only
  printed a failure and exited successfully.
- Remove the empty export/install/uninstall commands.

Shared bottle creation
- Bottle.create in WhiskyKit validates, creates the folder, sets up the
  prefix and removes everything if that fails; the app and CLI both use it.

Errors people never saw
- Moving, exporting or removing a bottle, running a program from the Run
  button or from Finder, creating a shortcut, opening a Wine tool and moving
  the app to Applications printed failures to the console. They now show an
  alert (ErrorAlert). Configuration rows already showed failures, so their
  errors are logged instead of printed.
- Winetricks showed an empty sheet when its verb list was missing; it now
  says it couldn't load.
- Moving a bottle rewrote its pins to the new path before moving it, so a
  failed move left them pointing nowhere; moving and removing also left the
  bottle stuck as in progress on failure.

Correctness
- FileManager.replaceDLLs returned at the first file that was not a DLL
  instead of skipping it, so DXVK could be half installed.
- Shortcut apps made their launch script world-writable (0777); it is 0755.
- Executables without an icon got an empty image instead of the placeholder.
- Non-Unicode Start Menu shortcuts were decoded as Turkish (CP1254) instead
  of Western (CP1252).
- One "run file" helper replaces two copies of the batch-or-program logic.

Performance
- Opening a bottle walked all of Program Files, including Steam's game
  library, on the main thread. The walk now runs in the background, and the
  hidden list is a set instead of being searched for every file.

Dead code
- Remove the unreachable Running Processes view (it also waited 2,000
  nanoseconds where it meant two seconds), WhiskyWineInstaller.uninstall and
  their strings.
Shift-click "run in Terminal" failed with "Expected end of line, etc. but
found identifier" (Whisky-App#712, Whisky-App#942, Whisky-App#1139): the shell command put
each environment value in double quotes, and only backslashes were escaped
before embedding it in an AppleScript string, so the first quote ended it.
The same values were also open to shell expansion of $, backticks and quotes
in paths, and Open in Terminal put the bottle's name inside double quotes, so
a name containing $(...) would run as a command.

- String.shellQuoted (POSIX single quotes) and String.appleScriptQuoted
  replace String.esc and the three hand-escaped AppleScript blocks.
- Terminal.run opens Terminal with a command and throws when AppleScript
  fails; Shift-click, Open in Terminal and Winetricks use it.
- Terminal commands and shortcut apps get Steam's launch preparation too, so
  a Steam shortcut or Shift-click launch also draws its window.
- Tests round-trip quoting through /bin/sh and AppleScript, and compile the
  Terminal script for paths with quotes, spaces, parentheses, $ and
  backslashes.
- Replace the idle-CPU test, which measured the whole test process and
  failed when other tests ran in parallel, with a direct check that the
  pipes' readability handlers are removed at end of file.
@iStorry
iStorry merged commit a79f3fb into main Oct 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant