Summary
contour daemon (and therefore every contour client session, since it hosts through the
daemon) only resolves a profile's emulation settings via config::resolveEmulationSettings()
(ContourApp::daemonAction(), src/contour/cli/ContourApp.cpp). That function returns a bare
vtbackend::Settings, which structurally cannot carry most of what a TerminalProfile actually
configures. Everything beyond that narrow slice silently reverts to a hardcoded default —
on the very first daemon spawn, not just when an already-running daemon is reused.
A local contour terminal launch does not have this problem: TerminalSession::createSettingsFromConfig()
and SessionFactory::createPty() layer the rest of the profile on top of the same
emulationSettings() call. The daemon path never calls either.
Reproduction
contour client --profile myprofile
where myprofile sets any of: a custom shell:, a non-default status_line, a custom cursor
style, a non-default color_scheme, ssh:, or escape_sandbox: false.
Expected: behaves like contour terminal --profile myprofile.
Actual: silently falls back to defaults for all of the below.
Confirmed gaps
-
Shell (ContourApp::daemonAction, src/contour/cli/ContourApp.cpp:707) — hardcoded to
vtpty::Process::loginShell(). The profile's shell: block (program/arguments/workingDirectory/env)
is never read at all.
-
Presentation settings (TerminalSession::createSettingsFromConfig,
src/contour/session/TerminalSession.cpp:209-289) — ~15 fields the daemon never sets: cursor
shape/blink/display, status line type/position/indicator text, smooth/momentum scrolling,
screen transition style/duration, mouse wheel scroll multiplier, copy_last_mark_range_offset,
highlight timeout/word, sync_window_title_with_host_writable_status_display, tab naming mode.
-
Color palette (TerminalSession.cpp:284) — the profile's colors: scheme never reaches
Terminal::_colorPalette, which is semantically load-bearing (default fg/bg, indicator status
line colors, fold-marker colors, indexed-color lookups) — not just a rendering skin. The
client-side remote-mirroring path (src/contour/remote/) has no palette handling of its own to
compensate.
-
SSH profiles (SessionFactory::createPty, src/contour/session/SessionFactory.cpp:45 vs.
vthost::makeShellPtyFactory, src/vthost/Daemon.cpp:81-94) — a profile with ssh.hostname set
opens a plain local shell under the daemon instead of a vtpty::SshSession. DaemonConfig has
no field to carry SSH config at all.
-
escape_sandbox (vthost::makeShellPtyFactory, src/vthost/Daemon.cpp:92) — hardcoded to
true, ignoring a profile's escape_sandbox: false.
-
CONTOUR_PROFILE env var (vthost::hostedShellEnvironment, src/vthost/HostedShell.hpp:40)
— never set for daemon-hosted shells, unlike a local session
(ContourGuiApp.cpp:938). Lower severity/confidence — the same header already documents a
sibling gap (TERM/COLORTERM/TERM_PROGRAM) as a deliberately deferred decision, so the
maintainers may want to fold this in with that rather than fix in isolation.
Root cause
All six share the same structural cause: src/vthost is (deliberately) Qt-free and has no access
to the full contour::config::TerminalProfile model — only to vtbackend::Settings via
config::resolveEmulationSettings(). There is currently no Qt-free path that resolves all of a
profile's non-GUI-rendering fields (shell, ssh, escape_sandbox, and the presentation/color fields
above) for a caller like daemonAction() to hand to DaemonConfig.
Suggested fix direction
Factor the logic currently duplicated between TerminalSession::createSettingsFromConfig() +
SessionFactory::createPty() (local sessions) into a shared, Qt-free "resolved session config"
step that both the local-session path and ContourApp::daemonAction() can call, then extend
DaemonConfig (src/vthost/Daemon.hpp) to carry what it's currently missing (full shell
ExecInfo already exists as a field but is never populated correctly; needs ssh,
escapeSandbox, and the daemon needs to pass the complete vtbackend::Settings including
presentation/color fields).
cc @contour-terminal/contour maintainers
Summary
contour daemon(and therefore everycontour clientsession, since it hosts through thedaemon) only resolves a profile's emulation settings via
config::resolveEmulationSettings()(
ContourApp::daemonAction(),src/contour/cli/ContourApp.cpp). That function returns a barevtbackend::Settings, which structurally cannot carry most of what aTerminalProfileactuallyconfigures. Everything beyond that narrow slice silently reverts to a hardcoded default —
on the very first daemon spawn, not just when an already-running daemon is reused.
A local
contour terminallaunch does not have this problem:TerminalSession::createSettingsFromConfig()and
SessionFactory::createPty()layer the rest of the profile on top of the sameemulationSettings()call. The daemon path never calls either.Reproduction
where
myprofilesets any of: a customshell:, a non-defaultstatus_line, a custom cursorstyle, a non-default
color_scheme,ssh:, orescape_sandbox: false.Expected: behaves like
contour terminal --profile myprofile.Actual: silently falls back to defaults for all of the below.
Confirmed gaps
Shell (
ContourApp::daemonAction,src/contour/cli/ContourApp.cpp:707) — hardcoded tovtpty::Process::loginShell(). The profile'sshell:block (program/arguments/workingDirectory/env)is never read at all.
Presentation settings (
TerminalSession::createSettingsFromConfig,src/contour/session/TerminalSession.cpp:209-289) — ~15 fields the daemon never sets: cursorshape/blink/display, status line type/position/indicator text, smooth/momentum scrolling,
screen transition style/duration, mouse wheel scroll multiplier,
copy_last_mark_range_offset,highlight timeout/word,
sync_window_title_with_host_writable_status_display, tab naming mode.Color palette (
TerminalSession.cpp:284) — the profile'scolors:scheme never reachesTerminal::_colorPalette, which is semantically load-bearing (default fg/bg, indicator statusline colors, fold-marker colors, indexed-color lookups) — not just a rendering skin. The
client-side remote-mirroring path (
src/contour/remote/) has no palette handling of its own tocompensate.
SSH profiles (
SessionFactory::createPty,src/contour/session/SessionFactory.cpp:45vs.vthost::makeShellPtyFactory,src/vthost/Daemon.cpp:81-94) — a profile withssh.hostnamesetopens a plain local shell under the daemon instead of a
vtpty::SshSession.DaemonConfighasno field to carry SSH config at all.
escape_sandbox(vthost::makeShellPtyFactory,src/vthost/Daemon.cpp:92) — hardcoded totrue, ignoring a profile'sescape_sandbox: false.CONTOUR_PROFILEenv var (vthost::hostedShellEnvironment,src/vthost/HostedShell.hpp:40)— never set for daemon-hosted shells, unlike a local session
(
ContourGuiApp.cpp:938). Lower severity/confidence — the same header already documents asibling gap (
TERM/COLORTERM/TERM_PROGRAM) as a deliberately deferred decision, so themaintainers may want to fold this in with that rather than fix in isolation.
Root cause
All six share the same structural cause:
src/vthostis (deliberately) Qt-free and has no accessto the full
contour::config::TerminalProfilemodel — only tovtbackend::Settingsviaconfig::resolveEmulationSettings(). There is currently no Qt-free path that resolves all of aprofile's non-GUI-rendering fields (shell, ssh, escape_sandbox, and the presentation/color fields
above) for a caller like
daemonAction()to hand toDaemonConfig.Suggested fix direction
Factor the logic currently duplicated between
TerminalSession::createSettingsFromConfig()+SessionFactory::createPty()(local sessions) into a shared, Qt-free "resolved session config"step that both the local-session path and
ContourApp::daemonAction()can call, then extendDaemonConfig(src/vthost/Daemon.hpp) to carry what it's currently missing (fullshellExecInfo already exists as a field but is never populated correctly; needs
ssh,escapeSandbox, and the daemon needs to pass the completevtbackend::Settingsincludingpresentation/color fields).
cc @contour-terminal/contour maintainers