Merge cmux Linux embedding ABI v15 into shared fork - #199
Conversation
|
@codex review |
📝 WalkthroughWalkthroughThe change expands the Linux embedding API, adds embedded OpenGL lifecycle handling, improves resource discovery and terminal resize behavior, adds OSC 99 notifications, and introduces a comprehensive Linux embedding smoke test in CI. ChangesEmbedding API and platform contracts
Embedded OpenGL lifecycle
Terminal behavior
Linux embedding validation
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7406ccde99
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (comptime builtin.target.os.tag == .linux) { | ||
| return self.opts.action(self.opts.userdata, c_target, c_action); |
There was a problem hiding this comment.
Pass the actual app handle to Linux action callbacks
On Linux, every action callback receives runtime_config.userdata even though ghostty_runtime_action_cb declares its first argument as ghostty_app_t, and the non-Linux path passes self. A conforming embedder may call APIs such as ghostty_app_userdata(app) on that argument; when userdata is an unrelated host struct, this is treated as an App allocation and can crash or corrupt memory as soon as an action is dispatched. Pass self here, or change and clearly version the public callback contract.
Useful? React with 👍 / 👎.
| const metadata = raw[0..payload_start]; | ||
| const payload = data[payload_start + 1 .. data.len - 1 :0]; | ||
| if (payload.len == 0) return null; | ||
| if (!encoding.isSafeUtf8(payload)) { |
There was a problem hiding this comment.
Decode base64 OSC 99 payloads before displaying them
For a valid OSC 99 message with e=1, this code ignores the encoding metadata and treats the base64 representation as the notification text. Applications using encoded payloads therefore display strings such as SGVsbG8= instead of Hello; decode the payload before UTF-8 validation and command construction.
Useful? React with 👍 / 👎.
| if (std.mem.eql(u8, part, "body")) { | ||
| parser.command = .{ | ||
| .show_desktop_notification = .{ | ||
| .title = parser.kitty_notification_titles.get(id) orelse default_title, | ||
| .body = payload, | ||
| }, | ||
| }; | ||
| return &parser.command; |
There was a problem hiding this comment.
Buffer OSC 99 chunks until the final part
When an OSC 99 sender splits a payload using d=0 followed by a final d=1 chunk, every body chunk reaches this branch and is emitted immediately as a separate notification; split title chunks are similarly replaced rather than concatenated. This breaks long or deliberately chunked notifications, so the parser needs to retain per-ID title/body data and emit only when the completion flag is set.
Useful? React with 👍 / 👎.
| parser.kitty_notification_titles.put(alloc, id, payload) catch |err| { | ||
| log.warn("failed to store OSC 99 notification title err={}", .{err}); | ||
| }; |
There was a problem hiding this comment.
Retire stored OSC 99 titles after notification completion
Each p=title message with a new i value allocates a permanent key and title in kitty_notification_titles; entries are only freed when the entire parser is destroyed, and processing the matching body never removes them. A long-running terminal application that uses unique notification IDs therefore grows memory without bound, so completed entries should be removed or the pending-title store should be bounded.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/renderer/OpenGL.zig (1)
807-844: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winThread-scoped context ownership is asymmetric with the per-frame bracket.
threadEntercallsembeddedLinuxMakeCurrentbut does not setembedded_linux_context_current.drawFrameEndthen callsendEmbeddedLinuxContext, which callsdone_currentand releases the context the renderer thread acquired.threadExitcallsdone_currentagain. This path is inactive whilemust_draw_from_app_threadis true, so it is latent today, but it breaks if that flag ever becomes false for a Linux embedder.Record thread-level ownership in the same flag, or drop the
threadEnteracquisition and rely only on the per-frame bracket.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/OpenGL.zig` around lines 807 - 844, Make thread-scoped embedded Linux context ownership consistent between threadEnter, drawFrameEnd, and threadExit. In the embedded Linux branch of threadEnter, update embedded_linux_context_current when embeddedLinuxMakeCurrent succeeds, and ensure cleanup clears it so drawFrameEnd does not release thread-owned context and threadExit does not call done_current twice; alternatively remove the threadEnter acquisition and rely solely on the existing per-frame context bracket..github/workflows/test.yml (1)
771-815: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winAdd
permissions: contents: readtobuild-linux-libghostty.The workflow has no top-level permissions block, and this job has no job-level permissions block.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/test.yml around lines 771 - 815, Add a job-level permissions block to build-linux-libghostty granting contents: read, without changing the existing steps or permissions for other jobs.Source: Linters/SAST tools
🧹 Nitpick comments (8)
src/terminal/osc/parsers/kitty_notification.zig (1)
147-224: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd tests for the untested OSC 99 paths.
The current tests are correct and leak-safe. Coverage is missing for the cases that carry the most risk:
- metadata present without a
pkey (see the comment on lines 103-135)e=1Base64 payloads- empty
i=value, which maps to thedefaultid- a metadata
p=titlechunk parsed withParser.init(null), which must not crash and must log the missing allocator- the empty payload path
99;i=x:p=body;Add these together with the spec fixes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/terminal/osc/parsers/kitty_notification.zig` around lines 147 - 224, Extend the OSC 99 parser tests and implementation to cover metadata without a p key, e=1 Base64 payloads, empty i= mapping to the default id, empty body payloads, and title chunks received with Parser.init(null). Ensure the null-allocator path does not crash and records the missing allocator, while preserving unsafe-payload rejection and existing chunk pairing behavior. Add regression tests using Parser.init, reset, and end for each case.include/ghostty.h (1)
1680-1685: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSplit the whole declaration instead of splitting the parameter list.
The
#ifblock currently divides one function declaration. The opening line ends with(ghostty_surface_t,and the parameter list continues after#endif. This compiles, but any later edit to the parameter list must be applied to a single shared tail. Declare each variant completely inside its branch, as the surrounding code already does forghostty_surface_textandghostty_surface_preedit.♻️ Proposed readability fix
`#if` defined(__linux__) -GHOSTTY_API bool ghostty_surface_set_color_scheme(ghostty_surface_t, +GHOSTTY_API bool ghostty_surface_set_color_scheme(ghostty_surface_t, + ghostty_color_scheme_e); `#else` -GHOSTTY_API void ghostty_surface_set_color_scheme(ghostty_surface_t, +GHOSTTY_API void ghostty_surface_set_color_scheme(ghostty_surface_t, + ghostty_color_scheme_e); `#endif` - ghostty_color_scheme_e);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@include/ghostty.h` around lines 1680 - 1685, Update the ghostty_surface_set_color_scheme declaration so each __linux__ and non-Linux preprocessor branch contains its complete function signature, including the ghostty_surface_t and ghostty_color_scheme_e parameters, rather than sharing a parameter-list tail after `#endif`. Match the branch structure used by ghostty_surface_text and ghostty_surface_preedit.build.zig (1)
204-206: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the unused
ghostty-internal.soinstallation.
ghostty-internal.pcreferenceslibghostty-internal.so, and the repository has no consumer forghostty-internal.so. Unless an external embedder requires this exact filename, removelib_shared.installLibraryFile("ghostty-internal.so"). The static-library installation remains required byghostty-internal-static.pc.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@build.zig` around lines 204 - 206, Remove the lib_shared.installLibraryFile call for “ghostty-internal.so” from the installation block, while preserving both lib_static installation calls and the existing static-library packaging behavior.src/terminal/Terminal.zig (1)
3981-3981: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd regression coverage for prompt-history preservation.
Line 3981 changes resize behavior, but no test exercises
.preserve_prompt_history = trueor compares prompt history after reflow. Add paired true/false cases to protect theTerminal.resizetoScreen.resizecontract.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/terminal/Terminal.zig` at line 3981, Add regression tests around Terminal.resize and Screen.resize that cover opts.preserve_prompt_history set to both true and false, then compare prompt history after reflow to verify the flag is propagated and behavior differs as expected. Keep the cases paired and preserve existing resize assertions.src/termio/Exec.zig (1)
2047-2076: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftExercise the no-
POLLINPTY path.This test uses a pipe and reads it directly. It does not assert that the hangup event lacks
POLLIN, and it does not executegatherMainPosix. The test can therefore pass without covering the new drain branches at Line 1913 and Line 1989. Add a Linux PTY integration test that verifies tail data reaches the gather pipeline before EOF.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/termio/Exec.zig` around lines 2047 - 2076, The test “pty hangup keeps pending input readable” does not exercise the no-POLLIN PTY handling in gatherMainPosix. Replace the pipe/direct-read setup with a Linux PTY integration scenario that closes the peer after writing marker data, produces a hangup event without POLLIN, and runs gatherMainPosix through its normal pipeline; assert the marker is delivered before EOF and the relevant drain behavior is covered.test/linux/test_libghostty_embedding.c (2)
87-93: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueUse
<=for the exact-capacity case.
len < sizeof(probe->bytes)truncates a payload whose length equals the buffer size, even though the copy would fit. Uselen <= sizeof(probe->bytes).♻️ Proposed change
- const size_t copied = len < sizeof(probe->bytes) ? (size_t)len : sizeof(probe->bytes); + const size_t copied = + (size_t)len <= sizeof(probe->bytes) ? (size_t)len : sizeof(probe->bytes);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/linux/test_libghostty_embedding.c` around lines 87 - 93, Update the copied-length calculation in manual_io_write to use <= so payloads exactly equal to sizeof(probe->bytes) are copied in full, while preserving truncation for larger payloads.
689-1087: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffConsolidate the repeated teardown sequence.
The same five-call cleanup block (
ghostty_surface_free,deinit_gl_context,deinit_gl_runtime,ghostty_app_free,ghostty_config_free) is duplicated at more than a dozen failure sites. A singlegoto cleanuptarget with nullable handles removes the duplication and prevents a future failure branch from forgetting one release.verify_concurrent_surfacesalready uses this pattern.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/linux/test_libghostty_embedding.c` around lines 689 - 1087, Consolidate the duplicated failure cleanup in the test function containing the surface lifecycle checks by adding one cleanup label that conditionally releases the surface, GL context/runtime, app, and config handles. Replace each repeated five-call teardown block with assignments as needed followed by goto cleanup, while preserving failure return values and ensuring nullable handles are safe; follow the existing pattern used by verify_concurrent_surfaces..github/workflows/test.yml (1)
797-815: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd a run timeout to the embedding smoke test.
The test polls with bounded retry loops, but a hung
ghostty_app_tickor a stalled child process leaves the step running until the job-level default timeout. Wrap the binary intimeoutso a hang fails fast.♻️ Proposed change
EGL_PLATFORM=surfaceless LIBGL_ALWAYS_SOFTWARE=1 \ LD_LIBRARY_PATH="$PWD/zig-out/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \ - /tmp/test_libghostty_embedding + timeout 300 /tmp/test_libghostty_embedding🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/test.yml around lines 797 - 815, Wrap the `/tmp/test_libghostty_embedding` invocation in the “Test installed Linux embedding contract” step with a finite `timeout` command, preserving the existing EGL and `LD_LIBRARY_PATH` environment variables so hangs in `ghostty_app_tick` or child processes fail promptly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pkg/dcimgui/ext.cpp`:
- Around line 77-80: Update the imgl3wInit() failure path in the backend
initialization flow so it does not zero imgl3wProcs while any tracked GTK or
embedded backend context remains active; either defer clearing until all tracked
contexts are inactive or prevent every remaining context from reaching
ImGui_ImplOpenGL3_RenderDrawData().
In `@src/build/GhosttyResources.zig`:
- Around line 447-466: The embedder path still installs all resources because
the app_runtime == .none branch calls install() instead of installRuntime().
Update that branch in the build configuration to call
GhosttyResources.installRuntime(), preserving the full-resource install path for
standalone applications.
In `@src/config/CApi.zig`:
- Around line 180-190: Restore nullable handle parameters on the non-Linux
exports by changing self from *Config to ?*Config in
ghostty_config_load_string_non_linux, ghostty_config_load_cli_args_non_linux,
ghostty_config_load_file_non_linux, and
ghostty_config_load_default_files_non_linux. Preserve their existing forwarding
behavior so null handles are rejected by the underlying functions consistently
with the Linux exports.
- Around line 144-162: Update the Linux configuration loading path around
ghostty_config_load_string and its loadIter call to apply the same relative-path
expansion used by loadReader/loadString for Path and RepeatablePath values,
preserving expansion for config-file, background-image, and custom-shader
without changing other parsing behavior.
In `@src/os/resourcesdir.zig`:
- Around line 201-226: Update the dladdr call in selfSharedObjectPath to pass
`@ptrCast`(&resourcesDir) instead of the function value directly, preserving the
existing comptime platform guard and all other path handling.
In `@src/renderer/image.zig`:
- Around line 1040-1072: Change all three test-local image declarations in image
context loss cleanup test from var image to const image, since
deinitAfterContextLost consumes Image by value without mutating the locals.
In `@src/renderer/OpenGL.zig`:
- Around line 244-250: Update embeddedLinuxGetProcAddress to apply `@alignCast` to
proc before the existing `@ptrCast` conversion, returning the aligned function
pointer while preserving the current null handling.
In `@src/terminal/osc/parsers/kitty_notification.zig`:
- Around line 14-15: Update the default_title constant in the OSC 99
notification parser to remove the downstream product name, using a neutral
upstream title such as “Ghostty” or an empty value so the apprt can fall back to
the surface title.
- Around line 32-56: Bound TitleMap.put by enforcing a fixed maximum entry count
and evicting the oldest stored title before adding a new distinct id, while
preserving replacement behavior for existing ids. Update the matching p=body
consumption path to remove and free the title once used, and ensure Parser.reset
does not retain entries indefinitely.
- Around line 103-135: Update the OSC 99 parsing flow around optionValue
metadata handling: default missing `p` to `title` instead of returning null, and
when `e=1` Base64-decode the payload before storing or displaying it. Use `i` as
the notification key and honor `d` to buffer fragments, assembling all chunks
before emitting the notification while preserving title/body handling in
`kitty_notification_titles` and `parser.command`.
---
Outside diff comments:
In @.github/workflows/test.yml:
- Around line 771-815: Add a job-level permissions block to
build-linux-libghostty granting contents: read, without changing the existing
steps or permissions for other jobs.
In `@src/renderer/OpenGL.zig`:
- Around line 807-844: Make thread-scoped embedded Linux context ownership
consistent between threadEnter, drawFrameEnd, and threadExit. In the embedded
Linux branch of threadEnter, update embedded_linux_context_current when
embeddedLinuxMakeCurrent succeeds, and ensure cleanup clears it so drawFrameEnd
does not release thread-owned context and threadExit does not call done_current
twice; alternatively remove the threadEnter acquisition and rely solely on the
existing per-frame context bracket.
---
Nitpick comments:
In @.github/workflows/test.yml:
- Around line 797-815: Wrap the `/tmp/test_libghostty_embedding` invocation in
the “Test installed Linux embedding contract” step with a finite `timeout`
command, preserving the existing EGL and `LD_LIBRARY_PATH` environment variables
so hangs in `ghostty_app_tick` or child processes fail promptly.
In `@build.zig`:
- Around line 204-206: Remove the lib_shared.installLibraryFile call for
“ghostty-internal.so” from the installation block, while preserving both
lib_static installation calls and the existing static-library packaging
behavior.
In `@include/ghostty.h`:
- Around line 1680-1685: Update the ghostty_surface_set_color_scheme declaration
so each __linux__ and non-Linux preprocessor branch contains its complete
function signature, including the ghostty_surface_t and ghostty_color_scheme_e
parameters, rather than sharing a parameter-list tail after `#endif`. Match the
branch structure used by ghostty_surface_text and ghostty_surface_preedit.
In `@src/terminal/osc/parsers/kitty_notification.zig`:
- Around line 147-224: Extend the OSC 99 parser tests and implementation to
cover metadata without a p key, e=1 Base64 payloads, empty i= mapping to the
default id, empty body payloads, and title chunks received with
Parser.init(null). Ensure the null-allocator path does not crash and records the
missing allocator, while preserving unsafe-payload rejection and existing chunk
pairing behavior. Add regression tests using Parser.init, reset, and end for
each case.
In `@src/terminal/Terminal.zig`:
- Line 3981: Add regression tests around Terminal.resize and Screen.resize that
cover opts.preserve_prompt_history set to both true and false, then compare
prompt history after reflow to verify the flag is propagated and behavior
differs as expected. Keep the cases paired and preserve existing resize
assertions.
In `@src/termio/Exec.zig`:
- Around line 2047-2076: The test “pty hangup keeps pending input readable” does
not exercise the no-POLLIN PTY handling in gatherMainPosix. Replace the
pipe/direct-read setup with a Linux PTY integration scenario that closes the
peer after writing marker data, produces a hangup event without POLLIN, and runs
gatherMainPosix through its normal pipeline; assert the marker is delivered
before EOF and the relevant drain behavior is covered.
In `@test/linux/test_libghostty_embedding.c`:
- Around line 87-93: Update the copied-length calculation in manual_io_write to
use <= so payloads exactly equal to sizeof(probe->bytes) are copied in full,
while preserving truncation for larger payloads.
- Around line 689-1087: Consolidate the duplicated failure cleanup in the test
function containing the surface lifecycle checks by adding one cleanup label
that conditionally releases the surface, GL context/runtime, app, and config
handles. Replace each repeated five-call teardown block with assignments as
needed followed by goto cleanup, while preserving failure return values and
ensuring nullable handles are safe; follow the existing pattern used by
verify_concurrent_surfaces.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: bcb4ce81-9ed3-40fe-9eb9-61e8975c834d
📒 Files selected for processing (41)
.github/workflows/test.ymlbuild.ziginclude/ghostty.hmacos/Sources/Ghostty/Ghostty.App.swiftmacos/Sources/Ghostty/Ghostty.Config.swiftmacos/Sources/Ghostty/Ghostty.Inspector.swiftmacos/Sources/Ghostty/Surface View/SurfaceView.swiftmacos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swiftnix/devShell.nixpkg/dcimgui/ext.cpppkg/dcimgui/main.zigsrc/App.zigsrc/Surface.zigsrc/apprt.zigsrc/apprt/action.zigsrc/apprt/embedded.zigsrc/apprt/gtk/class/imgui_widget.zigsrc/apprt/gtk/class/surface.zigsrc/apprt/structs.zigsrc/apprt/surface.zigsrc/build/GhosttyLib.zigsrc/build/GhosttyResources.zigsrc/config/CApi.zigsrc/main_c.zigsrc/os/resourcesdir.zigsrc/renderer/Metal.zigsrc/renderer/OpenGL.zigsrc/renderer/Thread.zigsrc/renderer/generic.zigsrc/renderer/image.zigsrc/renderer/opengl/shaders.zigsrc/terminal/Screen.zigsrc/terminal/Terminal.zigsrc/terminal/osc.zigsrc/terminal/osc/parsers.zigsrc/terminal/osc/parsers/kitty_notification.zigsrc/termio/Exec.zigsrc/termio/Termio.zigsrc/termio/Thread.zigsrc/termio/message.zigtest/linux/test_libghostty_embedding.c
| if (imgl3wInit() != GL3W_OK) | ||
| { | ||
| memset(&imgl3wProcs, 0, sizeof(imgl3wProcs)); | ||
| return false; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline pkg/dcimgui/ext.cpp --items all
rg -n -C 5 \
'ImGui_ImplOpenGL3_(InitWithLoaderTracking|ShutdownWithLoaderTracking|AbandonLoaderTracking)|imgl3w(Init|Shutdown)|imgl3wProcs' \
pkg/dcimgui src
fd -t f -g 'imgui_impl_opengl3*' . \
-x rg -n -C 5 'ImGui_ImplOpenGL3_Shutdown|imgl3wShutdown|imgl3wInit' {}Repository: manaflow-ai/ghostty
Length of output: 9982
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- ext.cpp ---'
cat -n pkg/dcimgui/ext.cpp | sed -n '38,105p'
printf '%s\n' '--- loader/backend files ---'
fd -t f -i 'imgui_impl_opengl3*' . -x sh -c '
echo "### $1"
rg -n -C 4 "imgl3w(Init|Shutdown)|imgl3wProcs|ImGui_ImplOpenGL3_(NewFrame|RenderDrawData|Shutdown|Init)" "$1" || true
' sh {}
printf '%s\n' '--- all tracked-wrapper callers ---'
rg -n -C 8 'ImGui_ImplOpenGL3_(ShutdownWithLoaderTracking|AbandonLoaderTracking|InitWithLoaderTracking)' pkg src
printf '%s\n' '--- loader table access patterns ---'
rg -n -C 3 'imgl3wProcs|gl3wProcs|imgl3wInit|imgl3wShutdown' . -g '!build' -g '!dist'Repository: manaflow-ai/ghostty
Length of output: 14674
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- tracked ImGui/backend files ---'
git ls-files | rg -i 'imgui|opengl3|gl3w|dcimgui|build.zig|build.zig.zon' | head -200
printf '%s\n' '--- backend symbols and render call sites ---'
rg -n -C 5 'ImGui_ImplOpenGL3_(NewFrame|RenderDrawData|Shutdown|Init)|imgui_opengl3_backend_users|ImGui_Render|RenderDrawData|NewFrame' pkg src
printf '%s\n' '--- GTK widget lifecycle and rendering ---'
cat -n src/apprt/gtk/class/imgui_widget.zig | sed -n '200,390p'
printf '%s\n' '--- dependency references ---'
rg -n -C 5 'Dear ImGui|imgui_impl_opengl3|imgl3w|cimgui|dcimgui' build.zig build.zig.zon pkg srcRepository: manaflow-ai/ghostty
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
LOG=/tmp/coderabbit-shell-logs/shell-output-ld9Ls3
printf '%s\n' '--- prior command markers ---'
rg -n --text '^---|^###|ImGui_ImplOpenGL3_(NewFrame|RenderDrawData)|imgl3w|opengl3_loader|build.zig.zon|dcimgui' "$LOG" | head -200
printf '%s\n' '--- exact backend symbols in tracked source ---'
rg -n --no-heading \
'ImGui_ImplOpenGL3_(NewFrame|RenderDrawData|Shutdown|Init)|imgl3w(Init|Shutdown|Procs)' \
pkg src build.zig build.zig.zon 2>/dev/null | head -300
printf '%s\n' '--- dependency declarations ---'
rg -n --no-heading -C 4 \
'imgui|cimgui|dcimgui|opengl3' \
build.zig build.zig.zon pkg/dcimgui 2>/dev/null | head -300
printf '%s\n' '--- likely frame/render functions ---'
rg -n --no-heading -C 4 \
'ImGui_(NewFrame|Render|EndFrame|GetDrawData)|RenderDrawData|backend.*NewFrame|newFrame' \
src/apprt/gtk src/apprt/embedded.zig src/inspector 2>/dev/null | head -400Repository: manaflow-ai/ghostty
Length of output: 33742
🌐 Web query:
Dear ImGui v1.92.5 docking imgui_impl_opengl3_loader.h imgl3wProcs imgl3wInit source
💡 Result:
In Dear ImGui v1.92.5, the file imgui_impl_opengl3_loader.h is a minimal, embedded OpenGL loader used by the imgui_impl_opengl3 backend to manage OpenGL function pointers [1][2]. It is a stripped-down version of the gl3w loader, specifically generated to include only the OpenGL symbols required by the Dear ImGui backend, ensuring a lightweight and bloat-free integration [1][3]. Key components referenced in your query include: imgl3wInit: This function is the primary entry point for initializing the loader [3]. It must be called once after a valid OpenGL context has been created to populate the function pointers [3]. The implementation internally calls imgl3wInit2, which loads the required procedures using a platform-specific get_proc function [4][5]. imgl3wProcs: This is an internal global union (union ImGL3WProcs) that stores the loaded OpenGL function pointers [5][6]. The header uses macro definitions (e.g., #define glActiveTexture imgl3wProcs.gl.ActiveTexture) to map standard OpenGL function calls to the pointers stored within this structure [1][6]. The symbol was specifically named imgl3wProcs (rather than the original gl3wProcs) to avoid namespace conflicts when other loaders are used in the same project [1]. Usage: The Dear ImGui OpenGL3 backend (imgui_impl_opengl3.cpp) automatically includes and utilizes this loader when IMGUI_IMPL_OPENGL_LOADER_IMGL3W is defined [2]. Users typically do not need to interact with imgl3wInit or imgl3wProcs directly, as these are handled internally by ImGui_ImplOpenGL3_Init [2]. If you are developing a custom backend or encountering issues (such as initialization failures in complex environments like DLLs), you may refer to the imgui_impl_opengl3_loader.h source in the backends/ directory [7][2]. The loader's source code is generated by the gl3w_stripped tool, which scans imgui_impl_opengl3.cpp to determine exactly which symbols are needed [1][3]. If you modify the backend and require additional OpenGL functions, you would need to regenerate this header file using the gl3w_gen.py script provided in the dearimgui/gl3w_stripped repository [2][3].
Citations:
- 1: OpenGL backend now embeds its own GL loader (imgui_impl_opengl3_loader.h) ocornut/imgui#4445
- 2: https://github.com/ocornut/imgui/blob/0ff81003/backends/imgui_impl_opengl3.cpp
- 3: https://github.com/dearimgui/gl3w_stripped
- 4: https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_opengl3_loader.h
- 5: https://skia.googlesource.com/external/github.com/ocornut/imgui/+/refs/heads/docking/backends/imgui_impl_opengl3_loader.h
- 6: https://codebrowser.dev/imgui/imgui/backends/imgui_impl_opengl3_loader.h.html
- 7: SIGSEGV when calling OpenGL function from DLL ocornut/imgui#8406
Keep the loader valid while tracked contexts remain.
If imgl3wInit() fails, clear imgl3wProcs only after all tracked backends are inactive. The GTK and embedded render paths can still call ImGui_ImplOpenGL3_RenderDrawData(), which then dereferences the zeroed function pointers. Preserve the loader or block every remaining context from rendering.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pkg/dcimgui/ext.cpp` around lines 77 - 80, Update the imgl3wInit() failure
path in the backend initialization flow so it does not zero imgl3wProcs while
any tracked GTK or embedded backend context remains active; either defer
clearing until all tracked contexts are inactive or prevent every remaining
context from reaching ImGui_ImplOpenGL3_RenderDrawData().
| /// Install only resources needed by embedders that host the terminal runtime | ||
| /// without installing Ghostty's standalone desktop application. | ||
| pub fn installRuntime(self: *const GhosttyResources) void { | ||
| const b = self.steps[0].owner; | ||
| self.addRuntimeStepDependencies(b.getInstallStep()); | ||
| } | ||
|
|
||
| pub fn addStepDependencies( | ||
| self: *const GhosttyResources, | ||
| other_step: *std.Build.Step, | ||
| ) void { | ||
| for (self.steps) |step| other_step.dependOn(step); | ||
| } | ||
|
|
||
| pub fn addRuntimeStepDependencies( | ||
| self: *const GhosttyResources, | ||
| other_step: *std.Build.Step, | ||
| ) void { | ||
| for (self.runtime_steps) |step| other_step.dependOn(step); | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find callers of the new runtime resource APIs.
set -euo pipefail
rg -n 'installRuntime|addRuntimeStepDependencies|runtime_steps' . -g '*.zig' -g '!zig-cache' -g '!zig-out'Repository: manaflow-ai/ghostty
Length of output: 1280
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- build.zig resource installation call sites ---'
rg -n -C 12 'resources\.(install|installRuntime|addRuntimeStepDependencies)|app_runtime' build.zig '*.zig' 2>/dev/null || true
printf '%s\n' '--- GhosttyResources API and resource classification ---'
sed -n '1,175p' src/build/GhosttyResources.zig
sed -n '245,275p' src/build/GhosttyResources.zig
sed -n '430,470p' src/build/GhosttyResources.zig
printf '%s\n' '--- all runtime API references, excluding generated directories ---'
rg -n 'installRuntime|addRuntimeStepDependencies|runtime_steps' . \
-g '*.zig' -g '!zig-cache' -g '!zig-out'Repository: manaflow-ai/ghostty
Length of output: 16953
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- build configuration and branch definitions ---'
rg -n -C 8 'app_runtime|emit_lib_vt|libghostty|GhosttyResources' build.zig src/build -g '*.zig'
printf '%s\n' '--- resource and library install APIs ---'
rg -n -C 10 'pub fn (install|installRuntime)|resources\.install|addRuntimeStepDependencies' . \
-g '*.zig' -g '!zig-cache' -g '!zig-out'Repository: manaflow-ai/ghostty
Length of output: 50375
Connect the runtime-only resource path. No code calls installRuntime or addRuntimeStepDependencies. The app_runtime == .none branch at build.zig:209 still calls resources.install(), which installs every resource step. Use resources.installRuntime() for the embedder path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/build/GhosttyResources.zig` around lines 447 - 466, The embedder path
still installs all resources because the app_runtime == .none branch calls
install() instead of installRuntime(). Update that branch in the build
configuration to call GhosttyResources.installRuntime(), preserving the
full-resource install path for standalone applications.
| pub fn ghostty_config_load_string( | ||
| self_: ?*Config, | ||
| str_: ?[*]const u8, | ||
| len: usize, | ||
| ) callconv(.c) bool { | ||
| const self = configHandle(self_, "ghostty_config_load_string") orelse return false; | ||
| const str = bytesForLength(str_, len, "ghostty_config_load_string") orelse return false; | ||
|
|
||
| var reader: std.Io.Reader = .fixed(str); | ||
| var iter: cli.args.LineIterator = .{ | ||
| .r = &reader, | ||
| .filepath = "<ghostty_config_load_string>", | ||
| }; | ||
| self.loadIter(global.alloc(), &iter) catch |err| { | ||
| log.err("error loading config from string err={}", .{err}); | ||
| return false; | ||
| }; | ||
| return true; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Compare loadString and loadIter behavior in Config.zig.
set -euo pipefail
fd -t f 'Config.zig' src/config --exec ast-grep outline {} --items all --match 'loadString|loadIter|loadFile|expandPaths|replacePaths'
fd -t f 'Config.zig' src/config --exec rg -n -C 12 'pub fn loadString\(|pub fn loadIter\('Repository: manaflow-ai/ghostty
Length of output: 2494
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- CApi entry points ---'
rg -n -C 24 'ghostty_config_load_string|loadString|loadIter|LineIterator' src/config/CApi.zig include/ghostty.h
echo '--- Config loading and path resolution ---'
rg -n -C 18 'pub fn loadIter|pub fn loadString|fn loadReader|filepath|resolve relative|relative config|config-file|background-image|command' src/config/Config.zig src/cli src
echo '--- LineIterator definition and parser call sites ---'
rg -n -C 24 'const LineIterator|pub const LineIterator|LineIterator|filepath' src
echo '--- Relevant tests ---'
rg -n -C 16 'loadString|loadIter|relative.*path|background-image|config-file|command' src/config src/cli tests 2>/dev/null || trueRepository: manaflow-ai/ghostty
Length of output: 50378
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- CApi implementation ---'
rg -n -C 18 'ghostty_config_load_string|ghostty_config_load_file|bytesForLength|loadString|loadIter' src/config/CApi.zig
echo '--- Config loader implementation ---'
sed -n '3970,4125p' src/config/Config.zig
echo '--- Path-bearing config value types and loaders ---'
rg -n -C 14 'const .*Path|RepeatablePath|resolve.*path|path.*resolve|loadFile|config-file|background-image|cwd|filepath' src/config/Config.zig src/config src/cli/args.zig
echo '--- LineIterator and filepath consumers ---'
rg -n -C 18 'LineIterator|filepath' src/cli src/config --glob '*.zig'Repository: manaflow-ai/ghostty
Length of output: 50378
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- expandPaths implementation ---'
rg -n -C 30 'fn expandPaths|expandPaths\(' src/config/Config.zig
echo '--- path.zig ---'
cat -n src/config/path.zig
echo '--- path-valued fields and use sites ---'
rg -n -C 10 'Path|RepeatablePath|expandPaths|config-file|background-image|custom-shader|theme' src/config/Config.zig src/config src --glob '*.zig' \
| rg -v 'background-image-opacity|background-image-position|background-image-fit|background-image-repeat|Config.zig:[0-9]+-///'
echo '--- C API declarations and documentation ---'
rg -n -C 12 'ghostty_config_load_string|load_string' include/ghostty.h src --glob '*.h' --glob '*.zig'Repository: manaflow-ai/ghostty
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- Config path expansion ---'
rg -n -C 24 'fn expandPaths|pub fn loadRecursiveFiles|loadRecursiveFiles\(' src/config/Config.zig
echo '--- Path type behavior ---'
cat -n src/config/path.zig
echo '--- Header contract ---'
rg -n -C 20 'ghostty_config_load_string' include/ghostty.hRepository: manaflow-ai/ghostty
Length of output: 30900
Preserve relative path expansion on Linux.
loadReader expands all Path and RepeatablePath values. The Linux entry point calls loadIter directly, so relative values such as config-file, background-image, and custom-shader remain unexpanded. Use the same expansion as loadString, or document the Linux-specific behavior in include/ghostty.h.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/config/CApi.zig` around lines 144 - 162, Update the Linux configuration
loading path around ghostty_config_load_string and its loadIter call to apply
the same relative-path expansion used by loadReader/loadString for Path and
RepeatablePath values, preserving expansion for config-file, background-image,
and custom-shader without changing other parsing behavior.
| fn ghostty_config_load_cli_args_non_linux(self: *Config) callconv(.c) void { | ||
| _ = ghostty_config_load_cli_args(self); | ||
| } | ||
|
|
||
| fn ghostty_config_load_file_non_linux(self: *Config, path: [*:0]const u8) callconv(.c) void { | ||
| _ = ghostty_config_load_file(self, path); | ||
| } | ||
|
|
||
| fn ghostty_config_load_default_files_non_linux(self: *Config) callconv(.c) void { | ||
| _ = ghostty_config_load_default_files(self); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Restore null-handle safety on the non-Linux exports.
The Linux exports accept ?*Config and reject a null handle. The non-Linux wrappers declare self: *Config. A C caller that passes NULL therefore produces illegal behavior on macOS and Windows instead of the logged rejection that Linux now returns. The wrappers already forward to functions that accept ?*Config, so the guard costs nothing.
Apply the same change to ghostty_config_load_string_non_linux at Line 167.
🛡️ Proposed fix
-fn ghostty_config_load_cli_args_non_linux(self: *Config) callconv(.c) void {
+fn ghostty_config_load_cli_args_non_linux(self: ?*Config) callconv(.c) void {
_ = ghostty_config_load_cli_args(self);
}
-fn ghostty_config_load_file_non_linux(self: *Config, path: [*:0]const u8) callconv(.c) void {
+fn ghostty_config_load_file_non_linux(self: ?*Config, path: ?[*:0]const u8) callconv(.c) void {
_ = ghostty_config_load_file(self, path);
}
-fn ghostty_config_load_default_files_non_linux(self: *Config) callconv(.c) void {
+fn ghostty_config_load_default_files_non_linux(self: ?*Config) callconv(.c) void {
_ = ghostty_config_load_default_files(self);
}-fn ghostty_config_load_recursive_files_non_linux(self: *Config) callconv(.c) void {
+fn ghostty_config_load_recursive_files_non_linux(self: ?*Config) callconv(.c) void {
_ = ghostty_config_load_recursive_files(self);
}
-fn ghostty_config_finalize_non_linux(self: *Config) callconv(.c) void {
+fn ghostty_config_finalize_non_linux(self: ?*Config) callconv(.c) void {
_ = ghostty_config_finalize(self);
}Also applies to: 213-219
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/config/CApi.zig` around lines 180 - 190, Restore nullable handle
parameters on the non-Linux exports by changing self from *Config to ?*Config in
ghostty_config_load_string_non_linux, ghostty_config_load_cli_args_non_linux,
ghostty_config_load_file_non_linux, and
ghostty_config_load_default_files_non_linux. Preserve their existing forwarding
behavior so null handles are rejected by the underlying functions consistently
with the Linux exports.
| fn selfSharedObjectPath(buf: []u8) ?[]const u8 { | ||
| if (comptime builtin.target.os.tag != .linux or !builtin.link_libc) return null; | ||
|
|
||
| var info: DlInfo = undefined; | ||
| if (dladdr(resourcesDir, &info) == 0) return null; | ||
| const raw = info.dli_fname orelse return null; | ||
| const path = std.mem.span(raw); | ||
| if (path.len == 0) return null; | ||
|
|
||
| if (std.fs.path.isAbsolute(path)) { | ||
| return std.fmt.bufPrint(buf, "{s}", .{path}) catch null; | ||
| } | ||
|
|
||
| const len = std.Io.Dir.cwd().realPathFile(global.io(), path, buf) catch | ||
| return null; | ||
| return buf[0..len]; | ||
| } | ||
|
|
||
| const DlInfo = extern struct { | ||
| dli_fname: ?[*:0]const u8, | ||
| dli_fbase: ?*anyopaque, | ||
| dli_sname: ?[*:0]const u8, | ||
| dli_saddr: ?*anyopaque, | ||
| }; | ||
|
|
||
| extern "c" fn dladdr(addr: ?*const anyopaque, info: *DlInfo) c_int; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Confirm Windows builds reach this code and check dladdr usage elsewhere.
set -euo pipefail
rg -n -C 4 'windows' build.zig | rg -n -C 4 'ghostty-internal'
rg -n -C 3 'dladdr|dli_fname' src --glob '*.zig'
rg -n -C 3 'resourcesDir\(' src --glob '*.zig'Repository: manaflow-ai/ghostty
Length of output: 6390
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- resourcesdir.zig imports and target-dependent function ---'
sed -n '1,70p' src/os/resourcesdir.zig
sed -n '185,235p' src/os/resourcesdir.zig
printf '%s\n' '--- build target and source inclusion ---'
sed -n '160,215p' build.zig
rg -n -C 5 'resourcesdir|os/main|src/os' build.zig src/os/main.zig src/os --glob '*.zig'
printf '%s\n' '--- available Zig tooling ---'
command -v zig || true
zig version 2>/dev/null || trueRepository: manaflow-ai/ghostty
Length of output: 11034
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path("src/os/resourcesdir.zig")
text = p.read_text()
start = text.index("fn selfSharedObjectPath")
end = text.index("/// Little helper", start)
snippet = text[start:end]
print("selfSharedObjectPath references:")
for i, line in enumerate(snippet.splitlines(), 1):
if "comptime" in line or "dladdr" in line or "DlInfo" in line or "resourcesDir" in line:
print(f"{i}: {line}")
print("\nExtern declaration is at file/container scope:",
'\nconst DlInfo = extern struct' in snippet or 'const DlInfo = extern struct' in text,
'\nextern "c" fn dladdr' in text)
PYRepository: manaflow-ai/ghostty
Length of output: 538
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,70p' src/os/resourcesdir.zig
sed -n '185,235p' src/os/resourcesdir.zig
sed -n '160,215p' build.zig
rg -n -C 5 'resourcesdir|os/main|src/os' build.zig src/os/main.zig src/os --glob '*.zig'
command -v zig || true
zig version 2>/dev/null || trueRepository: manaflow-ai/ghostty
Length of output: 10899
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path("src/os/resourcesdir.zig").read_text()
start = text.index("fn selfSharedObjectPath")
end = text.index("/// Little helper", start)
snippet = text[start:end]
for i, line in enumerate(snippet.splitlines(), 1):
if any(x in line for x in ("comptime", "dladdr", "DlInfo", "resourcesDir")):
print(f"{i}: {line}")
print('file-scope DlInfo:', 'const DlInfo = extern struct' in text)
print('file-scope dladdr:', 'extern "c" fn dladdr' in text)
PYRepository: manaflow-ai/ghostty
Length of output: 495
🌐 Web query:
Zig language reference comptime if branch semantic analysis if (comptime``
💡 Result:
In Zig, the comptime keyword used within an if statement—specifically in the form if (comptime condition)—is a mechanism to explicitly force the compiler to evaluate the condition and perform branch analysis at compile time [1]. ### Semantic Mechanics When the compiler encounters if (comptime condition), it guarantees that the branch is resolved during semantic analysis (Sema) [2][1]. The primary effects of this are: 1. Statically-Determined Execution: Because the condition is evaluated at compile time, the compiler can definitively determine which code path to include in the resulting program [3][1]. 2. Code Removal: Any branch that is not taken is entirely discarded by the compiler [4][3]. This is frequently used to conditionally compile code, ensuring that the discarded branch does not contribute to the final binary size or cause compilation errors in irrelevant contexts [3][1]. 3. Requirement for Comptime-Known Values: The expression used as the condition must be comptime-known [3]. If the expression depends on runtime values, compilation will fail, as the compiler cannot resolve the branch during semantic analysis [1]. ### Comparison with Implicit Comptime Zig is often smart enough to perform this analysis implicitly without the explicit comptime keyword if the condition is already known to be comptime-constant (e.g., checking a const value or a builtin feature) [3]. Using if (comptime ...) acts as an explicit directive to the compiler [3][1]. This is particularly useful for: * Forcing an error if a condition is not actually comptime-known [1]. * Making intentions clear to readers and the compiler [3]. * Ensuring that side effects of code evaluation are handled within the comptime context [2]. ### Important Distinction Do not confuse if (comptime condition) with a comptime block containing an if statement, though they often achieve similar goals. The if (comptime condition) syntax specifically targets the branch resolution of the if statement itself, ensuring that dead code paths are pruned during the semantic analysis phase [3][1]. Top results: [3][1][2]
Citations:
- 1: https://ziggit.dev/t/assert-vs-comptime-if/4229
- 2: https://deepwiki.com/ziglang/zig/2.3-semantic-analysis-(sema)
- 3: https://mitchellh.com/writing/zig-comptime-conditional-disable
- 4: Improve comptime conditional evaluation ziglang/zig#6768
🌐 Web query:
Zig function value coercion to *const anyopaque @ptrCast function pointer
💡 Result:
In Zig, function pointers do not implicitly coerce to *const anyopaque because they are pointer-to-code types, whereas *anyopaque is specifically a pointer-to-data type [1][2]. Attempting to cast a function pointer to *const anyopaque (or any *anyopaque variant) using @ptrCast is technically possible but generally discouraged or invalid depending on the target architecture and calling convention, as the ABI representation of code pointers and data pointers can differ significantly. Key technical considerations: 1. Data vs. Code Pointers: *anyopaque is a type-erased pointer intended for data objects (analogous to void* in C) [1][2]. Function pointers are distinct and represent the address of executable instructions. 2. @ptrCast usage: You can use @ptrCast to force this conversion, but the compiler will not treat the result as a safe, type-erased container for the function. If you need to store function pointers in a generic way, the standard approach is to use a structure that explicitly contains the function pointer, such as: const FunctionContainer = struct { ptr: *const fn void, }; 3. Common Misconceptions: Users often attempt to cast function pointers to *anyopaque when trying to implement polymorphic behavior or vtables [2][3]. The correct pattern in Zig for such cases is to keep the function pointer signature explicit or use a context pointer (often *anyopaque) alongside a typed function pointer that accepts it as an argument [2][4]. Example of the recommended pattern: fn handler(context: *anyopaque) void { const self: *MyStruct = @ptrCast(@alignCast(context)); //... } If you encounter errors when attempting to cast, it is typically because the compiler is enforcing the distinction between data pointers and function pointers, which are incompatible types [3].
Citations:
- 1: https://ziglang.org/documentation/master/
- 2: https://github.com/ok-ryoko/zig-notes/blob/main/notes/type-erased-pointer.md
- 3: https://ziggit.dev/t/stricter-function-pointer-casts/6713
- 4: https://ziggit.dev/t/how-to-handle-function-pointers-with-unknown-signature/6853
🌐 Web query:
site:ziglang.org/documentation/master function pointer anyopaque coercion Zig
💡 Result:
In Zig, the anyopaque type is specifically intended for type-erased object pointers [1]. It represents a pointer to memory of an unknown type, analogous to void* in C [1]. Function pointers in Zig are distinct from data pointers, and they cannot be implicitly coerced to *anyopaque. Because function pointers and data pointers may have different representations or sizes depending on the target architecture, they are not interchangeable. If you need to store a function pointer in a context that requires type erasure, you must use an explicit cast, typically via @ptrCast. However, note that casting a function pointer to a data pointer type (like *anyopaque) is generally unsafe and is often prohibited by the compiler because function pointers are not guaranteed to be compatible with data pointers. If you are attempting to store a function pointer where a generic pointer is expected, consider using a struct that wraps the function pointer or an integer-based representation, depending on your specific requirements.
Citations:
🌐 Web query:
Zig "expected type '*const anyopaque', found 'fn"
💡 Result:
The error message "expected type '*const anyopaque', found 'fn...'" occurs in Zig when you attempt to assign or pass a specific function pointer to a variable or field defined as '*const anyopaque'. This is a type mismatch because function pointers are not data pointers and cannot be implicitly coerced into a data pointer type like '*const anyopaque' [1][2]. In Zig, 'const anyopaque' is intended for type-erased data, acting similarly to a C 'void' [3]. Because a function pointer represents code rather than a data buffer, Zig's strict type system prevents this coercion to ensure memory safety and correct calling conventions [4]. To resolve this, you have two primary approaches depending on your goal: 1. If you are creating a system for callbacks or vtables (where you need to store different functions), you must define your storage field as the specific function pointer type that matches the signature you intend to call [4][5]. Example: Instead of: field: *const anyopaque, Use the actual signature: field: *const fn (*anyopaque, u32) void, 2. If you absolutely need to store a function pointer as a generic type, you must cast it to an integer or a pointer type that can hold it (such as a usize or a specific function pointer type), though this is generally discouraged in favor of proper type definitions [1]. If you are encountering this error when working with vtables or generic interfaces, you should define your structure fields to hold the exact function pointer signature required, while using '*anyopaque' solely for the 'context' or 'self' parameter passed to those functions [4][5]. This allows the function to remain type-safe while still accepting various data structures as context [5].
Citations:
- 1: https://ziggit.dev/t/is-there-way-to-define-an-opaque-pointer-to-a-function/12265
- 2: https://ziggit.dev/t/could-function-pointers-with-anyopaque-parameter-accept-functions-with-opaque-pointer/17137
- 3: https://github.com/ok-ryoko/zig-notes/blob/main/notes/type-erased-pointer.md
- 4: https://ziggit.dev/t/stricter-function-pointer-casts/6713
- 5: https://ziggit.dev/t/how-to-handle-function-pointers-with-unknown-signature/6853
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 3 '`@ptrCast`|anyopaque|fn [A-Za-z_][A-Za-z0-9_]*\(' src build.zig --glob '*.zig' |
rg -n -C 3 'anyopaque|`@ptrCast`' || true
rg -n -C 3 'dladdr|dlsym|dlopen|DlInfo' . --glob '*.zig' --glob '*.h' --glob '*.c' || trueRepository: manaflow-ai/ghostty
Length of output: 50376
Cast resourcesDir before passing it to dladdr. Zig does not implicitly coerce a function value to *const anyopaque, so Linux builds fail at this call. Use @ptrCast(&resourcesDir). The comptime guard excludes the dladdr call on non-Linux targets, so it does not create a Windows link dependency.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/os/resourcesdir.zig` around lines 201 - 226, Update the dladdr call in
selfSharedObjectPath to pass `@ptrCast`(&resourcesDir) instead of the function
value directly, preserving the existing comptime platform guard and all other
path handling.
| test "image context loss cleanup frees CPU data without destroying textures" { | ||
| const alloc = std.testing.allocator; | ||
|
|
||
| { | ||
| const data = try alloc.alloc(u8, 4); | ||
| var image: Image = .{ .pending = .{ | ||
| .width = 1, | ||
| .height = 1, | ||
| .pixel_format = .rgba, | ||
| .data = data.ptr, | ||
| } }; | ||
| image.deinitAfterContextLost(alloc); | ||
| } | ||
|
|
||
| { | ||
| const data = try alloc.alloc(u8, 4); | ||
| var image: Image = .{ .replace = .{ | ||
| .texture = undefined, | ||
| .pending = .{ | ||
| .width = 1, | ||
| .height = 1, | ||
| .pixel_format = .rgba, | ||
| .data = data.ptr, | ||
| }, | ||
| } }; | ||
| image.deinitAfterContextLost(alloc); | ||
| } | ||
|
|
||
| { | ||
| var image: Image = .{ .ready = undefined }; | ||
| image.deinitAfterContextLost(alloc); | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm the receiver is by value and check for similar patterns elsewhere.
rg -n -C 3 'pub fn deinitAfterContextLost' src/renderer/image.zig
rg -n 'var image: Image' src/renderer/image.zigRepository: manaflow-ai/ghostty
Length of output: 871
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- method implementation ---'
sed -n '830,865p' src/renderer/image.zig
printf '%s\n' '--- test ---'
sed -n '1035,1080p' src/renderer/image.zig
printf '%s\n' '--- Zig availability and formatting check ---'
if command -v zig >/dev/null 2>&1; then
zig version
zig fmt --check src/renderer/image.zig
else
echo 'zig is not available'
fiRepository: manaflow-ai/ghostty
Length of output: 2393
Declare the test images as const. Image.deinitAfterContextLost takes self: Image by value and does not mutate the images. Replace all three var image declarations with const image to avoid Zig's “local variable is never mutated” compile error.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/renderer/image.zig` around lines 1040 - 1072, Change all three test-local
image declarations in image context loss cleanup test from var image to const
image, since deinitAfterContextLost consumes Image by value without mutating the
locals.
| fn embeddedLinuxGetProcAddress(name: [*:0]const u8) callconv(.c) ?*const fn () callconv(.c) void { | ||
| if (comptime EmbeddedLinux == void) return null; | ||
|
|
||
| const platform = embedded_linux_loader orelse return null; | ||
| const proc = platform.get_proc_address(platform.userdata, name) orelse return null; | ||
| return @ptrCast(proc); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect both loader implementations and the Linux platform callback type.
rg -n -C 4 'embeddedLinuxGetProcAddress|embeddedGetProcAddress' src/renderer/OpenGL.zig
ast-grep run --pattern 'pub const Linux = $$$' --lang zig src/apprt/embedded.zig || \
rg -n -C 12 'get_proc_address' src/apprt/embedded.zigRepository: manaflow-ai/ghostty
Length of output: 19962
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- OpenGL loader definitions ---'
sed -n '1,70p' src/renderer/OpenGL.zig
printf '%s\n' '--- relevant Zig/toolchain metadata ---'
rg -n 'minimum.*zig|zig_version|\.zig-version|requires Zig|Zig version' README.md build.zig build.zig.zon .github 2>/dev/null || true
if command -v zig >/dev/null 2>&1; then
zig version
else
printf '%s\n' 'zig: unavailable'
fiRepository: manaflow-ai/ghostty
Length of output: 3648
🌐 Web query:
Zig language reference @ptrCast @aligncast pointer alignment function pointer
💡 Result:
In Zig, pointer alignment is a core property of the pointer type, representing the requirement that the memory address must be divisible by a specific power of two [1]. Pointers with higher alignment can be coerced to pointers with lower alignment, but not vice versa [1]. @ptrCast @ptrCast is a builtin function used to convert a pointer of one type to a pointer of another type [2][3]. It is an unsafe operation that reinterprets the child type of the pointer [4][5]. It does not perform any runtime logic; rather, it informs the compiler to treat the given pointer as a different type [4]. @alignCast @alignCast is a builtin function used to increase the alignment of a pointer [5][1]. Since alignment is a property of the type in Zig, @alignCast performs a type cast to a more-aligned pointer type [4]. It is a no-op at runtime but inserts a safety check to ensure that the memory address is actually divisible by the new alignment [4][5][1]. If the check fails at runtime, it triggers a panic [4][1]. Usage Patterns When both operations are needed (e.g., when casting an *anyopaque to a specific type with higher alignment), both @ptrCast and @alignCast can be used [4]. The order of operations—whether you use @alignCast(@ptrCast(x)) or @ptrCast(@alignCast(x))—does not affect the result, as they are orthogonal operations [4]. Each handles a different aspect of the conversion (child type vs. alignment) and they can be used independently or in combination depending on the specific pointer requirements [4].
Citations:
- 1: https://github.com/ok-ryoko/zig-notes/blob/main/notes/pointer-alignment.md
- 2: https://www.bookstack.cn/read/Zig-0.9-en/spilt.38.zig.md
- 3: https://www.bookstack.cn/read/Zig-0.9-en/spilt.13.zig.md
- 4: https://ziggit.dev/t/aligncast-ptrcast-x-or-ptrcast-aligncast-x/10419
- 5: https://ziglang.org/documentation/0.4.0/
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- exact type aliases and casts ---'
rg -n -C 8 'const GlProc|GlProc|`@ptrCast`|`@alignCast`' src/renderer/OpenGL.zig
printf '%s\n' '--- repository Zig files ---'
git ls-files '*.zig' | head -40Repository: manaflow-ai/ghostty
Length of output: 3330
🌐 Web query:
site:ziglang.org/documentation/0.16.0 @ptrCast @aligncast function pointer anyopaque alignment
💡 Result:
In Zig, anyopaque is a type used for type-erased pointers, representing a pointer to memory where the underlying type is unknown [1]. Because it lacks type information, it is frequently used when interfacing with C APIs that require generic pointers (void*). The @ptrCast builtin is used to convert a pointer of one type to a pointer of another type. When casting to or from anyopaque, Zig handles the type erasure safely, but the programmer remains responsible for ensuring the target type is correct when casting back from anyopaque [1]. The @alignCast builtin is used to adjust the alignment of a pointer. This is necessary when you have a pointer with a lower alignment requirement and you need to treat it as a type with a higher alignment requirement. Before using @alignCast, you must ensure the memory address is actually aligned to the target alignment; otherwise, the program will trigger a runtime safety error. Regarding function pointers, they are distinct from data pointers in Zig and generally cannot be cast to or from anyopaque pointers in the same manner as data pointers. Attempting to cast a function pointer to an opaque pointer type is often disallowed or unsafe, as the ABI for function pointers differs significantly from that of data pointers. In general, you should not use anyopaque to store function pointers.
Citations:
🌐 Web query:
Zig 0.16 @ptrCast increases pointer alignment *anyopaque function pointer
💡 Result:
In Zig, the error message "cast increases pointer alignment" occurs when you attempt to use @ptrCast to convert a pointer with a lower alignment requirement to a pointer type with a higher alignment requirement [1][2]. Because Zig enforces strict memory safety and alignment rules, it prohibits this operation directly to prevent undefined behavior that could occur if the underlying memory address is not actually aligned to the requirements of the new pointer type [3][4]. The error is not specific to anyopaque or function pointers; it is a general rule for all pointer types [4][1]. When you cast, for example, from *anyopaque (which typically has an alignment of 1) to *u64 (which requires an alignment of 8), the compiler detects the "increase" in alignment requirement and rejects the cast [4][2]. To resolve this, you must explicitly confirm that the pointer is correctly aligned at runtime using @alignCast [3][5]. The idiomatic pattern is to use @alignCast to assert the alignment of the source pointer before casting it to the target type [3][5]. Example of the correct approach: const target_ptr: *T = @ptrCast(@alignCast(target_alignment, source_ptr)); In this pattern: 1. @alignCast(target_alignment, source_ptr) asserts that the source pointer satisfies the alignment requirement of the target type [3][6]. 2. The result of this assertion is then passed to @ptrCast to perform the actual type conversion [3]. If the pointer provided to @alignCast does not actually meet the required alignment at runtime, Zig will trigger a panic in Debug/ReleaseSafe modes, thereby preventing memory-safety violations [3]. For scenarios involving function pointers or C interop where you must cast from a type-erased pointer like anyopaque, this combination of @alignCast and @ptrCast is the standard and required procedure [3][7].
Citations:
- 1: https://git.medv.io/zig/blob/master/test/cases/compile_errors/increase_pointer_alignment_in_ptrCast.zig.html
- 2: ptrCast confusing error message ziglang/zig#14474
- 3: https://ziggit.dev/t/alignment-and-casting-for-anyopaque-pointer/2165
- 4: Pointer cast: incorrect alignment ziglang/zig#11887
- 5: Improper translate-c of pointer cast ziglang/zig#12110
- 6: https://github.com/ziglang/zig/blob/e498fb155051f548071da1a13098b8793f527275/test/behavior/align.zig
- 7: [*c] anyopaque is not compileable, needed for C interop ziglang/zig#11630
🏁 Script executed:
#!/bin/bash
set -eu
url='https://raw.githubusercontent.com/ziglang/zig/master/test/cases/compile_errors/increase_pointer_alignment_in_ptrCast.zig'
printf '%s\n' '--- official ptrCast alignment test ---'
curl -fsSL "$url" | sed -n '1,180p'
printf '%s\n' '--- project source around Linux loader ---'
sed -n '228,255p' src/renderer/OpenGL.zigRepository: manaflow-ai/ghostty
Length of output: 1549
🌐 Web query:
Zig "*const fn" "@ptrCast increases pointer alignment" "@aligncast"
💡 Result:
In Zig, if a @ptrCast attempts to convert a pointer to a type that has a higher alignment requirement than the source pointer, the compiler will emit an error stating that the cast increases pointer alignment [1][2]. This error is a safety mechanism to prevent undefined behavior caused by dereferencing a pointer that does not satisfy its type's alignment requirements [3][4]. To resolve this, you must explicitly inform the compiler that you have verified the alignment by wrapping the pointer in @alignCast [4][1]. The idiomatic pattern is to use them together: const my_ptr: *TargetType = @ptrCast(@alignCast(source_ptr)); How this works: 1. @alignCast: This builtin asserts that the pointer's address is already aligned to the requirements of the target type [3][4]. At runtime, in non-optimized build modes (Debug and ReleaseSafe), Zig inserts checks to verify that the pointer is indeed correctly aligned [3]. If the check fails, the program will panic. 2. @ptrCast: This performs the conversion to the desired pointer type [4]. Because Zig's pointer cast builtins are designed to compose, you do not need to specify intermediate types [2]. The compiler understands that the combination of these builtins effectively performs the type and alignment adjustment in a single logical operation [2]. Note that @alignCast does not change the memory address of the pointer; it only asserts that the pointer satisfies the required alignment [5].
Citations:
- 1: https://ziggit.dev/t/zr-simple-batteries-included-hot-reloading-for-zig/14019
- 2: https://ziggit.dev/t/alignment-and-casting-for-anyopaque-pointer/2165
- 3: https://ziggit.dev/t/vtable-interfaces-and-the-role-of-ptrcast-and-aligncast/2062
- 4: https://www.openmymind.net/Zig-Interfaces/
- 5: https://ziggit.dev/t/uefi-file-getinfo-alignment-error/10981
🌐 Web query:
site:github.com/ziglang/zig "function pointer" "alignment" "@ptrCast"
💡 Result:
In Zig, @ptrCast is a compile-time operation used to convert a pointer from one type to another. When working with function pointers and alignment, there are several critical considerations regarding safety and language constraints [1][2]. Alignment Rules Zig enforces strict alignment requirements for all pointers, including function pointers [1][2]. If a cast results in a pointer that requires a higher alignment than the source memory provides, the operation will trigger a compile error (e.g., "cast increases pointer alignment") [3]. To address this, developers must use @alignCast to explicitly inform the compiler that the pointer is correctly aligned, assuming the underlying memory address actually satisfies that alignment [3][1]. Function Pointer Specifics Function pointers carry their own alignment requirements, which can vary based on the target architecture and the calling convention (e.g., C calling convention) [1][2]. 1. Alignment Checks: Attempting to cast to an over-aligned function pointer without ensuring the source address meets those requirements is unsafe and will result in a runtime panic if safety checks are enabled [1]. 2. Architecture Dependence: On some architectures (such as ARM/Thumb), function pointers may require specific alignment or have bit-level requirements (like the Thumb bit) that must be preserved [1][2]. Naive pointer casting or arithmetic that ignores these requirements can lead to crashes or undefined behavior [4][2]. 3. Data vs. Function Pointers: Zig treats pointers to data and pointers to functions as distinct [4]. While they might have the same bit representation on some platforms, they are not universally interchangeable [4]. Direct conversion between them via @ptrCast is often restricted or discouraged to prevent misinterpretation of memory contents [4]. Best Practices To safely handle function pointer alignment: - Always ensure the pointer being cast is inherently aligned to the requirement of the target function type [1]. - Use @alignCast if you have verified the alignment manually and need to satisfy the compiler's requirements [3][1]. - For low-level operations (like loading functions from memory or dynamic libraries), verify that the memory region is properly aligned and mapped with the necessary executable permissions [5]. - Avoid arbitrary casting between function and data pointers; instead, use explicit conversion methods like @intFromPtr or @ptrFromInt when you must manipulate pointer addresses directly, and ensure you handle architectural specifics like instruction set bits [4][2]. In summary, @ptrCast does not automatically adjust or "fix" the alignment of a pointer; it merely changes the type. If the target type demands stricter alignment than the source, you must ensure the alignment externally and use @alignCast [3][1].
Citations:
- 1: https://github.com/ziglang/zig/blob/e498fb155051f548071da1a13098b8793f527275/test/behavior/align.zig
- 2: https://github.com/ziglang/zig/blob/738d2be9/test/behavior/align.zig
- 3: How to pass a Zig struct to a C ABI and back to Zig ziglang/zig#6487
- 4: Language Defect: Conversion between function and data pointers changes pointee and creates object confusion ziglang/zig#24118
- 5: Function pointers inside a struct in a DLL are zero ziglang/zig#20980
Add @alignCast before the function-pointer cast. @ptrCast alone can fail when the destination function pointer has stricter alignment than *anyopaque. Use return @ptrCast(@aligncast(proc));.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/renderer/OpenGL.zig` around lines 244 - 250, Update
embeddedLinuxGetProcAddress to apply `@alignCast` to proc before the existing
`@ptrCast` conversion, returning the aligned function pointer while preserving the
current null handling.
| const default_id = "default"; | ||
| const default_title: [:0]const u8 = "cmux"; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Do not hardcode "cmux" as the notification title.
default_title becomes the user-visible title for every OSC 99 body chunk that has no stored title. This PR merges into the shared manaflow-ai/ghostty fork, so a downstream product name leaks into all consumers of the fork, including macOS. Use a neutral default such as "Ghostty", or leave the title empty and let the apprt fall back to the surface title.
🔤 Proposed change
-const default_title: [:0]const u8 = "cmux";
+const default_title: [:0]const u8 = "Ghostty";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const default_id = "default"; | |
| const default_title: [:0]const u8 = "cmux"; | |
| const default_id = "default"; | |
| const default_title: [:0]const u8 = "Ghostty"; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/terminal/osc/parsers/kitty_notification.zig` around lines 14 - 15, Update
the default_title constant in the OSC 99 notification parser to remove the
downstream product name, using a neutral upstream title such as “Ghostty” or an
empty value so the apprt can fall back to the surface title.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6274b8af1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| export fn ghostty_inspector_free(ptr_: ?*Inspector) void { | ||
| const ptr = inspectorHandle(ptr_, "ghostty_inspector_free") orelse return; | ||
| ptr.surface.freeInspector(); |
There was a problem hiding this comment.
Declare inspector free with the handle it consumes
When a C embedder follows the installed include/ghostty.h, ghostty_inspector_free still accepts a ghostty_surface_t, but this implementation now interprets that argument as an Inspector and immediately dereferences ptr.surface. Passing the surface handle required by the public declaration therefore reads an unrelated object layout and can crash or corrupt memory when closing the inspector; change the header to accept ghostty_inspector_t or preserve the surface-based implementation.
Useful? React with 👍 / 👎.
| input.Mods.Backing, | ||
| @truncate(@as(c_uint, @bitCast(mods))), | ||
| )), | ||
| inputMods(mods), | ||
| ); | ||
| } | ||
|
|
||
| /// Update the mouse position within the view. | ||
| export fn ghostty_surface_mouse_pos( |
There was a problem hiding this comment.
Publish the new status-returning C signatures
For C clients compiled against the installed header, the newly status-returning APIs remain declared as void: this includes ghostty_surface_mouse_pos, mouse scroll, IME point, close/split operations, and the inspector input/setter functions in include/ghostty.h. Such clients cannot inspect the new validation failures, and ABI-v15 source that expects the advertised Boolean result will not compile; update the public declarations to return bool alongside these implementation changes.
Useful? React with 👍 / 👎.
|
@codex review |
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Ready for shared-fork maintainer merge at Codex reviewed the exact head and reported no major issues. Focused Linux/OSC 99/header ABI tests, strict C11 compilation, ReleaseFast installation, and the installed C/EGL smoke (3/3) pass. The cmux submodule pointer remains intentionally unchanged until this commit is reachable from |
|
Closing because this integration belongs to the siure-owned cmux/Ghostty project, not the manaflow-ai shared fork. No commit was pushed to manaflow-ai/ghostty; the reviewed work will be published on siure/ghostty instead. |
Integrates the Ghostty commit currently pinned by cmux PR #4 into the shared
manaflow-ai/ghosttyfork so cmux can stop depending on a personal-fork-only object.cmux context: siure/cmux#4
Merge parents:
c5d8fc1add7f53eb8d0da8cf4e9ec3cf2bc74b5c479baf4d56f1d9e94cf80560175fac21b9ccaf17Conflict resolution preserves the Linux C ABI and installed-library embedding smoke contract while retaining current shared-main renderer transaction/lease behavior, terminal resize/history semantics, idempotent C initialization, and modern Zig 0.16 APIs. It also restores the C compatibility aliases/declarations covered by the merged ABI tests.
Validation on Linux with Zig 0.16.0:
-Dapp-runtime=nonebuild: passed-Wall -Wextra -WerrorpassedThe cmux submodule pointer will not be updated until this merge commit is reachable from
manaflow-ai/ghostty:main.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Merges Linux embedding ABI v15 into the shared
ghosttyfork to stabilize the C ABI and the installed-library embedding path on Linux. Publishes status-returning C APIs and adds signature-pinning tests; also hardens renderer, terminal, and resource lookup behaviors.New Features
ghostty.h(ABI/version, Linux platform, keycode flags, physical-key helper).-Dapp-runtime=none, surfaceless EGL) viapkg-configforghostty-internal; validates callbacks, OSC 99, and header contracts.libghostty-internal.soandghostty-internal.soplus.avariants; Nix devShell addslibglvndandmesa.Migration
GHOSTTY_EMBEDDING_ABI_VERSION 15) and link viapkg-config --cflags --libs ghostty-internal egl.Written for commit 5fddee2. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes