Add physical window size overrides
This commit is contained in:
@@ -201,7 +201,7 @@ The meaningful consumer catalog is:
|
|||||||
| Setting group | Native Himegari effect | Classification |
|
| Setting group | Native Himegari effect | Classification |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `CREATEOBJECT`, `DRAWMODE` | `CreateObject=2` selects only the retained D3D/object backend; `DrawMode=1` selects its matching draw/text path. Startup verifies that bit `1 << DrawMode` exists in the backend mask. | Active renderer selection |
|
| `CREATEOBJECT`, `DRAWMODE` | `CreateObject=2` selects only the retained D3D/object backend; `DrawMode=1` selects its matching draw/text path. Startup verifies that bit `1 << DrawMode` exists in the backend mask. | Active renderer selection |
|
||||||
| `SCREENX`, `SCREENY` | Replaces the generic `640x480` fallback with the `800x600` logical canvas before window/surface creation. The port now parses and applies these dimensions to its logical canvas and default windowed client size. | Active core profile; implemented |
|
| `SCREENX`, `SCREENY` | Replaces the generic `640x480` fallback with the `800x600` logical canvas before window/surface creation. The port parses and applies these dimensions to its logical canvas and default windowed client size; optional host boot arguments can vary only the physical client. | Active core profile; implemented |
|
||||||
| `FONT`, `ENABLEANTIFONT`, `ANTIFONTVERSION` | Seeds MS Mincho, enables `message:UseAntiFont`, selects the grayscale glyph-outline path, and records antialias version 3. | Active text profile |
|
| `FONT`, `ENABLEANTIFONT`, `ANTIFONTVERSION` | Seeds MS Mincho, enables `message:UseAntiFont`, selects the grayscale glyph-outline path, and records antialias version 3. | Active text profile |
|
||||||
| `DEPENDMOVIESOUND` | Supplies the default movie-audio dependency/routing policy when movie opcode flags do not force another route. | Active media policy |
|
| `DEPENDMOVIESOUND` | Supplies the default movie-audio dependency/routing policy when movie opcode flags do not force another route. | Active media policy |
|
||||||
| `FULLSCREENBIT`, `ALWAYSBACKUPSURFACE` | These are read only inside the legacy `CreateObject & 1` DirectDraw branch of `engine_initialize_graphics_from_settings@0x406480`. Himegari selects only bit 2, so its 32-bit/zero values do not drive the active renderer. | Valid compatibility settings, inert for this profile |
|
| `FULLSCREENBIT`, `ALWAYSBACKUPSURFACE` | These are read only inside the legacy `CreateObject & 1` DirectDraw branch of `engine_initialize_graphics_from_settings@0x406480`. Himegari selects only bit 2, so its 32-bit/zero values do not drive the active renderer. | Valid compatibility settings, inert for this profile |
|
||||||
@@ -226,7 +226,9 @@ This is intentionally a native-behavior catalog, not a mandate to reproduce ever
|
|||||||
`Sys4AssetCatalog` now parses the ordered trailer once, retaining unknown pairs for diagnostics, and projects
|
`Sys4AssetCatalog` now parses the ordered trailer once, retaining unknown pairs for diagnostics, and projects
|
||||||
`SCREENX`/`SCREENY` into one validated engine-owned logical canvas. Godot uses that value for its content-scale
|
`SCREENX`/`SCREENY` into one validated engine-owned logical canvas. Godot uses that value for its content-scale
|
||||||
base, default windowed client, backbuffer/compositor bounds, primary surface, movie/layout fallbacks, and input
|
base, default windowed client, backbuffer/compositor bounds, primary surface, movie/layout fallbacks, and input
|
||||||
coordinates. AGE's independent `640x480` defaults apply for missing or invalid dimensions. The port still
|
coordinates. `--window-width`/`--window-height` may vary that client independently while the logical canvas
|
||||||
|
and all AGE coordinates remain unchanged. AGE's independent `640x480` defaults apply for missing or invalid
|
||||||
|
dimensions. The port still
|
||||||
preserves requested Mincho/Gothic faces, save version 3.10, and movie/audio behavior through their existing
|
preserves requested Mincho/Gothic faces, save version 3.10, and movie/audio behavior through their existing
|
||||||
paths; those keys have not yet been migrated to the parsed registry.
|
paths; those keys have not yet been migrated to the parsed registry.
|
||||||
|
|
||||||
|
|||||||
@@ -3545,6 +3545,30 @@ The Godot C# project builds with zero warnings, and the threaded headless selfte
|
|||||||
windowed startup directly requests the same validated dimensions. Physical window overrides remain the
|
windowed startup directly requests the same validated dimensions. Physical window overrides remain the
|
||||||
second slice described above.
|
second slice described above.
|
||||||
|
|
||||||
|
### Physical-window override result (implemented 2026-07-28)
|
||||||
|
|
||||||
|
The second slice adds `--window-width <pixels>` and `--window-height <pixels>` as presentation-only
|
||||||
|
Godot user arguments. Each omitted dimension independently defaults to the selected SYS4INI logical
|
||||||
|
canvas; specifying only width or height therefore changes only that physical client dimension. Repeated
|
||||||
|
options use the last value. Present values must be invariant-culture integers in `1..16384`; missing,
|
||||||
|
nonnumeric, nonpositive, and oversized values stop startup with a parameter-specific diagnostic.
|
||||||
|
|
||||||
|
`WindowLaunchOptions` is resolved immediately after the logical canvas and before any presentation
|
||||||
|
allocation. In ordinary windowed mode its result sets `Window.Size`; fullscreen, maximized, or minimized
|
||||||
|
launch modes retain their display-owned physical size. In every mode `ContentScaleSize`, backbuffers,
|
||||||
|
primary/offscreen surfaces, ADV layouts, and VM/input coordinates remain tied to the unchanged logical
|
||||||
|
canvas under Godot's `Viewport`/`Keep` aspect policy. The startup log distinguishes the logical canvas,
|
||||||
|
requested physical window, request source, actual backend size, and current window mode. The local
|
||||||
|
launchers remain intentionally narrow conveniences; pass these user arguments directly after Godot's
|
||||||
|
`--` separator.
|
||||||
|
|
||||||
|
Eleven focused cases cover defaults, both overrides, independent partial overrides, duplicates, missing
|
||||||
|
values, malformed/nonpositive values, and the upper bound. The complete engine suite passes 454/454 and
|
||||||
|
the Godot project builds with zero warnings. Headless selftests pass both the default `800x600` request
|
||||||
|
and an overridden `1200x700` request while continuing to report `logical-canvas=800x600`; invalid zero
|
||||||
|
width exits during startup with the expected diagnostic. Headless's minimized `64x64` backend remains
|
||||||
|
non-authoritative for visible client geometry.
|
||||||
|
|
||||||
## ADV requested-face selection implemented (2026-07-28)
|
## ADV requested-face selection implemented (2026-07-28)
|
||||||
|
|
||||||
Opcode `0x1a5` is no longer a presentation no-op. The VM retains its requested face in
|
Opcode `0x1a5` is no longer a presentation no-op. The VM retains its requested face in
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ or replaced before claiming portable exports.
|
|||||||
|
|
||||||
| Area | Current dependency | Runtime impact | Portability status / future action |
|
| Area | Current dependency | Runtime impact | Portability status / future action |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| SYS4INI per-game startup profile | `Sys4AssetCatalog` parses and retains the bounded ordered startup trailer; `Sys4LogicalCanvas` applies `SCREENX`/`SCREENY` with AGE's independent `640x480` fallbacks. Native SYS4INI also carries text, ADV input/skip, save ABI/path, audio, legacy renderer, and Windows registration settings | Godot now uses the selected canvas for content scaling, backbuffers/compositor bounds, primary surfaces, layout/input fallbacks, and the matching default windowed client. The port still does not source `CancelMesSkipOnClick`, `CoexistMesSkip`, cursor/redraw policy, or wheel action ids from the trailer | Add later boot parameters that vary only the physical window while Godot scales/letterboxes the unchanged canvas. Apply further semantic keys explicitly, translate save roots through the host, and classify DirectDraw/fullscreen-bit and registration/key settings as native compatibility metadata. Canonical inventory and consumers: `sys4-format-notes.md` and `engine-re.md` |
|
| SYS4INI per-game startup profile | `Sys4AssetCatalog` parses and retains the bounded ordered startup trailer; `Sys4LogicalCanvas` applies `SCREENX`/`SCREENY` with AGE's independent `640x480` fallbacks. Native SYS4INI also carries text, ADV input/skip, save ABI/path, audio, legacy renderer, and Windows registration settings | Godot uses the selected canvas for content scaling, backbuffers/compositor bounds, primary surfaces, layout/input fallbacks, and the default windowed client. `--window-width`/`--window-height` vary only the physical client while preserving the canvas and letterbox policy. The port still does not source `CancelMesSkipOnClick`, `CoexistMesSkip`, cursor/redraw policy, or wheel action ids from the trailer | Validate the existing presentation policy on Linux/macOS window managers. Apply further semantic keys explicitly, translate save roots through the host, and classify DirectDraw/fullscreen-bit and registration/key settings as native compatibility metadata. Canonical inventory and consumers: `sys4-format-notes.md` and `engine-re.md` |
|
||||||
| Retained graphics presentation | Backend-neutral `GfxState`; accepted default Godot `Sprite2D` GPU stage plus the retained software pixel oracle, using runtime `ImageTexture`, canvas transforms/materials, and no native graphics API | GPU backend caches static/color-key variants, updates dynamic surfaces, handles retained range transitions, and falls back whole-frame for the legacy host screen-transition path | Godot owns D3D/Vulkan/Metal/OpenGL selection; validate shader/blend/filter behavior per target rather than adding a platform renderer |
|
| Retained graphics presentation | Backend-neutral `GfxState`; accepted default Godot `Sprite2D` GPU stage plus the retained software pixel oracle, using runtime `ImageTexture`, canvas transforms/materials, and no native graphics API | GPU backend caches static/color-key variants, updates dynamic surfaces, handles retained range transitions, and falls back whole-frame for the legacy host screen-transition path | Godot owns D3D/Vulkan/Metal/OpenGL selection; validate shader/blend/filter behavior per target rather than adding a platform renderer |
|
||||||
| AGE movie decode (`0x236` scene movies; `0x20f` modal LOGO/OP/ED) | `FfmpegMovieDecoder` is the sole factory over the project-owned `native/age_movie_ffmpeg` ABI | Windows-x64 passes the complete 213-payload installed video/audio corpus gate plus audible LOGO/OP/CHAPTER playback | Add target-specific native builds and export packaging |
|
| AGE movie decode (`0x236` scene movies; `0x20f` modal LOGO/OP/ED) | `FfmpegMovieDecoder` is the sole factory over the project-owned `native/age_movie_ffmpeg` ABI | Windows-x64 passes the complete 213-payload installed video/audio corpus gate plus audible LOGO/OP/CHAPTER playback | Add target-specific native builds and export packaging |
|
||||||
| Movie integration | Each surface owns a unique playback-instance id; `MovieRuntime` owns `IMovieDecoder` from an injected factory; video-only streams use monotonic pacing while audio-bearing streams use the Godot output clock | Concurrent/restarted uses of one asset have independent frame/audio/completion/teardown state; managed code is no longer Windows-annotated, while only the win-x64 native bundle exists today | Add Linux/macOS native builds and smoke gates |
|
| Movie integration | Each surface owns a unique playback-instance id; `MovieRuntime` owns `IMovieDecoder` from an injected factory; video-only streams use monotonic pacing while audio-bearing streams use the Godot output clock | Concurrent/restarted uses of one asset have independent frame/audio/completion/teardown state; managed code is no longer Windows-annotated, while only the win-x64 native bundle exists today | Add Linux/macOS native builds and smoke gates |
|
||||||
|
|||||||
@@ -686,9 +686,10 @@ specific data for diagnostics; semantic settings are applied through explicit cr
|
|||||||
legacy renderer/registration switches remain classified rather than blindly emulated. The first bounded
|
legacy renderer/registration switches remain classified rather than blindly emulated. The first bounded
|
||||||
application slice is complete: `SCREENX`/`SCREENY` select the validated logical canvas, all presentation
|
application slice is complete: `SCREENX`/`SCREENY` select the validated logical canvas, all presentation
|
||||||
allocations and primary bounds consume it, and it is the default windowed size. Other settings remain on
|
allocations and primary bounds consume it, and it is the default windowed size. Other settings remain on
|
||||||
their existing paths while gameplay is the priority. A second slice will add
|
their existing paths while gameplay is the priority. The second slice is also complete:
|
||||||
independent physical `--window-width`/`--window-height` overrides without changing VM coordinates or
|
independent physical `--window-width`/`--window-height` overrides change only the windowed client while
|
||||||
surface geometry. The executable task plan and gates live in `phase-a-slice-plan.md`. This experiment was
|
the logical canvas, VM coordinates, and surface geometry remain fixed. The executable task plan and gates
|
||||||
|
live in `phase-a-slice-plan.md`. This experiment was
|
||||||
diagnostic only; no Kamidori support or `0x1be` semantics were implemented.
|
diagnostic only; no Kamidori support or `0x1be` semantics were implemented.
|
||||||
|
|
||||||
### Other engine versions (SYS3 / SYS5) — one app, not many
|
### Other engine versions (SYS3 / SYS5) — one app, not many
|
||||||
|
|||||||
@@ -318,6 +318,11 @@ use the last `input-wait` event in `timeline.jsonl` as the authoritative manual
|
|||||||
|
|
||||||
- `--scene <NAME>` — override the default `SYSTEM4` root with a direct diagnostic scene, e.g. `--scene SC0240` (executes 29 nested subroutines).
|
- `--scene <NAME>` — override the default `SYSTEM4` root with a direct diagnostic scene, e.g. `--scene SC0240` (executes 29 nested subroutines).
|
||||||
- `--selftest` — headless; runs a **synthesized** scene through the thread/suspend/`CallDeferred` plumbing and asserts it matches a live headless run (full handling; no vm0/frozen golden). Exits.
|
- `--selftest` — headless; runs a **synthesized** scene through the thread/suspend/`CallDeferred` plumbing and asserts it matches a live headless run (full handling; no vm0/frozen golden). Exits.
|
||||||
|
- `--window-width <pixels>` / `--window-height <pixels>` — override only the physical windowed client
|
||||||
|
dimensions; each omitted dimension defaults independently to the SYS4INI logical canvas. Godot keeps
|
||||||
|
that logical canvas and aspect-preserving letterboxing, so these options do not change VM coordinates,
|
||||||
|
AGE surfaces, layout, or backbuffer allocation. Values must be integers in `1..16384`; duplicate
|
||||||
|
options use the last value.
|
||||||
- `--seed 0xADDR=VAL` (repeatable) — seed initial global state, e.g. `--seed 0xa57=1` unlocks Lily's form-A voiced dialogue.
|
- `--seed 0xADDR=VAL` (repeatable) — seed initial global state, e.g. `--seed 0xa57=1` unlocks Lily's form-A voiced dialogue.
|
||||||
- `--boot` — direct-scene diagnostic only: with `--scene <non-SYSTEM4>`, run the old
|
- `--boot` — direct-scene diagnostic only: with `--scene <non-SYSTEM4>`, run the old
|
||||||
`INITCONFIG/INIT2/INIT` state prefix before that isolated scene. The normal SYSTEM4-rooted launch neither
|
`INITCONFIG/INIT2/INIT` state prefix before that isolated scene. The normal SYSTEM4-rooted launch neither
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
<Compile Include="..\..\godot\PerformanceFrameLog.cs" Link="PerformanceFrameLog.cs" />
|
<Compile Include="..\..\godot\PerformanceFrameLog.cs" Link="PerformanceFrameLog.cs" />
|
||||||
<Compile Include="..\..\godot\MovieSurfaceRegistry.cs" Link="MovieSurfaceRegistry.cs" />
|
<Compile Include="..\..\godot\MovieSurfaceRegistry.cs" Link="MovieSurfaceRegistry.cs" />
|
||||||
<Compile Include="..\..\godot\RiffWaveSanitizer.cs" Link="RiffWaveSanitizer.cs" />
|
<Compile Include="..\..\godot\RiffWaveSanitizer.cs" Link="RiffWaveSanitizer.cs" />
|
||||||
|
<Compile Include="..\..\godot\WindowLaunchOptions.cs" Link="WindowLaunchOptions.cs" />
|
||||||
<Compile Include="..\..\tools\movie-corpus-gate\MovieCorpusGate.cs" Link="MovieCorpusGate.cs" />
|
<Compile Include="..\..\tools\movie-corpus-gate\MovieCorpusGate.cs" Link="MovieCorpusGate.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
64
engine/Age.Engine.Tests/WindowLaunchOptionsTests.cs
Normal file
64
engine/Age.Engine.Tests/WindowLaunchOptionsTests.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
using Age.Engine.Sys4;
|
||||||
|
|
||||||
|
public class WindowLaunchOptionsTests
|
||||||
|
{
|
||||||
|
private static readonly Sys4LogicalCanvas LogicalCanvas = new(1024, 576);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DefaultsPhysicalWindowToLogicalCanvas()
|
||||||
|
{
|
||||||
|
var options = WindowLaunchOptions.Resolve([], LogicalCanvas);
|
||||||
|
|
||||||
|
Assert.Equal(new WindowLaunchOptions(1024, 576, false, false), options);
|
||||||
|
Assert.False(options.IsOverridden);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WidthAndHeightOverrideOnlyPhysicalDimensions()
|
||||||
|
{
|
||||||
|
var options = WindowLaunchOptions.Resolve(
|
||||||
|
["--scene", "SC0000", "--window-width", "1600", "--window-height", "900"],
|
||||||
|
LogicalCanvas);
|
||||||
|
|
||||||
|
Assert.Equal(new WindowLaunchOptions(1600, 900, true, true), options);
|
||||||
|
Assert.Equal(new Sys4LogicalCanvas(1024, 576), LogicalCanvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("--window-width", "1280", 1280, 576, true, false)]
|
||||||
|
[InlineData("--window-height", "720", 1024, 720, false, true)]
|
||||||
|
public void PartialOverrideKeepsOtherLogicalDefault(
|
||||||
|
string argument, string value, int width, int height,
|
||||||
|
bool widthOverridden, bool heightOverridden)
|
||||||
|
{
|
||||||
|
var options = WindowLaunchOptions.Resolve([argument, value], LogicalCanvas);
|
||||||
|
|
||||||
|
Assert.Equal(new WindowLaunchOptions(
|
||||||
|
width, height, widthOverridden, heightOverridden), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DuplicateDimensionUsesLastValue()
|
||||||
|
{
|
||||||
|
var options = WindowLaunchOptions.Resolve(
|
||||||
|
["--window-width", "1200", "--window-width", "1400"], LogicalCanvas);
|
||||||
|
|
||||||
|
Assert.Equal(1400, options.Width);
|
||||||
|
Assert.Equal(576, options.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("--window-width")]
|
||||||
|
[InlineData("--window-width", "0")]
|
||||||
|
[InlineData("--window-height", "-1")]
|
||||||
|
[InlineData("--window-width", "not-a-number")]
|
||||||
|
[InlineData("--window-height", "16385")]
|
||||||
|
[InlineData("--window-width", "--window-height", "720")]
|
||||||
|
public void InvalidOrMissingDimensionIsRejected(params string[] arguments)
|
||||||
|
{
|
||||||
|
var error = Assert.Throws<ArgumentException>(
|
||||||
|
() => WindowLaunchOptions.Resolve(arguments, LogicalCanvas));
|
||||||
|
|
||||||
|
Assert.Contains(arguments[0], error.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ public partial class Main : Godot.Control
|
|||||||
private const int NativeBoldGlyphSpacing = 1;
|
private const int NativeBoldGlyphSpacing = 1;
|
||||||
private int _screenWidth = Sys4LogicalCanvas.DefaultWidth;
|
private int _screenWidth = Sys4LogicalCanvas.DefaultWidth;
|
||||||
private int _screenHeight = Sys4LogicalCanvas.DefaultHeight;
|
private int _screenHeight = Sys4LogicalCanvas.DefaultHeight;
|
||||||
|
private WindowLaunchOptions _windowOptions;
|
||||||
private TextureRect _screenView = null!; // shows the composited screen backbuffer
|
private TextureRect _screenView = null!; // shows the composited screen backbuffer
|
||||||
private Image _screen = null!; // SYS4INI-sized immediate-mode canvas
|
private Image _screen = null!; // SYS4INI-sized immediate-mode canvas
|
||||||
private ImageTexture _screenTex = null!;
|
private ImageTexture _screenTex = null!;
|
||||||
@@ -108,10 +109,21 @@ public partial class Main : Godot.Control
|
|||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
|
var userArgs = OS.GetCmdlineUserArgs();
|
||||||
// Resolve the selected game's logical canvas before any presentation allocation. The same catalog
|
// Resolve the selected game's logical canvas before any presentation allocation. The same catalog
|
||||||
// instance is reused for scripts and assets later in startup.
|
// instance is reused for scripts and assets later in startup.
|
||||||
var catalog = Sys4AssetCatalog.Load(Paths.Sys4Ini);
|
var catalog = Sys4AssetCatalog.Load(Paths.Sys4Ini);
|
||||||
var logicalCanvas = catalog.LogicalCanvas;
|
var logicalCanvas = catalog.LogicalCanvas;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_windowOptions = WindowLaunchOptions.Resolve(userArgs, logicalCanvas);
|
||||||
|
}
|
||||||
|
catch (System.ArgumentException error)
|
||||||
|
{
|
||||||
|
GD.PushError($"[startup] {error.Message}");
|
||||||
|
GetTree().Quit(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_screenWidth = logicalCanvas.Width;
|
_screenWidth = logicalCanvas.Width;
|
||||||
_screenHeight = logicalCanvas.Height;
|
_screenHeight = logicalCanvas.Height;
|
||||||
_screenPixels = new byte[logicalCanvas.RgbaByteCount];
|
_screenPixels = new byte[logicalCanvas.RgbaByteCount];
|
||||||
@@ -120,9 +132,11 @@ public partial class Main : Godot.Control
|
|||||||
rootWindow.ContentScaleAspect = Window.ContentScaleAspectEnum.Keep;
|
rootWindow.ContentScaleAspect = Window.ContentScaleAspectEnum.Keep;
|
||||||
rootWindow.ContentScaleSize = new Vector2I(_screenWidth, _screenHeight);
|
rootWindow.ContentScaleSize = new Vector2I(_screenWidth, _screenHeight);
|
||||||
if (rootWindow.Mode == Window.ModeEnum.Windowed)
|
if (rootWindow.Mode == Window.ModeEnum.Windowed)
|
||||||
rootWindow.Size = new Vector2I(_screenWidth, _screenHeight);
|
rootWindow.Size = new Vector2I(_windowOptions.Width, _windowOptions.Height);
|
||||||
GD.Print($"[profile] SYS4INI logical canvas={_screenWidth}x{_screenHeight} " +
|
GD.Print($"[profile] SYS4INI logical canvas={_screenWidth}x{_screenHeight} " +
|
||||||
$"window={rootWindow.Size.X}x{rootWindow.Size.Y}");
|
$"requested window={_windowOptions.Width}x{_windowOptions.Height} " +
|
||||||
|
$"source={(_windowOptions.IsOverridden ? "boot-arguments" : "logical-canvas")} " +
|
||||||
|
$"actual={rootWindow.Size.X}x{rootWindow.Size.Y} mode={rootWindow.Mode}");
|
||||||
|
|
||||||
// One logical canvas that draw-texture blits into, shown behind the dialogue.
|
// One logical canvas that draw-texture blits into, shown behind the dialogue.
|
||||||
_screen = Image.CreateEmpty(_screenWidth, _screenHeight, false, Image.Format.Rgba8);
|
_screen = Image.CreateEmpty(_screenWidth, _screenHeight, false, Image.Format.Rgba8);
|
||||||
@@ -205,7 +219,6 @@ public partial class Main : Godot.Control
|
|||||||
}
|
}
|
||||||
_audioOutputLatencySeconds = AudioServer.GetOutputLatency();
|
_audioOutputLatencySeconds = AudioServer.GetOutputLatency();
|
||||||
|
|
||||||
var userArgs = OS.GetCmdlineUserArgs();
|
|
||||||
_selftest = System.Array.IndexOf(userArgs, "--selftest") >= 0;
|
_selftest = System.Array.IndexOf(userArgs, "--selftest") >= 0;
|
||||||
bool boot = System.Array.IndexOf(userArgs, "--boot") >= 0; // diagnostic prefix for direct-scene runs
|
bool boot = System.Array.IndexOf(userArgs, "--boot") >= 0; // diagnostic prefix for direct-scene runs
|
||||||
bool nativeDebugMenu = System.Array.IndexOf(userArgs, "--native-debug-menu") >= 0;
|
bool nativeDebugMenu = System.Array.IndexOf(userArgs, "--native-debug-menu") >= 0;
|
||||||
@@ -2158,7 +2171,10 @@ public partial class Main : Godot.Control
|
|||||||
&& _screen.GetHeight() == _screenHeight
|
&& _screen.GetHeight() == _screenHeight
|
||||||
&& _screenPixels.Length == checked(_screenWidth * _screenHeight * 4)
|
&& _screenPixels.Length == checked(_screenWidth * _screenHeight * 4)
|
||||||
&& rootWindow.ContentScaleSize
|
&& rootWindow.ContentScaleSize
|
||||||
== new Vector2I(_screenWidth, _screenHeight);
|
== new Vector2I(_screenWidth, _screenHeight)
|
||||||
|
&& _windowOptions == WindowLaunchOptions.Resolve(
|
||||||
|
OS.GetCmdlineUserArgs(),
|
||||||
|
new Sys4LogicalCanvas(_screenWidth, _screenHeight));
|
||||||
textEffectSmoke.QueueFree();
|
textEffectSmoke.QueueFree();
|
||||||
ok &= launcherOk && sleepMinimumOk && inputTranslationOk && cp932WavMetadataOk
|
ok &= launcherOk && sleepMinimumOk && inputTranslationOk && cp932WavMetadataOk
|
||||||
&& bgmReplacementCancelsFade && textEffectModesOk && fontCalibrationOk
|
&& bgmReplacementCancelsFade && textEffectModesOk && fontCalibrationOk
|
||||||
@@ -2167,13 +2183,15 @@ public partial class Main : Godot.Control
|
|||||||
$"debug launcher catalog/UI smoke ({debugEntries.Count} packed scripts); " +
|
$"debug launcher catalog/UI smoke ({debugEntries.Count} packed scripts); " +
|
||||||
$"sleep-min=1ms; native-key-translation=ok; cp932-wav-info=ok; " +
|
$"sleep-min=1ms; native-key-translation=ok; cp932-wav-info=ok; " +
|
||||||
$"bgm-fade-replacement=ok; text-effect-modes=ok; font-calibration=ok; " +
|
$"bgm-fade-replacement=ok; text-effect-modes=ok; font-calibration=ok; " +
|
||||||
$"logical-canvas={_screenWidth}x{_screenHeight}");
|
$"logical-canvas={_screenWidth}x{_screenHeight}; " +
|
||||||
|
$"window-request={_windowOptions.Width}x{_windowOptions.Height}");
|
||||||
else GD.Print($"SELFTEST FAIL: threaded={actual.Count} vs headless={expected.Count}; " +
|
else GD.Print($"SELFTEST FAIL: threaded={actual.Count} vs headless={expected.Count}; " +
|
||||||
$"debug-launcher={launcherOk}; sleep-min={sleepMinimumOk}; " +
|
$"debug-launcher={launcherOk}; sleep-min={sleepMinimumOk}; " +
|
||||||
$"native-key-translation={inputTranslationOk}; cp932-wav-info={cp932WavMetadataOk}; " +
|
$"native-key-translation={inputTranslationOk}; cp932-wav-info={cp932WavMetadataOk}; " +
|
||||||
$"bgm-fade-replacement={bgmReplacementCancelsFade}; " +
|
$"bgm-fade-replacement={bgmReplacementCancelsFade}; " +
|
||||||
$"text-effect-modes={textEffectModesOk}; font-calibration={fontCalibrationOk}; " +
|
$"text-effect-modes={textEffectModesOk}; font-calibration={fontCalibrationOk}; " +
|
||||||
$"logical-canvas={logicalCanvasOk}({_screenWidth}x{_screenHeight})");
|
$"logical-canvas={logicalCanvasOk}({_screenWidth}x{_screenHeight}); " +
|
||||||
|
$"window-request={_windowOptions.Width}x{_windowOptions.Height}");
|
||||||
GetTree().Quit(ok ? 0 : 1);
|
GetTree().Quit(ok ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
52
godot/WindowLaunchOptions.cs
Normal file
52
godot/WindowLaunchOptions.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using Age.Engine.Sys4;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Presentation-only window dimensions resolved from Godot user arguments. These never redefine the
|
||||||
|
/// SYS4 logical canvas, VM coordinates, or AGE surface dimensions.
|
||||||
|
/// </summary>
|
||||||
|
public readonly record struct WindowLaunchOptions(
|
||||||
|
int Width, int Height, bool WidthOverridden, bool HeightOverridden)
|
||||||
|
{
|
||||||
|
public const int MaximumDimension = Sys4LogicalCanvas.MaximumDimension;
|
||||||
|
public bool IsOverridden => WidthOverridden || HeightOverridden;
|
||||||
|
|
||||||
|
public static WindowLaunchOptions Resolve(
|
||||||
|
IReadOnlyList<string> arguments, Sys4LogicalCanvas logicalCanvas)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(arguments);
|
||||||
|
int width = logicalCanvas.Width;
|
||||||
|
int height = logicalCanvas.Height;
|
||||||
|
bool widthOverridden = false;
|
||||||
|
bool heightOverridden = false;
|
||||||
|
|
||||||
|
for (int index = 0; index < arguments.Count; index++)
|
||||||
|
{
|
||||||
|
string argument = arguments[index];
|
||||||
|
if (argument is not ("--window-width" or "--window-height")) continue;
|
||||||
|
if (index + 1 >= arguments.Count)
|
||||||
|
throw new ArgumentException($"{argument} requires a pixel value");
|
||||||
|
|
||||||
|
string raw = arguments[++index];
|
||||||
|
if (!int.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out int value)
|
||||||
|
|| value <= 0 || value > MaximumDimension)
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"{argument} must be an integer from 1 through {MaximumDimension}; got '{raw}'");
|
||||||
|
|
||||||
|
if (argument == "--window-width")
|
||||||
|
{
|
||||||
|
width = value;
|
||||||
|
widthOverridden = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
height = value;
|
||||||
|
heightOverridden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WindowLaunchOptions(width, height, widthOverridden, heightOverridden);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user