Skip to content

test(eui-neo): cover both entry-point shapes with window members - #139

Merged
Sunrisepeak merged 4 commits into
mainfrom
feat/eui-neo-window-and-app-main-examples
Jul 30, 2026
Merged

test(eui-neo): cover both entry-point shapes with window members#139
Sunrisepeak merged 4 commits into
mainfrom
feat/eui-neo-window-and-app-main-examples

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

Why

compat.eui-neo's four existing workspace members (eui-neo, eui-neo-markdown, eui-neo-vulkan, eui-neo-sdl2) are all headless by construction — they assert on eui::json::Document, core::platform:: frame flags and markdown blocks, and verify the backends by symbol reference rather than by rendering. Two gaps followed:

  1. The app-main feature — upstream's core/app/glfw_app_main.cpp, 584 lines of platform code — was built by zero members on zero platforms.
  2. A consumer that drives EUI itself (core::window::createWindow() + core::render::createRenderBackend() + app::update/app::render) was never exercised, so it was unknown whether that surface is even reachable through the descriptor's include_dirs.

What

One member per entry-point shape — the two shapes an EUI application can have, mutually exclusive per project because features resolve per consuming project.

member app-main entry point new coverage
eui-neo-window off hand-written main() in the test TU core::window, OpenGL backend, ScopedRenderBackend, paced loop, freetype text
eui-neo-app-main on upstream glfw_app_main.cpp the feature's source compiles; main really comes from the package

Both build on all three platforms, deliberately not cfg-gated. imgui-window and gui-stack are linux-only because they pull the X11 packages directly; compat.eui-neo and compat.glfw are not. Audited before un-gating: every core/ header these TUs reach is platform-clean (no windows.h, no Cocoa, no GL) and every GLFW entry point used is portable. Both add GLFW_INCLUDE_NONE so GLFW does not pull a GL header of its own choosing (GL/gl.h on Windows, the deprecated OpenGL/gl.h on macOS).

Build + link is the headless test; the real window run is opt-in via MCPP_RUN_WINDOW=1 and bounded to 60 frames so it terminates on its own.

Neither re-declares compat.glfw, even though both call GLFW directly (EUI's core::window facade exposes no event-pump entry point): mcpp propagates a transitive dependency's include_dirs and link inputs to the consumer, verified by dropping the line and rebuilding. Same convention as eui-neo-sdl2 (#include <SDL.h>, <curl/curl.h>) and eui-neo-vulkan (vkEnumerateInstanceVersion), neither of which re-declares what it reaches through a feature.

eui-neo-app-main cannot put the opt-in gate in main() (it has none), so it lives in a namespace-scope constructor, which runs first.

A green test is not a rendered frame

app::update() reaches compose() and the .onFrame() hook without touching the render backend, so a member that counts frames would pass against a null backend — exactly the failure mode the first revision of this descriptor shipped (createRenderBackend() returning null behind a green CI). Both members therefore assert on core::render::lastRenderFrameStats() and require rectDraws > 0 and textDraws > 0. textDraws is split out because text is the only path needing freetype plus a usable system font — a different failure from a dead backend.

Windows: two real requirements the app-main TU carries

glfw_app_main.cpp had never been compiled on any platform, so auditing its Windows half produced the only non-comment descriptor changes here, both windows-section-only:

  • -D_WIN32_WINNT=0x0A00 (cflags + cxxflags) — core/app/frame_pacing.h calls CreateWaitableTimerExW, which mingw-w64's winbase.h and the Windows SDK both guard behind #if _WIN32_WINNT >= 0x0600. The header sets no floor of its own and mingw-w64 has historically defaulted as low as 0x502, so the build would otherwise depend on which default the runner's llvm ships. Every other Windows API in the package is pre-Vista, which is why this never surfaced.
  • -lkernel32 — same header reaches CreateWaitableTimerExW / SetWaitableTimer / WaitForSingleObject / CloseHandle. In every sane default lib set, so belt-and-braces; but the surrounding comment is specifically about mcpp not inheriting CMake's CMAKE_C_STANDARD_LIBRARIES, so naming it is consistent and free.

timeBeginPeriod (winmm) and MonitorFromWindow / GetMonitorInfoW / EnumDisplaySettingsW (user32) were already covered; glfw3native.h ships in compat.glfw's include/GLFW/.

Why the mcpp pin does NOT move

MCPP_VERSION stays at 0.0.109. The three capabilities that could simplify this descriptor all landed at or below the pin: default-features = false (#242, 0.0.98), feature→feature forwarding (#243, 0.0.99), obj-path disambiguation (#233/#240). Nothing in 2026.7.27.1 .. 2026.7.29.2 touches feature semantics — git log v0.0.109..HEAD over the feature/resolver/plan sources comes back empty of anything relevant.

One correction while re-probing: the descriptor claimed "there is no default-features = false (mcpp#242)". Wrong — #242 shipped in 0.0.98. The accurate statement is that its seedDefault gate is manifest-side, and a default feature declared in an xpkg descriptor is never seeded at all, so a consumer has nothing to switch off. Re-probed by giving the package default = { defines = { "MCPP_PROBE_DEFAULT_APPLIED=1" } } and checking the macro from a plain consumer TU: absent on 0.0.109. So the generated-header + forced--include backend encoding stays. Probably worth an upstream issue as a descriptor-side gap in #242's coverage.

Descriptor change (otherwise comment-only)

The app-main comment said "a consumer that has its own main() must not get a second one". True, but it understates the consequence: mcpp links a dependency's objects eagerly, not as lazily-selected archive members, so glfw_app_main.o is always in the link rather than only when main is still undefined. An app-main project may therefore contain no main() of its own at all — including every mcpp test TU. Verified:

ld: obj/glfw_app_main.o: in function `main':
    core/app/glfw_app_main.cpp:398: multiple definition of `main';
    obj/probe.o: first defined here

That makes eui-neo-app-main a two-sided assertion: if app-main stops contributing its source -> undefined reference to 'main' here; if it stops being opt-in -> multiple definition of 'main' in every other member.

Loop mechanics recorded in the design doc

Two upstream behaviours a consumer must know, both found by actually running the loop:

  • Pacing is mandatory. OpenGLRenderBackend::initialize() calls glfwSwapInterval(0), so present() never blocks on vsync. The unpaced loop ran at ~8300 fps and pinned a core; the member paces off DslAppConfig::fpsValue.
  • onFrame fires twice per rendered frame. app::update() runs the runtime a second time with deltaSeconds == 0 to settle the re-compose that onFrame itself requested. Counting every invocation reports 2x the real frame rate — gate on the delta.

Neither is a descriptor bug, and neither was discoverable from the headless members.

Verification

mcpp 0.0.109 (matching validate.yml env.MCPP_VERSION), linux-x86_64, gcc 16.1.0, X11 display with NVIDIA GL 4.6.

                              headless        MCPP_RUN_WINDOW=1
eui-neo-window                ok (0.07s)      ok (60 frames in 0.98s, 120 rect / 120 text draws)
eui-neo-app-main              ok (0.08s)      ok (60 frames, 118 rect / 118 text draws)
eui-neo            (existing) ok
eui-neo-markdown   (existing) ok

mcpp xpkg parse, check_mirror_urls.lua, check_package_name.lua and check_cross_package_refs.lua all pass on the touched descriptor.

macOS and Windows are CI-verified only — there is no runner for either here, so the build+link half on those platforms is asserted by the workspace (macos) / workspace (windows) legs of this PR, not locally. Newly exercised there: glfw_app_main.cpp (never compiled anywhere before), and the first consumer-side link of compat.glfw off Linux — macOS Cocoa/IOKit/CoreFoundation, Windows -lgdi32.

Not done

  • app-main-sdl2 stays uncovered — same shape against compat.sdl2, a fifth window member for one sources line. Worth doing if the SDL2 window backend gains real users.
  • The opt-in windowed run has been exercised on linux-x86_64 only; both other runners are headless, so they get build+link, same as imgui-window.

compat.eui-neo's four existing workspace members are all headless by
construction, so nothing built upstream's `core/app/glfw_app_main.cpp` (the
`app-main` feature — 584 lines of platform code, zero coverage on zero
platforms) and nothing exercised a consumer driving EUI itself. Add one
member per entry-point shape:

* `eui-neo-window`   — `app-main` OFF, hand-written main() + render loop.
  Covers core::window::createWindow(), the OpenGL backend,
  ScopedRenderBackend and the freetype text path.
* `eui-neo-app-main` — `app-main` ON, no main() at all (catch2-main shape),
  so `main` must come from the package.

Both are linux-gated like imgui-window; build+link is the headless test and
the real window run is opt-in via MCPP_RUN_WINDOW=1, bounded to 60 frames so
it terminates on its own. eui-neo-app-main cannot put that gate in main() —
it has none — so it lives in a namespace-scope constructor.

Neither member trusts a frame counter: app::update() reaches compose() and
the .onFrame() hook without touching the render backend, so both assert
rectDraws > 0 and textDraws > 0 from core::render::lastRenderFrameStats().
textDraws is separate because it is the only path needing freetype plus a
usable system font.

Also sharpen the descriptor comment on `app-main`. mcpp links a dependency's
objects eagerly rather than as lazily-selected archive members, so
glfw_app_main.o is always in the link — an app-main project may contain no
main() of its own at all, including every `mcpp test` TU. Adding one yields
`multiple definition of 'main'` from glfw_app_main.cpp:398. The previous
wording described the intent but understated the consequence.

Verified on mcpp 0.0.109 / linux-x86_64 / gcc 16.1.0, headless and against a
real X11 + GL 4.6 display:

  eui-neo-window    ok (60 frames in 0.98s, 120 rect / 120 text draws)
  eui-neo-app-main  ok (60 frames, 118 rect / 118 text draws)

The descriptor change is comment-only; `mcpp xpkg parse` and the three lua
lints pass, and the existing eui-neo members stay green.
Both members were [target.'cfg(linux)']-gated, which made them compile to
`int main() { return 0; }` on macOS and Windows — trivially green, zero
coverage, and it left `glfw_app_main.cpp` uncompiled on every platform after
all.

imgui-window and gui-stack are linux-only because they pull the X11 packages
directly. compat.eui-neo and compat.glfw are not, and the four existing
eui-neo members already build on every runner. Audited before un-gating:
every core/ header these TUs reach (window_types, window_backend,
render_backend, primitive_geometry, render_surface, platform, input_state,
input_types, ime_bridge) is platform-clean — no windows.h, no Cocoa, no GL —
and every GLFW entry point used is portable. core::releaseInputQueue reaches
eui_ime_uninstall_message_filter, which ime_bridge.c defines on all three.

Also add GLFW_INCLUDE_NONE before <GLFW/glfw3.h>, as core/input/input_state.h
does, so GLFW does not pull a GL header of its own choosing (GL/gl.h on
Windows, the deprecated OpenGL/gl.h on macOS).

Drop the redundant `glfw = "3.4"` from both members. mcpp propagates a
transitive dependency's include_dirs and link inputs to the consumer, and
compat.eui-neo already depends on compat.glfw everywhere — verified by
removing the line and rebuilding. Matches the existing convention:
eui-neo-sdl2 includes <SDL.h>/<curl/curl.h> and eui-neo-vulkan calls
vkEnumerateInstanceVersion, neither re-declaring those packages.

macOS/Windows are CI-verified only; there is no runner for either here. The
opt-in windowed run stays linux-only (both runners are headless), same as
imgui-window.

Descriptor comment fix while re-probing the encoding: it claimed "there is no
`default-features = false` (mcpp#242)". Wrong — #242 shipped in 0.0.98. The
accurate statement is that its seedDefault gate is manifest-side and a
`default` feature declared in an xpkg DESCRIPTOR is never seeded at all, so
the consumer has nothing to switch off. Re-probed with
`default = { defines = { ... } }` and a plain consumer: macro absent on
0.0.109.

MCPP_VERSION deliberately stays at 0.0.109: #242 (0.0.98), #243 (0.0.99) and
#233/#240 (0.0.97/0.0.98) are all at or below the pin, and nothing in
2026.7.27.1 .. 2026.7.29.2 touches feature semantics, so a bump buys no
simplification here.
Auditing the one TU the `app-main` feature adds — which nothing in this index
compiled until tests/examples/eui-neo-app-main existed — turned up two
Windows-only requirements it carries:

* core/app/frame_pacing.h calls CreateWaitableTimerExW, and both mingw-w64's
  winbase.h and the Windows SDK guard that declaration behind
  `#if _WIN32_WINNT >= 0x0600`. The header sets no floor of its own, so it
  inherits the toolchain default, and mingw-w64 has historically defaulted as
  low as 0x502. Pin 0x0A00 in cflags + cxxflags rather than depend on which
  default the runner's llvm ships — that is effectively what upstream builds
  against via the MSVC SDK, and a command-line define is respected by
  _mingw.h's `#ifndef` guard. Every other Windows API in the package is
  pre-Vista, which is why this never surfaced.

* the same header reaches CreateWaitableTimerExW / SetWaitableTimer /
  WaitForSingleObject / CloseHandle, so name -lkernel32. It is in every sane
  default lib set, but the surrounding comment is specifically about mcpp not
  inheriting CMake's CMAKE_C_STANDARD_LIBRARIES, so spelling it out is
  consistent and free.

timeBeginPeriod (winmm) and MonitorFromWindow / GetMonitorInfoW /
EnumDisplaySettingsW (user32) were already covered; glfw3native.h ships in
compat.glfw's include/GLFW/.

Linux is unaffected (the flags are inside the `windows` section) and all three
eui-neo members re-verified green there.
PR #139 run 30502516910: all three workspace legs selected the same six
eui-neo members and passed (linux 12m26s, macos 6m15s, windows 7m50s).

The app-main assertion is self-proving and worth spelling out: the test TU
defines no main(), so the binary can only run if glfw_app_main.o supplied one.
Its 'linked, main() supplied by the package' line appearing on the windows and
macos legs is proof that TU compiled and linked there for the first time —
which is what exercises the -D_WIN32_WINNT=0x0A00 and -lkernel32 additions.
@Sunrisepeak
Sunrisepeak merged commit 8d67478 into main Jul 30, 2026
5 checks 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