From da8ee4f7345f14808a05a52ff7fe6dc4c4fe3c67 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Mon, 3 Aug 2026 04:05:08 +0000 Subject: [PATCH 70/71] fix(macos): adapt frontend to upstream engine signature/target changes on rebase Rebasing the vendor series onto upstream 0397f9cf (then ca44f8de, then c51e15ed) moved engine APIs the macOS frontend calls, renamed a library target it links, and re-spelled every engine header and much of crispy/text_shaper's vocabulary; this patch adapts the affected sites so the frontend compiles and links against the new base. Kept as a single trailing patch rather than folded into the originals so the drift is reviewable in one place and the earlier patches stay byte-for-byte what they were before the rebase. - Terminal::sendCharEvent() gained a KeyIdentity second parameter (the Kitty / win32-input keyboard-protocol identity). SessionBridge::bridge_send_char() now passes vtbackend::KeyIdentity{ .unshiftedKey = codepoint, .nativeVirtualKey = 0 }: the bridge only ever receives an already-resolved codepoint (AppKit characterAtIndex:, not a raw key position), so the unshifted key is that codepoint and nativeVirtualKey stays 0 (a Win32 VK code, which no macOS key has -- matching the Qt frontend's discipline). - vtrasterizer::Renderer's constructor gained a PageMargin as its second argument. The three frontend Renderer constructions now pass it, spelled vtrasterizer::PageMargin (fully qualified: both smoke_main.cpp and render_probe.cpp pull in vtbackend:: AND vtrasterizer:: via using-directives, and PageMargin exists in both namespaces -- a bare name is ambiguous). TerminalSession.cpp reuses the file's existing PageMarginPx (the same value it already feeds applyResize()); the probe and smoke executables pass an explicit margin. No behavioural change -- the live margin is still applied via applyResize() after the first resize. - The vtmux library was renamed vtworkspace upstream (src/vtworkspace, target vtworkspace). contour_macos/CMakeLists.txt's link line now names vtworkspace, fixing "ld: library not found for -lvtmux". The frontend uses no vtmux/ vtworkspace symbols directly; it is a transitive engine dependency, so this is a pure link-list rename. - vtbackend::Terminal's constructor gained a crispy::environment const& second parameter (upstream #2044 made the environment an injected interface; the terminal resolves $HOME and $CONTOUR_SYNC_PTY_OUTPUT once, at construction). TerminalSession.cpp now passes crispy::defaultEnvironment() -- the frontend's session is its composition root, so this is the same resolution upstream's main() makes before threading it through the app. The render probe and smoke binaries need no change: they construct via MockTerm, whose own signature is unchanged and which supplies the environment internally. - vtrasterizer::Renderer's constructor gained a text::FontLocator& parameter, after atlasDirectMapping (upstream 5a44dc7e: the shaper's locator is injected rather than fetched from FontLocatorProvider, so a renderer can be built against MockFontLocator). All three frontend construction sites now pass vtrasterizer::createFontLocator(vtrasterizer::FontLocatorEngine::Native) -- the same call upstream's Qt display makes, differing only in that it reads the engine from a config profile the macOS frontend does not have. Native resolves to text::CoreTextLocator on Apple, which is the locator this port ships and the one render_probe.cpp already queried directly for its diagnostic. Spelled fully qualified for the reason PageMargin is: the probe and smoke sources pull in both vtbackend:: and vtrasterizer:: through using-directives. - Upstream #2046 renamed C++ headers .h -> .hpp, re-spelled crispy and text_shaper filenames in CamelCase, and unified type/function naming there. The frontend follows: 20 engine includes now name .hpp (and the two re-spelled ones, text_shaper/CoreTextLocator.hpp and text_shaper/FontLocator.hpp, crispy/Environment.hpp, vtbackend/Primitives.hpp); text::font_description, font_size, font_slant, font_spacing, font_weight, font_path, render_mode and their enumerators take their CamelCase spellings; crispy::lru_capacity and crispy::strong_hashtable_size become crispy::LRUCapacity and crispy::StrongHashtableSize. Our own contour_macos headers keep their .h extension -- upstream does not build this directory, and renaming them would rewrite the file paths of patches 0001-0069. Verified by static analysis only (this port cannot be built on the dev host); the fixed call sites match the new signatures argument-for-argument and the renamed target matches upstream's own src/CMakeLists.txt. --- docs/macos-port.md | 14 ++++---- src/contour_macos/CMakeLists.txt | 2 +- src/contour_macos/GLRenderTarget.h | 6 ++-- src/contour_macos/PixelBuffer.h | 2 +- src/contour_macos/SessionBridge.cpp | 30 +++++++++------- src/contour_macos/SoftwareRenderTarget.h | 6 ++-- src/contour_macos/StubRenderTarget.h | 6 ++-- src/contour_macos/TerminalSession.cpp | 26 ++++++++++---- src/contour_macos/TerminalSession.h | 8 ++--- src/contour_macos/Themes.h | 2 +- src/contour_macos/render_probe.cpp | 46 +++++++++++++----------- src/contour_macos/smoke_main.cpp | 18 +++++----- 12 files changed, 95 insertions(+), 71 deletions(-) diff --git a/docs/macos-port.md b/docs/macos-port.md index bae1880e..ae8a0bd9 100644 --- a/docs/macos-port.md +++ b/docs/macos-port.md @@ -78,7 +78,7 @@ replacing) `src/contour/`. It contains: ## The seam: `vtbackend::Terminal::Events` The host↔engine boundary is **not** contour's `TerminalSession` (which is Qt). It is -`vtbackend::Terminal::Events` (`src/vtbackend/Terminal.h`), a pure-virtual listener +`vtbackend::Terminal::Events` (`src/vtbackend/Terminal.hpp`), a pure-virtual listener that is 100% Qt-free — every method takes plain types (`std::string_view`, `RGBColor`, `LineCount`, …) and almost all have default no-op bodies. The new session object implements it. Relevant callbacks include: `screenUpdated`, @@ -93,15 +93,15 @@ settings, now)`. The Terminal owns the Pty and calls back into the session. `CGRenderTarget` implements exactly these (`src/vtrasterizer/`): -- **`RenderTarget`** (`RenderTarget.h`): `setRenderSize`, `renderSize`, `setMargin`, +- **`RenderTarget`** (`RenderTarget.hpp`): `setRenderSize`, `renderSize`, `setMargin`, `textureScheduler()`, `imageScheduler()`, `renderRectangle`, `scheduleScreenshot`, `setScissorRect`/`clearScissorRect` (bottom-left origin!), `execute(now)`, `clearCache`, `readAtlas`, `inspect`, optional `setTextOutline`. -- **`atlas::AtlasBackend`** (`TextureAtlas.h`) — four methods: `atlasSize`, +- **`atlas::AtlasBackend`** (`TextureAtlas.hpp`) — four methods: `atlasSize`, `configureAtlas`, `uploadTile`, `renderTile`. The atlas is a CPU pixel buffer; `uploadTile` blits a bitmap into a tile slot at its pixel offset; `renderTile` composites a tile sub-rect onto the target surface. -- **`atlas::ImageTextureBackend`** (`ImageTextureBackend.h`) — whole-image textures +- **`atlas::ImageTextureBackend`** (`ImageTextureBackend.hpp`) — whole-image textures for sixel/images: `createImageTexture`, `destroyImageTexture`, `renderImageQuad`, `renderImageGap`, `takeFailedImageTextures`. @@ -177,7 +177,7 @@ rather than reading screen state directly. `vtpty` is already cross-platform (openpty via `` on Apple, `pipe2`→`pipe` fallback, epoll/eventfd/utempter all behind `__linux__`, and the read loop falls back -to `select()` + self-pipe via `crispy::read_selector` on non-Linux). +to `select()` + self-pipe via `crispy::ReadSelector` on non-Linux). The port depends on [macports-legacy-support](https://github.com/macports/macports-legacy-support) (via the MacPorts `legacysupport` PortGroup), which supplies real, tested @@ -188,7 +188,7 @@ mask legacy-support's real close-on-exec support. That leaves one genuine, SDK-independent bug to patch: -- **`strerror_r` return type** — the `ExitStatus` formatter in `Process.h` used the +- **`strerror_r` return type** — the `ExitStatus` formatter in `Process.hpp` used the return value of `strerror_r` as a `char*`. That is only correct for glibc's GNU variant; Darwin, the BSDs, and plain POSIX ship the XSI (`int`-returning) variant, where the message is written into the buffer and the return is a status code. Fixed @@ -220,7 +220,7 @@ The GUI is Objective-C(++) that must compile with modern GCC's ObjC frontend: standalone `` umbrella. Several 10.7+ symbols (`NSFontWeight*`, ARC-style CoreText helpers) are absent on 10.6. -These constraints are exercised by the existing `src/text_shaper/coretext_locator.mm` +These constraints are exercised by the existing `src/text_shaper/CoreTextLocator.mm` plus the reference patches captured from the MacPorts port; note those patches were made to fix *compilation*, and were never run, so their runtime behavior (font-weight mapping, removed fallback-cascade logic) must be re-derived, not trusted. diff --git a/src/contour_macos/CMakeLists.txt b/src/contour_macos/CMakeLists.txt index 1259d932..0d308555 100644 --- a/src/contour_macos/CMakeLists.txt +++ b/src/contour_macos/CMakeLists.txt @@ -43,7 +43,7 @@ endif() add_library(contour_macos STATIC ${_source_files} ${_header_files}) target_include_directories(contour_macos PUBLIC ${PROJECT_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src) -target_link_libraries(contour_macos PUBLIC vtrasterizer vtbackend vtmux text_shaper vtpty crispy::core) +target_link_libraries(contour_macos PUBLIC vtrasterizer vtbackend vtworkspace text_shaper vtpty crispy::core) target_compile_options(contour_macos PRIVATE ${_contour_macos_warn_opts}) # GLRenderTarget calls into the OpenGL framework. Default to the conservative 10.5 GL 2.0 baseline diff --git a/src/contour_macos/GLRenderTarget.h b/src/contour_macos/GLRenderTarget.h index 5a8bb501..3071d425 100644 --- a/src/contour_macos/GLRenderTarget.h +++ b/src/contour_macos/GLRenderTarget.h @@ -1,9 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include -#include -#include +#include +#include +#include #include #include diff --git a/src/contour_macos/PixelBuffer.h b/src/contour_macos/PixelBuffer.h index 81412d64..a6c520b2 100644 --- a/src/contour_macos/PixelBuffer.h +++ b/src/contour_macos/PixelBuffer.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include +#include #include #include diff --git a/src/contour_macos/SessionBridge.cpp b/src/contour_macos/SessionBridge.cpp index 80d6f5cb..b059fda5 100644 --- a/src/contour_macos/SessionBridge.cpp +++ b/src/contour_macos/SessionBridge.cpp @@ -7,9 +7,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include @@ -79,22 +79,22 @@ TerminalSession* bridge_create(int widthPx, { auto fonts = vtrasterizer::FontDescriptions {}; fonts.dpi = text::DPI { font.dpiX, font.dpiY }; - fonts.size = text::font_size { font.sizePt }; + fonts.size = text::FontSize { font.sizePt }; // render_mode has no default initializer, so it value-initializes to enum value 0 = `bitmap` // (1-bit monochrome, no anti-aliasing) — glyphs come out aliased. The Qt frontend defaults to // `gray`; do the same. gray (grayscale AA) is the right pick here: lcd/light are subpixel modes // for LCD panels, and FreeType's LCD filter is often not built in on this toolchain anyway. - fonts.renderMode = text::render_mode::gray; + fonts.renderMode = text::RenderMode::Gray; std::string const family = font.family ? font.family : "Menlo"; - for (text::font_description* fd: { &fonts.regular, &fonts.bold, &fonts.italic, &fonts.boldItalic }) + for (text::FontDescription* fd: { &fonts.regular, &fonts.bold, &fonts.italic, &fonts.boldItalic }) { fd->familyName = family; - fd->spacing = text::font_spacing::mono; + fd->spacing = text::FontSpacing::Mono; } - fonts.bold.weight = text::font_weight::bold; - fonts.italic.slant = text::font_slant::italic; - fonts.boldItalic.weight = text::font_weight::bold; - fonts.boldItalic.slant = text::font_slant::italic; + fonts.bold.weight = text::FontWeight::Bold; + fonts.italic.slant = text::FontSlant::Italic; + fonts.boldItalic.weight = text::FontWeight::Bold; + fonts.boldItalic.slant = text::FontSlant::Italic; auto const surfaceSize = ImageSize { vtbackend::Width::cast_from(widthPx), vtbackend::Height::cast_from(heightPx) }; @@ -245,8 +245,14 @@ void bridge_send_key(TerminalSession* session, int bridgeKey, uint32_t modifiers void bridge_send_char(TerminalSession* session, uint32_t codepoint, uint32_t modifiers) { + // KeyIdentity carries what the platform natively calls this key, for the Kitty/win32-input + // keyboard protocols. This bridge only ever receives an already-resolved codepoint (the AppKit + // view hands us characterAtIndex:, not a raw key position), so the unshifted key IS that + // codepoint, and nativeVirtualKey stays 0 -- it is a Win32 VK code, which no macOS key has. session->terminal().sendCharEvent(static_cast(codepoint), - codepoint, + vtbackend::KeyIdentity { .unshiftedKey = + static_cast(codepoint), + .nativeVirtualKey = 0 }, toKeyboardModifiers(modifiers), vtbackend::KeyboardEventType::Press, std::chrono::steady_clock::now()); diff --git a/src/contour_macos/SoftwareRenderTarget.h b/src/contour_macos/SoftwareRenderTarget.h index 6d58dedb..e7a2ab2f 100644 --- a/src/contour_macos/SoftwareRenderTarget.h +++ b/src/contour_macos/SoftwareRenderTarget.h @@ -3,9 +3,9 @@ #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/src/contour_macos/StubRenderTarget.h b/src/contour_macos/StubRenderTarget.h index f7f38f4b..28bad245 100644 --- a/src/contour_macos/StubRenderTarget.h +++ b/src/contour_macos/StubRenderTarget.h @@ -1,9 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include -#include -#include +#include +#include +#include #include diff --git a/src/contour_macos/TerminalSession.cpp b/src/contour_macos/TerminalSession.cpp index 7772791b..12d1e298 100644 --- a/src/contour_macos/TerminalSession.cpp +++ b/src/contour_macos/TerminalSession.cpp @@ -1,13 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 #include -#include +#include -#include -#include -#include +#include +#include +#include -#include +#include + +#include // ImageIO + CoreGraphics (via ApplicationServices on 10.6) for decoding inline PNG images. This is // a C API, so it is usable from this ordinary-C++ translation unit (no ObjC needed). @@ -228,11 +230,17 @@ TerminalSession::TerminalSession(vtbackend::PageSize pageSize, _surfaceSize { surfaceSize }, _backend { backend }, _renderer { pageSize, + vtrasterizer::PageMargin { PageMarginPx, PageMarginPx, PageMarginPx }, std::move(fontDescriptions), colorPalette, - crispy::strong_hashtable_size { 4096 }, - crispy::lru_capacity { 4000 }, + crispy::StrongHashtableSize { 4096 }, + crispy::LRUCapacity { 4000 }, /* atlasDirectMapping */ true, + // The renderer's shaper resolves font descriptions through this locator, injected + // rather than fetched from the provider singleton. This session is the frontend's + // composition root, so it picks the engine, exactly as the Qt display does from its + // profile; Native is CoreText on Apple, which is what this port ships. + vtrasterizer::createFontLocator(vtrasterizer::FontLocatorEngine::Native), vtrasterizer::Decorator::Underline, vtrasterizer::Decorator::CurlyUnderline } { @@ -266,6 +274,10 @@ TerminalSession::TerminalSession(vtbackend::PageSize pageSize, _terminal = std::make_unique( *this, + // The engine resolves its environment reads ($HOME, $CONTOUR_SYNC_PTY_OUTPUT) at + // construction. This session is the frontend's composition root, so hand it the + // process-wide environment -- the same resolution upstream's main() makes. + crispy::defaultEnvironment(), makePty(_pageSize, ssh, shellOverride, _callbacks.verifySshHostkey), makeSettings(_pageSize), steady_clock::now()); diff --git a/src/contour_macos/TerminalSession.h b/src/contour_macos/TerminalSession.h index 415d74e2..559e2cd2 100644 --- a/src/contour_macos/TerminalSession.h +++ b/src/contour_macos/TerminalSession.h @@ -4,10 +4,10 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include diff --git a/src/contour_macos/Themes.h b/src/contour_macos/Themes.h index 327ac391..2ec478f0 100644 --- a/src/contour_macos/Themes.h +++ b/src/contour_macos/Themes.h @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include +#include #include diff --git a/src/contour_macos/render_probe.cpp b/src/contour_macos/render_probe.cpp index 155b5565..b17f1ce1 100644 --- a/src/contour_macos/render_probe.cpp +++ b/src/contour_macos/render_probe.cpp @@ -11,19 +11,19 @@ #include #if defined(__APPLE__) - #include + #include #endif -#include +#include -#include +#include -#include +#include -#include -#include -#include +#include +#include +#include -#include +#include #include #include @@ -91,33 +91,33 @@ int main(int argc, char** argv) // A real DPI and size: the default dpi {0,0} produced a tiny ~7px cell where per-glyph // advance rounding is fragile (a wide cap rounds to 2 cells and drifts, eating spaces). fonts.dpi = text::DPI { 96, 96 }; - fonts.size = text::font_size { 14.0 }; + fonts.size = text::FontSize { 14.0 }; char const* const family = argc > 2 ? argv[2] : "Menlo"; - for (text::font_description* fd: { &fonts.regular, &fonts.bold, &fonts.italic, &fonts.boldItalic }) + for (text::FontDescription* fd: { &fonts.regular, &fonts.bold, &fonts.italic, &fonts.boldItalic }) { fd->familyName = family; // A terminal is a fixed grid: force monospace advances so the shaper does not apply // proportional kerning (which otherwise makes "mac" too sparse and "OS"/"we" too tight). - fd->spacing = text::font_spacing::mono; + fd->spacing = text::FontSpacing::Mono; } - fonts.regular.weight = text::font_weight::normal; - fonts.bold.weight = text::font_weight::bold; - fonts.italic.slant = text::font_slant::italic; - fonts.boldItalic.weight = text::font_weight::bold; - fonts.boldItalic.slant = text::font_slant::italic; + fonts.regular.weight = text::FontWeight::Normal; + fonts.bold.weight = text::FontWeight::Bold; + fonts.italic.slant = text::FontSlant::Italic; + fonts.boldItalic.weight = text::FontWeight::Bold; + fonts.boldItalic.slant = text::FontSlant::Italic; #if defined(__APPLE__) // Diagnostic: show exactly what the CoreText locator resolves for the regular face, // isolating a locator bug (bad/empty path) from a shaper/FreeType load failure. { - text::coretext_locator locator; + text::CoreTextLocator locator; auto const sources = locator.locate(fonts.regular); std::printf("locator.locate(regular=%s) -> %zu source(s):\n", family, sources.size()); for (auto const& s: sources) { - if (std::holds_alternative(s)) + if (std::holds_alternative(s)) { - auto const& p = std::get(s); + auto const& p = std::get(s); std::printf(" path='%s' collectionIndex=%d\n", p.value.c_str(), p.collectionIndex); } else @@ -128,11 +128,15 @@ int main(int argc, char** argv) #endif auto renderer = Renderer { pageSize, + vtrasterizer::PageMargin { 2, 2, 2 }, fonts, colorPalette, - crispy::strong_hashtable_size { 4096 }, - crispy::lru_capacity { 4000 }, + crispy::StrongHashtableSize { 4096 }, + crispy::LRUCapacity { 4000 }, /* atlasDirectMapping */ true, + // Same locator the diagnostic above queried directly, reached through + // the provider so the renderer and the probe share one instance. + vtrasterizer::createFontLocator(vtrasterizer::FontLocatorEngine::Native), Decorator::Underline, Decorator::CurlyUnderline }; diff --git a/src/contour_macos/smoke_main.cpp b/src/contour_macos/smoke_main.cpp index 84eb4d43..9a4d3cca 100644 --- a/src/contour_macos/smoke_main.cpp +++ b/src/contour_macos/smoke_main.cpp @@ -8,14 +8,14 @@ #include -#include -#include -#include +#include +#include +#include -#include -#include +#include +#include -#include +#include #include @@ -29,11 +29,13 @@ int main() auto const fonts = FontDescriptions {}; auto renderer = Renderer { pageSize, + vtrasterizer::PageMargin { 0, 0, 0 }, fonts, colorPalette, - crispy::strong_hashtable_size { 4096 }, - crispy::lru_capacity { 4000 }, + crispy::StrongHashtableSize { 4096 }, + crispy::LRUCapacity { 4000 }, /* atlasDirectMapping */ true, + vtrasterizer::createFontLocator(vtrasterizer::FontLocatorEngine::Native), Decorator::Underline, Decorator::CurlyUnderline };