Skip to content

Merge cmux Linux embedding ABI v15 into shared fork - #199

Closed
siure wants to merge 9 commits into
manaflow-ai:mainfrom
siure:integrate-linux-abi-v15
Closed

Merge cmux Linux embedding ABI v15 into shared fork#199
siure wants to merge 9 commits into
manaflow-ai:mainfrom
siure:integrate-linux-abi-v15

Conversation

@siure

@siure siure commented Aug 12, 2026

Copy link
Copy Markdown

Integrates the Ghostty commit currently pinned by cmux PR #4 into the shared manaflow-ai/ghostty fork so cmux can stop depending on a personal-fork-only object.

cmux context: siure/cmux#4

Merge parents:

  • shared fork main: c5d8fc1add7f53eb8d0da8cf4e9ec3cf2bc74b5c
  • cmux Linux ABI v15 pin: 479baf4d56f1d9e94cf80560175fac21b9ccaf17

Conflict 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:

  • focused embedded/Linux ABI suite: 87/87 passed
  • ReleaseFast -Dapp-runtime=none build: passed
  • installed C/EGL embedding smoke: passed 3 consecutive runs with surfaceless software EGL
  • C header smoke: -Wall -Wextra -Werror passed
  • complete test target compiled cleanly; its test process was terminated by the container resource ceiling (exit 137) after several silent minutes, with no assertion output

The cmux submodule pointer will not be updated until this merge commit is reachable from manaflow-ai/ghostty:main.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Merges Linux embedding ABI v15 into the shared ghostty fork 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

    • Integrated Linux embedding ABI v15 and restored C-compat macros in ghostty.h (ABI/version, Linux platform, keycode flags, physical-key helper).
    • Published status-returning C APIs in the header; updated macOS app callsites; added a C test to pin public function signatures.
    • Added installed embedding CI smoke test (ReleaseSafe -Dapp-runtime=none, surfaceless EGL) via pkg-config for ghostty-internal; validates callbacks, OSC 99, and header contracts.
    • Embedding/runtime: idempotent C init with a thread-safe guard; honors embedded callbacks; seeds initial output before I/O; Linux resources resolve relative to the loaded DSO before env vars.
    • Renderer resilience: OpenGL/Dear ImGui loader tracking across contexts; safe deinit after context loss (shaders/images) and swap-chain abandon; stricter unrealize with error handling.
    • Terminal/PTY: resize can preserve prompt history; OSC 99 desktop notifications with a bounded multipart store and title map; PTY poll drains tail bytes on HUP/ERR/NVAL.
    • Build/install: install both libghostty-internal.so and ghostty-internal.so plus .a variants; Nix devShell adds libglvnd and mesa.
  • Migration

    • Linux embedders should target ABI v15 (GHOSTTY_EMBEDDING_ABI_VERSION 15) and link via pkg-config --cflags --libs ghostty-internal egl.
    • Some C APIs now return a status; callers remain source-compatible but should check return values.

Written for commit 5fddee2. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added Linux embedding support for rendering, input, configuration, resources, surfaces, and lifecycle management.
    • Added OSC 99 desktop notification support, including titled and multi-part notifications.
    • Added optional prompt-history preservation when resizing terminals.
    • Added APIs for retrieving surface titles, working directories, resources, scrollback tails, and split zoom state.
  • Bug Fixes

    • Improved recovery after graphics-context loss and strengthened OpenGL lifecycle handling.
    • Ensured terminal output remaining after PTY hangups is fully received.
    • Improved Linux resource discovery and embedding validation.

@siure

siure commented Aug 12, 2026

Copy link
Copy Markdown
Author

@codex review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

Embedding API and platform contracts

Layer / File(s) Summary
Embedding API and platform contracts
include/ghostty.h, src/config/CApi.zig, src/main_c.zig, src/os/resourcesdir.zig
The C API now exposes Linux embedding metadata, callbacks, boolean result contracts, nullable inputs, resource lookup, configuration validation, and resource-directory discovery.
Library installation and runtime resources
build.zig, src/build/*, nix/devShell.nix
Internal libraries install under explicit filenames. Runtime resources can be installed and dependency-wired separately from complete application resources. Linux development shells include EGL/OpenGL packages.

Embedded OpenGL lifecycle

Layer / File(s) Summary
Embedded OpenGL lifecycle and context recovery
src/renderer/OpenGL.zig, src/renderer/generic.zig, src/renderer/image.zig, src/renderer/opengl/shaders.zig, pkg/dcimgui/*
Linux embedded rendering now manages host contexts, GL loader state, renderer realization, context loss, GPU cleanup, and ImGui loader users across multiple contexts.
Platform adapters and compatibility updates
macos/Sources/Ghostty/*, src/apprt.zig, src/apprt/action.zig, src/App.zig
Platform callers handle boolean C API results, inherited configuration ownership, Linux callback userdata, and target-specific ABI layouts.

Terminal behavior

Layer / File(s) Summary
Prompt-preserving resize and terminal transport
src/terminal/*, src/termio/*, src/Surface.zig
Resize messages carry prompt-history preservation. Screen reflow tracks prompt boundaries and selection tails. PTY handling drains unread data after HUP or ERR events.
OSC 99 desktop notifications
src/terminal/osc.zig, src/terminal/osc/parsers/*
The OSC parser supports Kitty notification title and body pairing, default titles, title replacement, safe UTF-8 validation, and malformed-input rejection.

Linux embedding validation

Layer / File(s) Summary
Linux embedding integration validation
test/linux/test_libghostty_embedding.c, .github/workflows/test.yml
CI builds the library in ReleaseSafe mode, validates installed artifacts, compiles the EGL smoke test with strict warnings, and runs metadata, configuration, surface, rendering, input, scrollback, resize, concurrency, and teardown checks.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: mitchellh, lawrencecchen

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: merging the cmux Linux embedding ABI v15 into the shared fork.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/apprt/embedded.zig Outdated
Comment on lines +449 to +450
if (comptime builtin.target.os.tag == .linux) {
return self.opts.action(self.opts.userdata, c_target, c_action);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment on lines +100 to +103
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)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines +125 to +132
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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment on lines 119 to 121
parser.kitty_notification_titles.put(alloc, id, payload) catch |err| {
log.warn("failed to store OSC 99 notification title err={}", .{err});
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Thread-scoped context ownership is asymmetric with the per-frame bracket.

threadEnter calls embeddedLinuxMakeCurrent but does not set embedded_linux_context_current. drawFrameEnd then calls endEmbeddedLinuxContext, which calls done_current and releases the context the renderer thread acquired. threadExit calls done_current again. This path is inactive while must_draw_from_app_thread is 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 threadEnter acquisition 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 win

Add permissions: contents: read to build-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 win

Add 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 p key (see the comment on lines 103-135)
  • e=1 Base64 payloads
  • empty i= value, which maps to the default id
  • a metadata p=title chunk parsed with Parser.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 value

Split the whole declaration instead of splitting the parameter list.

The #if block 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 for ghostty_surface_text and ghostty_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 value

Remove the unused ghostty-internal.so installation.

ghostty-internal.pc references libghostty-internal.so, and the repository has no consumer for ghostty-internal.so. Unless an external embedder requires this exact filename, remove lib_shared.installLibraryFile("ghostty-internal.so"). The static-library installation remains required by ghostty-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 win

Add regression coverage for prompt-history preservation.

Line 3981 changes resize behavior, but no test exercises .preserve_prompt_history = true or compares prompt history after reflow. Add paired true/false cases to protect the Terminal.resize to Screen.resize contract.

🤖 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 lift

Exercise the no-POLLIN PTY path.

This test uses a pipe and reads it directly. It does not assert that the hangup event lacks POLLIN, and it does not execute gatherMainPosix. 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 value

Use <= for the exact-capacity case.

len < sizeof(probe->bytes) truncates a payload whose length equals the buffer size, even though the copy would fit. Use len <= 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 tradeoff

Consolidate 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 single goto cleanup target with nullable handles removes the duplication and prevents a future failure branch from forgetting one release. verify_concurrent_surfaces already 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 win

Add a run timeout to the embedding smoke test.

The test polls with bounded retry loops, but a hung ghostty_app_tick or a stalled child process leaves the step running until the job-level default timeout. Wrap the binary in timeout so 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

📥 Commits

Reviewing files that changed from the base of the PR and between c5d8fc1 and 7406ccd.

📒 Files selected for processing (41)
  • .github/workflows/test.yml
  • build.zig
  • include/ghostty.h
  • macos/Sources/Ghostty/Ghostty.App.swift
  • macos/Sources/Ghostty/Ghostty.Config.swift
  • macos/Sources/Ghostty/Ghostty.Inspector.swift
  • macos/Sources/Ghostty/Surface View/SurfaceView.swift
  • macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift
  • nix/devShell.nix
  • pkg/dcimgui/ext.cpp
  • pkg/dcimgui/main.zig
  • src/App.zig
  • src/Surface.zig
  • src/apprt.zig
  • src/apprt/action.zig
  • src/apprt/embedded.zig
  • src/apprt/gtk/class/imgui_widget.zig
  • src/apprt/gtk/class/surface.zig
  • src/apprt/structs.zig
  • src/apprt/surface.zig
  • src/build/GhosttyLib.zig
  • src/build/GhosttyResources.zig
  • src/config/CApi.zig
  • src/main_c.zig
  • src/os/resourcesdir.zig
  • src/renderer/Metal.zig
  • src/renderer/OpenGL.zig
  • src/renderer/Thread.zig
  • src/renderer/generic.zig
  • src/renderer/image.zig
  • src/renderer/opengl/shaders.zig
  • src/terminal/Screen.zig
  • src/terminal/Terminal.zig
  • src/terminal/osc.zig
  • src/terminal/osc/parsers.zig
  • src/terminal/osc/parsers/kitty_notification.zig
  • src/termio/Exec.zig
  • src/termio/Termio.zig
  • src/termio/Thread.zig
  • src/termio/message.zig
  • test/linux/test_libghostty_embedding.c

Comment thread pkg/dcimgui/ext.cpp
Comment on lines +77 to +80
if (imgl3wInit() != GL3W_OK)
{
memset(&imgl3wProcs, 0, sizeof(imgl3wProcs));
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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 src

Repository: 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 -400

Repository: 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:


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().

Comment on lines +447 to +466
/// 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);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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.

Comment thread src/config/CApi.zig
Comment on lines +144 to 162
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;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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 || true

Repository: 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.h

Repository: 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.

Comment thread src/config/CApi.zig
Comment on lines +180 to +190
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);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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.

Comment thread src/os/resourcesdir.zig
Comment on lines +201 to +226
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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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 || true

Repository: 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)
PY

Repository: 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 || true

Repository: 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)
PY

Repository: 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:


🌐 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:


🌐 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:


🏁 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' || true

Repository: 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.

Comment thread src/renderer/image.zig
Comment on lines +1040 to +1072
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);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.zig

Repository: 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'
fi

Repository: 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.

Comment thread src/renderer/OpenGL.zig
Comment on lines +244 to +250
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);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.zig

Repository: 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'
fi

Repository: 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:


🏁 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 -40

Repository: 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:


🏁 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.zig

Repository: 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:


🌐 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:


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.

Comment on lines +14 to +15
const default_id = "default";
const default_title: [:0]const u8 = "cmux";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Suggested change
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.

Comment thread src/terminal/osc/parsers/kitty_notification.zig Outdated
Comment thread src/terminal/osc/parsers/kitty_notification.zig Outdated
@siure

siure commented Aug 12, 2026

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/apprt/embedded.zig
Comment on lines +8197 to +8199
export fn ghostty_inspector_free(ptr_: ?*Inspector) void {
const ptr = inspectorHandle(ptr_, "ghostty_inspector_free") orelse return;
ptr.surface.freeInspector();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread src/apprt/embedded.zig
input.Mods.Backing,
@truncate(@as(c_uint, @bitCast(mods))),
)),
inputMods(mods),
);
}

/// Update the mouse position within the view.
export fn ghostty_surface_mouse_pos(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@siure

siure commented Aug 12, 2026

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 5fddee2a8a

ℹ️ 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".

@siure

siure commented Aug 12, 2026

Copy link
Copy Markdown
Author

Ready for shared-fork maintainer merge at 5fddee2a8ad54b021325eb3f21c17aaf397f4aa9.

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 manaflow-ai/ghostty:main.

@siure

siure commented Aug 12, 2026

Copy link
Copy Markdown
Author

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.

@siure siure closed this Aug 12, 2026
@siure
siure deleted the integrate-linux-abi-v15 branch August 12, 2026 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant