Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions .agents/docs/2026-06-03-consumer-ux-and-lock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# imgui-m: Consumer UX, Unpinned Toolchain, and Lock Policy

> 创建: 2026-06-03 · Owner: sunrisepeak · 仓库: mcpplibs/imgui-m
> 关联 Master Plan: `/home/speak/workspace/github/agentdocs/2026-06-03-mcpp-ecosystem-architecture-plan.md`
> (本文是该计划工作流 **W3 (imgui-m)** 的子设计文档,见 Master Plan §3.3 / §6)

本文记录 imgui-m 侧的三件事:不锁定工具链(unpinned)的决定与理由、三档递进式
消费者 UX(默认 `imgui.app` / 自动契约 / 显式后端模块)、以及 lockfile 策略
(GL 运行时闭合需要 `compat.glx-runtime`)。所有决定都对齐 Master Plan 的
「Capability → Resolution Plan,两平面 + 三档收敛」统一模型,不为单次验收做特殊设计。

---

## 1. 不锁定工具链(unpinned)

### 决定

本包**不在任何 `mcpp.toml` 中声明 `[toolchain]`**(根包与所有 examples 均不 pin)。
mcpp 解析环境/默认工具链;GL 运行时由 mcpp/mcpp-index 闭合
(`compat.glx-runtime`,Linux 上由 `compat.glfw` 传递依赖引入),**不由本包打包**。

### 理由

- **关注点分离 / 两平面**(Master Plan §2.1–2.2)。工具链与 GL 主机运行时属于
*环境/主机平面*,不是包的内容。包在 `mcpp.toml` 里 pin 工具链会把环境决定
硬编码进可移植的包元数据,破坏「同一包在不同环境用合适工具链」的能力。
- **三档模型的"默认档"**:零配置可达,意图而非机制。包不写工具链 = 让 mcpp 用
环境默认;需要时由消费者/CI 在*环境层*显式叠加(见 §4),而不是在包里固化。
- **可移植**:macOS / Windows runner 不需要也不应继承 Linux 的工具链选择;不 pin
让每个平台各自解析本地最优工具链。

---

## 2. 三档递进式消费者 UX

对齐 Master Plan §2.3「默认(零配置) → 自动匹配(意图驱动) → 显式指定(局部叠加)」。

### Tier-0 — 默认 / `imgui.app` facade(最少代码)

`imgui.app` 拥有完整的窗口/上下文生命周期,消费者只提供每帧 UI 回调。
模块接口里以 inline header-style 模板提供 `run`,UI 回调为模板参数,**无 `<functional>`
依赖**。`export import imgui.core;`,因此消费者免费拿到 `ImGui::*` 表面。

```cpp
import imgui.core;
import imgui.app;

int main() {
return ImGui::App::run([] {
ImGui::Begin("hi");
ImGui::TextUnformatted("imgui.app facade");
ImGui::End();
});
}
```

API:

```cpp
export namespace ImGui::App {
struct Options { int width = 1280; int height = 720; const char* title = "mcpp imgui app"; };
template <class Ui> int run(Options opts, Ui&& ui); // 完整生命周期
template <class Ui> int run(Ui&& ui); // 用默认 Options
}
```

`run()` 流程:InitGlfw → CreateWindow(RecommendedGlConfig) → MakeContextCurrent →
SwapInterval(1) → CreateContext/SetCurrentContext → Backend::Init → 循环
(PollEvents / Backend::NewFrame / ImGui::NewFrame / `ui()` / ImGui::Render /
FramebufferSize+Viewport+ClearColor(0.10,0.10,0.12,1)+ClearColorBuffer+
RenderDrawData(GetDrawData)+SwapBuffers)→ 退出时 Shutdown / DestroyContext /
DestroyWindow / TerminateGlfw。成功返回 0,初始化失败返回非 0(并打印
`Backend::LastError`)。

### Tier-1 — 自动 / `imgui.backend` 契约(意图驱动)

针对 `BackendApi` 概念与共享类型写**后端无关**代码;具体后端是哪个由上层选择。
任何满足契约的后端都能代入,消费者代码不变。

```cpp
import imgui.core;
import imgui.backend; // BackendApi 概念 + GlConfig/Error/FbSize + RecommendedGlConfig()

template <ImGui::Backend::BackendApi Backend>
void frame(typename Backend::Window* w) {
Backend::PollEvents();
Backend::NewFrame();
ImGui::NewFrame();
/* ... UI ... */
ImGui::Render();
const auto fb = Backend::FramebufferSize(w);
Backend::Viewport(0, 0, fb.width, fb.height);
Backend::RenderDrawData(ImGui::GetDrawData());
Backend::SwapBuffers(w);
}
```

### Tier-2 — 显式 / `imgui.backend.<impl>` 模块 + alias(完全控制)

显式导入一个具体后端模块并 alias;换后端 = 换 import + 改一行 alias,其余不变。

```cpp
import imgui.core;
import imgui.backend.glfw_opengl3;
using Backend = ImGui::Backend::GlfwOpenGL3; // 换后端只需改这两行

int main() {
Backend::InitGlfw();
auto* window = Backend::CreateWindow(960, 540, "demo");
Backend::MakeContextCurrent(window);
ImGuiContext* ctx = ImGui::CreateContext();
ImGui::SetCurrentContext(ctx);
Backend::Init(window);
while (!Backend::WindowShouldClose(window)) { /* 手写帧循环 */ }
Backend::Shutdown();
ImGui::DestroyContext(ctx);
Backend::DestroyWindow(window);
Backend::TerminateGlfw();
}
```

复杂度单调下沉:Tier-0 最少代码 → Tier-2 最大控制。三档共享同一套
`RecommendedGlConfig()` 跨平台 GL/GLSL 默认与同一后端契约。

---

## 3. Lockfile 策略

### 策略

`mcpp.lock` 必须反映当前 index 的解析结果。GL 运行时闭合依赖 `compat.glx-runtime`
(Linux 上 `compat.glfw` 的传递依赖,把 host libGL/GLX 软链进 install 目录,
供 rpath/RUNPATH 透传)。lock 应随当前 index 重生成,不手工编辑。

重生成方式:仓库根目录 `mcpp update`(清空 lock)→ `mcpp build`(重解析并重写 lock)。

### 实测现状(诚实记录)

- `compat.glx-runtime@2026.06.03` 确已存在于 index
(`compat.glfw.lua` linux `deps` 含 `compat.glx-runtime`),并在 BMI 解析的依赖树中可见。
- 但用 **mcpp 0.0.46** 重生成后,根 `mcpp.lock` 仅记录**直接依赖**
(`compat.glfw` / `compat.imgui` / `compat.opengl`),**未**展开 `compat.glx-runtime`
等传递依赖。即:当前 lock 格式只锁直接依赖,GL 运行时闭合发生在解析/链接期,
而非在 lock 文本里展开。
- 另注:本仓 `.gitignore` 忽略 `mcpp.lock`,故 lock 不入库。

这对应 Master Plan §1 的 **R3**(lock 相对 index 的 glx-runtime 接线 stale)与
W1 的链接闭合修复(R2 rpath)。**完整的传递闭合(让 glx-runtime 显式落到 lock /
RUNPATH)需要 mcpp 核心侧的解析/rpath 工作就绪**;imgui-m 侧已做到「不 pin + 用当前
index 重生成 lock + 文档化策略」。待 mcpp 核心改动落地后,重生成的 lock/链接将
自然纳入 glx-runtime,无需改本包。

---

## 4. CI 过渡步骤(显式环境默认,非特殊设计)

Linux CI 今天会红,根因是 Master Plan **R1**:全新机器 mcpp bootstrap 默认装
musl-static 工具链,无法链接 glibc 世界的 X11/GL 栈。

修复:在 `.github/workflows/ci.yml` 的 "Install project tools" 之后、"Build library"
之前,加**一步**(仅 Linux):

```yaml
- name: Set default toolchain (env)
if: runner.os == 'Linux'
shell: bash
run: mcpp toolchain default gcc@16.1.0
```

为什么这不是"特殊设计":这是在 *CI 环境层* 显式声明环境默认工具链
(三档模型里的"显式档,局部叠加"),与最终架构一致;**包仍不 pin**。它是过渡而非长期:
待 Master Plan W1(mcpp bootstrap 默认改为 glibc 工具链)合并后,此步可直接删除。
macOS / Windows 已通过,保持不变;矩阵与 `continue-on-error` 设置不变。

---

## 5. 与 Master Plan 的映射

| 本文小节 | Master Plan 对应 |
|---|---|
| §1 不 pin 工具链 | §2.1 两平面 / §2.3 三档默认档 / §1 R1 |
| §2 三档 UX | §2.3 三档递进;W3 的 `imgui.app` (Tier-0) |
| §3 lock 策略 | §1 R3(lock stale)/ W1 运行闭合(R2 rpath)/ §6 W3 |
| §4 CI 过渡步骤 | §7 注 100–102(过渡:CI 环境默认,W1 后删) |

本文是 Master Plan 列出的三份子文档之一(imgui-m 侧);另两份分别在 mcpp 核心
(runtime-closure-and-toolchain-defaults)与 mcpp-index(capability-runtime-metadata)。
20 changes: 10 additions & 10 deletions .agents/docs/2026-06-03-imgui-backend-abstraction-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,17 +245,17 @@ int main() {

## 8. 跨平台工程化

### 8.1 toolchain
### 8.1 toolchain(不锁定 / unpinned)

`mcpp.toml` 增加 macOS / Windows toolchain 条目(与 mcpp 支持的工具链命名对齐),
例如:
本包**不在任何 `mcpp.toml` 中锁定 toolchain**(没有 `[toolchain]` 段)。
mcpp 解析环境/默认 toolchain;GL 运行时由 mcpp/mcpp-index 闭合
(`compat.glx-runtime`,Linux 上由 `compat.glfw` 传递依赖引入),不由本包打包。

```toml
[toolchain]
linux = "llvm@20.1.7"
macos = "llvm@20.1.7" # 或系统 clang;最终以 mcpp-index 可用项为准
windows = "llvm@20.1.7"
```
> 过渡说明:目前全新机器上 mcpp bootstrap 默认是 musl-static 工具链,无法链接
> 宿主 X11/GL 运行时。CI 在 Linux 上用一个显式环境默认步骤
> (`mcpp toolchain default gcc@16.1.0`)绕开,而不是在 `mcpp.toml` 里锁定。
> 待 mcpp bootstrap 默认改为 glibc 工具链后即可移除该步骤。详见
> `.agents/docs/2026-06-03-consumer-ux-and-lock.md`。

### 8.2 GL/GLSL 配置

Expand Down Expand Up @@ -291,7 +291,7 @@ windows = "llvm@20.1.7"
- `src/core.cppm` → 补 `ImVec4` 等(§7)
- `tests/backend_test.cpp` → 针对新表面 + `static_assert(BackendApi<...>)`
- `examples/minimal_window`、`examples/glfw_opengl3` → 统一 `Backend::` 用法 + `RecommendedGlConfig()`
- `mcpp.toml`(toolchain + sources)、`.github/workflows/ci.yml`(矩阵)
- `mcpp.toml`(sources;**不含** toolchain)、`.github/workflows/ci.yml`(矩阵 + Linux 环境默认 toolchain 过渡步骤)
- `docs/architecture.md`、`README.md` 同步

> 旧的 `imgui.backend.glfw` / `imgui.backend.opengl3` 自由函数命名空间被
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ jobs:
xlings update
xlings install

# Transition step: a fresh mcpp bootstrap currently defaults to a
# musl-static toolchain, which cannot link the host X11/GL runtime on
# Linux. The package intentionally does not pin a toolchain in mcpp.toml;
# we set an explicit environment default here instead. Remove this once
# mcpp's bootstrap default becomes a glibc toolchain.
- name: Set default toolchain (env)
if: runner.os == 'Linux'
shell: bash
run: |
mcpp toolchain install gcc 16.1.0
mcpp toolchain default gcc@16.1.0

- name: Show mcpp version
shell: bash
run: mcpp --version
Expand Down
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ module interfaces for the core API plus GLFW/OpenGL3 backend entry points.

## Modules

Three tiers of consumer UX:

- **Tier-0** `imgui.app` — the convenience facade. Owns the whole window/context
lifecycle; you supply only a per-frame UI callback.
- **Tier-1** automatic via the `imgui.backend` contract — write backend-agnostic
code against the `BackendApi` concept and shared types.
- **Tier-2** explicit `imgui.backend.<impl>` — import a concrete backend module
and alias it (`using Backend = ...;`); swap backend with one import + alias.

- `imgui.app`
- Tier-0 facade. `ImGui::App::run(opts, ui)` / `ImGui::App::run(ui)` drive the
full GLFW/OpenGL3 lifecycle using the default backend. `export import`s
`imgui.core` so a consumer gets the `ImGui::*` surface for free.
- `imgui.core`
- Core Dear ImGui context, frame, widget, and draw-data APIs.
- `imgui.backend`
Expand Down Expand Up @@ -39,6 +52,12 @@ The root package depends on:
The repository does not vendor Dear ImGui sources. Source and header files come
from compat packages through mcpp dependency metadata.

### Toolchain and GL runtime

The package does not pin a toolchain; mcpp resolves the environment/default
toolchain. The GL runtime is closed by mcpp/mcpp-index (`compat.glx-runtime`,
pulled in transitively by `compat.glfw` on Linux), not bundled by this package.

## Quick Start

```bash
Expand Down Expand Up @@ -86,7 +105,24 @@ mcpp run
imgui = "0.0.1"
```

Then import the modules you need:
Then import the modules you need.

Tier-0 (`imgui.app` facade — least code):

```cpp
import imgui.core;
import imgui.app;

int main() {
return ImGui::App::run([] {
ImGui::Begin("hi");
ImGui::TextUnformatted("imgui.app facade");
ImGui::End();
});
}
```

Tier-2 (explicit backend module + alias — full control):

```cpp
import imgui.core;
Expand Down Expand Up @@ -135,6 +171,7 @@ The `0.0.1` release state is verified with mcpp `0.0.44`:
- `mcpp build`
- `mcpp test`
- `cd examples/basic && mcpp run`
- `cd examples/app_minimal && mcpp build`
- `cd examples/minimal_window && mcpp build`
- `cd examples/glfw_opengl3 && mcpp build`

Expand Down
19 changes: 19 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ upstream source ownership in compat packages.

## Goals

- Offer a three-tier consumer UX:
- Tier-0 `imgui.app` — convenience facade owning the full lifecycle.
- Tier-1 automatic via the `imgui.backend` contract (`BackendApi` concept).
- Tier-2 explicit `imgui.backend.<impl>` module + alias.
- Let consumers use explicit module imports:
- `import imgui.app;` (Tier-0 facade; `export import`s `imgui.core`)
- `import imgui.core;`
- `import imgui.backend;` (generic abstraction layer: shared types + contract)
- `import imgui.backend.platform.glfw;`
Expand All @@ -27,6 +32,7 @@ upstream source ownership in compat packages.
|-- mcpp.toml
|-- src/
| |-- core.cppm
| |-- app.cppm (Tier-0 imgui.app facade)
| `-- backends/
| |-- backend.cppm (abstraction layer: types + BackendApi)
| |-- platform_glfw.cppm (GLFW platform piece)
Expand All @@ -39,6 +45,7 @@ upstream source ownership in compat packages.
| `-- backend_test.cpp
`-- examples/
|-- basic/
|-- app_minimal/
|-- minimal_window/
`-- glfw_opengl3/
```
Expand All @@ -52,6 +59,7 @@ backend implementation translation units, and depends on compat packages:
[build]
sources = [
"src/core.cppm",
"src/app.cppm",
"src/backends/backend.cppm",
"src/backends/platform_glfw.cppm",
"src/backends/renderer_opengl3.cppm",
Expand All @@ -69,6 +77,10 @@ compat.opengl = "2026.05.31"
The wrapper package does not vendor `third_party/imgui`. Upstream headers,
core sources, and backend files are read from `compat.imgui`.

The package does not pin a toolchain; mcpp resolves the environment/default
toolchain. The GL runtime is closed by mcpp/mcpp-index (`compat.glx-runtime`,
a transitive dependency of `compat.glfw` on Linux), not bundled by this package.

## Module Wrappers

`src/core.cppm` adapts upstream `imgui.h` internally, then exports selected
Expand All @@ -80,6 +92,12 @@ internally and export explicit wrapper functions. The implementation `.cpp`
files compile upstream backend sources from `compat.imgui/backends` so consumers
do not need to copy backend sources into their own package.

`src/app.cppm` (`imgui.app`) is the Tier-0 facade. It `export import`s
`imgui.core` and privately imports `imgui.backend.glfw_opengl3`, exposing
`ImGui::App::run(...)` as an inline header-style template (the UI callback is a
template parameter, so there is no `<functional>` dependency). It drives the
full lifecycle so a consumer supplies only a per-frame UI callback.

## Validation

Primary proof points:
Expand All @@ -88,6 +106,7 @@ Primary proof points:
mcpp build
mcpp test
cd examples/basic && mcpp run
cd ../app_minimal && mcpp build
cd ../minimal_window && mcpp build
cd ../glfw_opengl3 && mcpp build
```
Expand Down
8 changes: 8 additions & 0 deletions examples/app_minimal/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "imgui-app-minimal"
version = "0.1.0"
description = "Tier-0 imgui.app facade consumer"
license = "MIT"

[dependencies]
imgui = { path = "../.." }
11 changes: 11 additions & 0 deletions examples/app_minimal/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import imgui.core;
import imgui.app;

// Tier-0: imgui.app owns the whole lifecycle; we only supply the per-frame UI.
int main() {
return ImGui::App::run([] {
ImGui::Begin("hi");
ImGui::TextUnformatted("imgui.app facade");
ImGui::End();
});
}
Loading
Loading