mirror of
https://github.com/willfaust/Madeira.git
synced 2026-10-02 08:35:24 +08:00
DXMT, Madeira Dock and madeira-d3d12 began under research/ as experiments and are now core parts of every build, so they move to the top level (dxmt/ and madeira-dock/ stay submodules, renamed to match). The test programs and host checks move out of build/ into tests/ (host, x64, x86, dxmt, proc, net), the helper scripts join tools/, and the example madeira.cfg moves to docs/. Every reference in the build scripts, tests, docs, notices and code comments follows the new paths; the adopted Converter Exception text is left as adopted. Removed: the Wine patches from before the Wine fork (the fork carries them), a game-specific deploy script and a parked session note. The README gains a repository layout table. Checked: the app builds, the host checks pass and fail exactly as before the move, and DXMT's build directories regenerate cleanly at the new path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
284 lines
17 KiB
Markdown
284 lines
17 KiB
Markdown
# Library front end
|
||
|
||
Madeira starts in a game library. The original diagnostic screen (the
|
||
"developer interface") is still there: **Settings › Interface › Use developer
|
||
interface** switches to it, and its **Use New Interface** button switches back.
|
||
Either change applies at the next start (close Madeira in the app switcher and
|
||
open it again).
|
||
|
||
The code is `app/Madeira/Library.swift` and `app/Madeira/GuestDisplay.swift`
|
||
(the virtual monitor and its layout), plus the wiring in `ContentView.swift`
|
||
(`launchLibraryEntry`, `runWineFullSequence(profile:)`, `sessionBody`, the
|
||
library HUD inside `TouchControlsOverlay`, and `MetalBackedView`'s layout and
|
||
touch mapping).
|
||
|
||
## Adding games
|
||
|
||
Copy a game's whole folder into **Madeira › wine › drive_c** with the Files app,
|
||
tap **+** and choose its `.exe`. Only x86 and x64 PE executables inside drive_c
|
||
can be added; the library stores the path relative to drive_c, so a changed
|
||
app container path does not break entries. Adding an entry installs nothing.
|
||
|
||
The library reads the executable's PE imports (and those of the DLLs next to
|
||
it, plus bounded scans for dynamically loaded renderer DLL names) to show a
|
||
graphics-API badge, and measures the install folder's size. The badge names an
|
||
API only when exactly one is found: it describes what the files import, not
|
||
which renderer a game picks at run time.
|
||
|
||
Library data is written atomically to `Documents/madeira-library.json`
|
||
(version 1); covers chosen from Files are stored as thumbnails in
|
||
`Documents/madeira-art/`. A library file that cannot be read, or that has a
|
||
newer version, is left untouched and cannot be overwritten from the UI.
|
||
Removing an entry never removes the game's files or saves.
|
||
|
||
## Library screen
|
||
|
||
- Layouts: cards, compact cards, list and compact list (one short row per
|
||
game). Sort by last played, name, date added or folder size. Search by title.
|
||
- Sections, as in the fork's library: when Madeira Dock is available,
|
||
**Steam** (the Steam games being downloaded and the games Steam has
|
||
installed, with their count; a **Sign in to Steam** card when signed out),
|
||
its **Not installed** group (the account's other games, with their count),
|
||
then **Other games** (the games you added; **+** in the navigation bar adds one). Tapping
|
||
**Steam** or **Other games** collapses it; **Not installed** folds on its
|
||
own, open by default; each state is remembered. Search, the layout and, for
|
||
installed games, Sort by apply to every section. Pull down to read the
|
||
Steam install records and the account's library again. Without Madeira
|
||
Dock the games you added are one grid.
|
||
- Grid cards: a game that is not installed shows its artwork darkened, with a
|
||
download glyph on a soft circle of blur. Every grid card throws ambient
|
||
light on the page around it, like an LED strip behind a TV: its artwork's own
|
||
colours, with arcs of the ring brightening and falling into shadow and the
|
||
colours travelling around it as if a film were playing (fainter and less vivid
|
||
for a game that is not installed; held still with Reduce Motion). Pressing a
|
||
card shrinks its artwork, and its light draws in with crisp rays and goes out
|
||
behind it like a spotlight's aperture closing; on release the artwork springs
|
||
back and the light opens again slowly. The width a row of cards leaves goes
|
||
into the gaps between them, so each card's light keeps to its own space.
|
||
- **Desktop** opens the Wine desktop (explorer and services in a virtual
|
||
desktop) with its own profile; its Resolution is the desktop's size.
|
||
- The build label (`MadeiraBuild` in Info.plist, else the bundle version) is
|
||
shown in Settings (Ready to play) and in the developer interface's status row.
|
||
- **Settings**: JIT and memory status, Enable JIT, extended logging, pointer
|
||
mode (Absolute, Relative or Touch) and touch sensitivity, **Display** (hold
|
||
the display at its maximum rate, off by default), **Memory & sync** (swap tier
|
||
Off/1/2/4 GB, off by default; sync engine, Fastsync by default), the interface
|
||
switch and **Credits** (the last section). Display applies from the next session or FPS limit change; Memory &
|
||
sync after a restart. They write `env.MADEIRA_PROMOTE`, `swap-mb`,
|
||
`inproc-sync` and `env.MADEIRA_FASTSYNC` in `Documents/madeira.cfg`, keeping
|
||
every other line. With neither sync key set the engine is fastsync
|
||
(`madeira_cfg_sync_engine` in `build/madeira_cfg.h`); `inproc-sync = 1` selects
|
||
madsync.
|
||
|
||
## Game details
|
||
|
||
Tapping a game opens its details page; it stays up until the session's
|
||
starting screen takes over (or an error is shown). A profile holds:
|
||
|
||
- title and cover image;
|
||
- **Resolution**: the size of the Windows screen (the virtual monitor) the game
|
||
renders for: 640×480 to 2560×1440, plus **Screen shape**, this device's own
|
||
aspect ratio at 720 lines (for example 1560×720 on a 19.5:9 phone), so a game
|
||
fills the screen without bars. It is exported as the session default
|
||
(`MADEIRA_SCREEN_W/H`, `MADEIRA_SCREEN_SRC=knob`), which win32u reports for
|
||
the session. New entries use 1024×768, the size main uses for every launch;
|
||
- **Aspect & scaling**: how that screen is shown. **Fit** letterboxes it,
|
||
**Fill** covers the screen and crops, **Stretch** fills it exactly, **Aspect**
|
||
letterboxes the shape the game actually draws (its back buffer) and **Fill
|
||
height** keeps that shape at full height. Touches are mapped through the same
|
||
rectangle, so input lines up in every mode;
|
||
- FPS limit: 60, the display maximum or uncapped (the same presentation
|
||
pacing modes as the FPS pill in the developer interface), and 30 when DXMT
|
||
has its 30 FPS cap (willfaust/dxmt#1; DXMT without it would present mode 3
|
||
uncapped, so the choice is hidden and a saved 30 runs as 60);
|
||
- reduced-precision x87: off by default, as in FEX; only an explicit choice
|
||
exports `FEX_X87REDUCEDPRECISION=1`;
|
||
- **CPU cores reported** (Automatic, 1, 2, 4 or 6) and **D3D9 anisotropic
|
||
filtering** (Application default, up to 1×, 2×, 4× or 8×): only a choice
|
||
other than the default exports `MADEIRA_CPU_COUNT` (wine) or
|
||
`DXMT_D9_ANISO_LIMIT` (DXMT); the defaults export nothing. The library
|
||
exports no other engine switch;
|
||
- launch arguments (double-quoted tokens, at most 64 and 4 KB in total; not
|
||
for Steam games, which Madeira Dock starts with Steam's own launch option);
|
||
- performance overlay, live logs and touch controls for the session, with the
|
||
controls' **opacity** and overall **size**. The touch layout itself is saved
|
||
per game from the in-game editor.
|
||
|
||
A game you added starts directly. A Steam game's page (`docs/STEAM_LIBRARY.md`)
|
||
adds a **Steam** section under the library details: **Start with** Madeira
|
||
Dock (the default) or **The game** (its own program without Steam, from Steam's
|
||
launch configuration or chosen under **Program**), Dock's per-launch pool
|
||
choice, **One-time installs**, updates, **Repair
|
||
installed files**, App ID, free space and **Uninstall**; its **Executable**
|
||
section shows the install folder, and it has no **Remove from library** (the
|
||
entry goes with **Uninstall**).
|
||
|
||
## Sessions
|
||
|
||
Play applies the profile and runs the same `runWineFullSequence` as the
|
||
developer interface's buttons. The game is shown full screen in either
|
||
orientation. A starting screen with the game's cover stays until the first
|
||
frames arrive (Metal presents or a desktop surface); after 30 seconds it offers
|
||
**Show game view**. A Madeira Dock start keeps it, with the Dock's status,
|
||
until the game's own window is shown, and adds **Show desktop**
|
||
(`docs/MADEIRA_DOCK.md`, "Starting screen"). A row of round glyph-only buttons (their words are
|
||
VoiceOver labels) holds **Show live log**, which shows the most recent log lines.
|
||
|
||
The small menu button (drag to move; it fades after three seconds) opens the
|
||
in-game menu:
|
||
|
||
1. touch controls on/off, their **Controller layout** (the built-in Xbox
|
||
controller, the user's custom layouts, **Create new layout**; remembered per
|
||
game), their **Opacity** and **Size**, **Edit controls**
|
||
(the existing editor) and the **Keyboard** (its own key window, with an
|
||
Esc/Ctrl/Shift/Alt/Tab/Enter/arrow row; modifiers latch);
|
||
2. the FPS limit, **Aspect & scaling**, and the mouse and pointer settings;
|
||
3. the performance overlay and its fields (FPS, average frame time, memory
|
||
footprint, battery);
|
||
4. **Quit game** in red. Quit asks the program to close with Alt+F4 through
|
||
the normal input queue, so it can save; the session ends when it exits.
|
||
|
||
Changes made in the menu (FPS limit, Aspect & scaling, controls, overlay) are
|
||
saved to the game's profile.
|
||
|
||
**Pointer modes.** Absolute drags the pointer like a trackpad, Relative sends
|
||
finger movement as mouse movement (mouse-look), and **Touch** clicks where the
|
||
finger is: tap to click, hold or move to drag, a two- or three-finger tap for a
|
||
right or middle click, a two-finger drag to scroll. Touch works in direct and
|
||
desktop sessions.
|
||
|
||
When the session ends Madeira returns to the library, restores the touch layout
|
||
it had before, and hides the ended session's surfaces. If the session ended by
|
||
itself and the program Madeira launched exited with a Windows error status
|
||
(for example `0xC0000005`, memory access violation), a message says so. The
|
||
status comes from one weak hook in ntdll's common exit wrapper
|
||
(`wine_launched_process_did_exit` in `build/ntdll-unix/server_ios.c`), called
|
||
only for the session's initial process, the one the app handed to
|
||
`__wine_main`; helpers and processes the program starts are never reported.
|
||
The hook takes one integer, does not allocate and does not log; the app keeps
|
||
only the last error status. No program names are involved.
|
||
|
||
One Wine session runs per app run: a second one cannot start in the same
|
||
process (the wineserver's permanent objects from the first session remain and
|
||
the registry initialisation aborts). The library asks to restart Madeira
|
||
instead.
|
||
|
||
## Steam setup
|
||
|
||
The code is `app/Madeira/Onboarding.swift`. It uses Steam sign-in
|
||
(`docs/STEAM_SIGNIN.md`) and Madeira Dock (`docs/MADEIRA_DOCK.md`) through
|
||
their public pieces only: `SteamSignInModel`/`SteamSignInView` for signing in
|
||
and out (the token stays in sign-in's Keychain store), and
|
||
`MadeiraDockModel.prepareClient()`/`MadeiraDockView` for Dock.
|
||
|
||
**First-run setup.** On a new install the library opens a full-screen setup
|
||
once: welcome, **Sign in to Steam**, **Prepare Madeira Dock** (Valve's client
|
||
components, about 73 MB, only when Dock is available), done. Every step has
|
||
**Set up later**, and the welcome page has **Skip setup**. Finishing or
|
||
skipping stores `madeiraOnboardingDone` in the app's UserDefaults, which iOS
|
||
removes with the app. Setup opens only when there is something to set up: the
|
||
sign-in page needs Steam sign-in or Dock, and without either setup never
|
||
opens. It never opens over a running session.
|
||
|
||
Setup is app UI only. It starts no Wine session, and the component download
|
||
runs Dock's own verified download without Wine. It changes no JIT pool, engine
|
||
switch or configuration default.
|
||
|
||
**Settings › Steam** shows the signed-in account with **Sign out of Steam**
|
||
(or **Sign in to Steam**), **Madeira Dock** (Dock's sheet, with the last Dock
|
||
result under it) and **Run setup again**. A game started from the Dock sheet
|
||
here runs as a library session: full-screen view, starting screen, in-game
|
||
menu, and the one-session-per-run rule. That session is not added to the
|
||
library.
|
||
|
||
**Steam games in the library** (`app/Madeira/SteamGames.swift`). When Madeira
|
||
Dock is available, the library shows a **Steam** section above **Other
|
||
games**, the games you added. It lists the games Steam has installed in the
|
||
prefix, exactly as Dock's own discovery finds them (`appmanifest_<appid>.acf`
|
||
in `C:\Program Files (x86)\Steam\steamapps` and the other C: libraries its
|
||
`libraryfolders.vdf` lists), and, once you are signed in, under **Not
|
||
installed**, the account's owned games that are not installed yet, which are
|
||
installed from their download sheet (`docs/STEAM_LIBRARY.md`); a game being
|
||
downloaded moves up to the installed games. The section follows the library's
|
||
search and layout and collapses like Other games. Artwork comes from Steam's
|
||
public store CDN.
|
||
An installed game opens its **Game details** page (above): the game is a
|
||
library entry with its own settings, listed only in the Steam section, and its
|
||
**Play** goes through Dock's launch path with the entry as its launch profile,
|
||
as a library session. When a download finishes, the sheet's button reads
|
||
**Open** and opens that page. Play starts only a game Steam marks fully
|
||
installed (and not being downloaded), with Valve's client components present
|
||
and a Steam sign-in. Reading install records never
|
||
writes Steam files; only the downloads and **Uninstall** of `docs/STEAM_LIBRARY.md`
|
||
do, and only in Madeira Dock's own library folder. Controller focus does not
|
||
reach the section yet.
|
||
|
||
Log tags: `[onboarding]` (`shown reason=… steps=…`, `step=…`, `done`,
|
||
`skipped`), `[steam-games]` (counts and App IDs), `[library-sections]`
|
||
(`native-steam=… sections=… collapse=…`, flags only) and the library and download
|
||
tags of `docs/STEAM_LIBRARY.md`. No account name, token or path is logged.
|
||
|
||
## Controllers
|
||
|
||
Player 1's controller navigates the library through `GamepadInput`: D-pad or
|
||
left stick moves the focus, A opens and plays, B goes back, Y adds a game and
|
||
the shoulder buttons switch between Library and Settings. In a session,
|
||
Back+Start opens the in-game menu and B closes it. While the library or its
|
||
menu owns input, the game sees a connected pad at rest.
|
||
|
||
## Switches
|
||
|
||
`env.NAME = 0` in `Documents/madeira.cfg` (or `NAME=0` in `madeira-env.txt`):
|
||
|
||
| Switch | Default | `0` means |
|
||
| --- | --- | --- |
|
||
| `MADEIRA_FRONTEND_DEFAULT_NEW` | on | the developer interface is the default |
|
||
| `MADEIRA_FRONTEND` | unset | `env.MADEIRA_FRONTEND = 0/1` picks the interface when nothing was chosen in the app |
|
||
| `MADEIRA_FRONTEND_CONTROLLER` | on | no controller navigation |
|
||
| `MADEIRA_ONE_SESSION_PER_RUN` | on | a second session is attempted anyway |
|
||
| `MADEIRA_EXIT_REPORT` | on | no message when a session ends by itself |
|
||
| `MADEIRA_LIBRARY_HIDE_ENDED_DESKTOP` | on | the ended desktop's surface is left as it was |
|
||
| `MADEIRA_BUILD_LABEL` | on | no build label (the `[build]` log line stays) |
|
||
| `MADEIRA_UI_LOG_IDLE` | on | the log view keeps parsing while hidden |
|
||
| `MADEIRA_LOG_VIA_STDERR` | on | Swift log lines use their own file handle |
|
||
| `MADEIRA_RUNTIME_SETTINGS` | on | no Display and Memory & sync sections in Settings |
|
||
| `MADEIRA_SESSION_TOOLS` | on | no Aspect & scaling in the in-game menu, and a session does not save it |
|
||
| `MADEIRA_SCREEN_SHAPE_RESOLUTION` | on | no Screen shape resolution choice |
|
||
| `MADEIRA_FRONTEND_KEYBOARD` | on | Keyboard opens the game view's own keyboard instead of the key window |
|
||
| `MADEIRA_ONBOARDING` | on | first-run setup never opens, and Settings › Steam has no **Run setup again** |
|
||
| `MADEIRA_LIBRARY_COLLAPSE` | on | the **Steam** and **Other games** titles do not collapse (**Not installed** still folds) |
|
||
| `MADEIRA_LIBRARY_AMBIENT` | on | no ambient light around the library's grid cards |
|
||
|
||
Opt-in (`env.NAME = 1`), off by default:
|
||
|
||
| Switch | `1` means |
|
||
| --- | --- |
|
||
| `MADEIRA_PROMOTE` | the display link also holds the panel at its maximum rate in the 60 FPS cap (Settings › Display) |
|
||
| `MADEIRA_DEVICE_STATS` | a `[device-load]` line (thermal state, low power, screen capture) every 10 s while Wine runs |
|
||
|
||
Log tags: `[frontend]`, `[display]`, `[display-shape]`, `[frontend-pointer]`, `[launch-view]`, `[startup-log]`, `[exit-report]`,
|
||
`[session-once]`, `[library-surface]`, `[library-metadata]`, `[onboarding]`,
|
||
`[frontend-controller]`, `[frontend-keyboard]`, `[device-load]`, `[promote]`.
|
||
|
||
## Tests
|
||
|
||
`tests/host/check-frontend.py` (profiles incl. resolution and scaling,
|
||
the engine switches a profile exports, the 30 FPS fallback, the display layout
|
||
math, controller commands, the exit hook, and the presence of the details and
|
||
in-game menu options), `tests/host/check-runtime-settings.py`
|
||
(`MadeiraConfig.set` and the Settings defaults) and
|
||
`tests/host/check-library-api.py` (renderer detection and the badge).
|
||
`tests/host/check-onboarding.py` covers Steam setup: the pages with and
|
||
without Dock, the done key, the `MADEIRA_ONBOARDING` switch, and the wiring
|
||
(no Wine session, no pool or engine switch, sign-in and Dock only through
|
||
their public pieces).
|
||
`tests/host/check-steam-games.py` covers the library's Steam section: Dock's
|
||
discovery on a synthetic drive_c laid out as Steam writes it, the merge of
|
||
installed and owned games, the section, status, card pill, search, Play and
|
||
artwork rules, the program an installed game's pills describe, the groups of the
|
||
library's sections and their Sort by order, and that Play uses only Dock's launch
|
||
path. `tests/host/check-library-sections.py` covers the library page's
|
||
sections (order, texts, collapsing, search, layout, pull to refresh).
|
||
`tests/host/check-steam-library.py`
|
||
covers the owned library and downloads (`docs/STEAM_LIBRARY.md`).
|