Skip to content

Report color scheme should happen synchronously #5922

Open
@mitchellh

Description

@mitchellh

Discussed in #5796

Originally posted by JakeWharton February 15, 2025
Right now, querying theme state is async, and is usually delivered after responses to subsequent VT sequences.

❯ echo -ne '\e[?996n\e[5n`'; cat
`^[[0n^[[?997;1n^C⏎

❯ echo -ne '\e[?996n\e[5n`'; cat
`^[[?997;1n^[[0n^C

Here two executions of a theme query followed by DSR show that the theme response can come before the DSR or after, and it is usually after in my testing.

This is because the code is effectively double dispatching. The parser for the theme request delegates the query to the surface:

.color_scheme => self.surfaceMessageWriter(.{ .report_color_scheme = true }),

The surface, upon receiving the dispatch:

.report_color_scheme => |force| self.reportColorScheme(force),

Enqueues a response:

ghostty/src/Surface.zig

Lines 1000 to 1005 in 9a5bc65

const output = switch (self.config_conditional_state.theme) {
.light => "\x1B[?997;2n",
.dark => "\x1B[?997;1n",
};
self.io.queueMessage(.{ .write_stable = output }, .unlocked);

And this races the subsequent DSR parse and response.

This behavior is in contrast with something like focus, which as of #2103 responds synchronously with the current value:

.focus_event => if (enabled) self.messageWriter(.{
.focused = self.terminal.flags.focused,
}),

❯ echo -ne '\e[?1004h\e[5n'; cat
^[[I^[[0n^C⏎

Is it possible to push theme state into the terminal so that it can respond to queries synchronously? I am working on a TUI library whose startup process sometimes sees the system theme query return before our DSR marker reply to be reflected in the initial state correctly, but more often than not sees the DSR marker reply before the system theme query response causing an unnecessary frame of potentially incorrect rendering.

Implementation Note

We can update termio.DerivedConfig to pull the Config._conditional_state so that we don't need to go back to the core Surface for this information. That way, we can avoid the async.

We should remove the report_color_scheme action from Surface if its otherwise unused.

Metadata

Metadata

Assignees

No one assigned

    Labels

    vtControl sequence related

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions