Condition the configuration on a pane's foreground program #32

Closed
opened 2026-10-17 17:50:45 -05:00 by jeff · 1 comment
Owner

Let the configuration depend on a pane's foreground program, the leader of its pty's foreground process group, through ctx.foreground. Examples: a red background while it runs as root, and other colors and bindings for ssh or pwsh. The design is in ADR 0006, and the term is in GLOSSARY.md.

What to build

  • Config.Context.foreground: ?Foreground, holding name (basename of argv[0]), exe: ?[]const u8 (/proc/<pid>/exe, null if it can't be read), uid: u32 (effective) and user: ?[]const u8 (passwd entry, null if there is none). It is null app-wide, on Windows, and for non-Pty panes.
  • App-wide evaluation with foreground = null decides everything that isn't per pane. Each Pty pane evaluates again with its foreground program and takes only colors, the non-global keybind entries, bell, cursor, clipboard, link_detection, notifications, drag_and_drop and images; its profile overrides go on top. grapheme_width stays fixed for the life of the pane.
  • If a pane's evaluation gives an app-wide field a different value, harrier ignores it, logs it once per reload, and validate-config reports it.
  • Cache evaluations keyed by the whole ctx.foreground value: 64 entries, LRU, cleared on reload or an appearance change. A failed evaluation is logged once per program per reload, and the pane keeps the app-wide values.
  • Change detection: call tcgetpgrp before each key is dispatched and after each output batch, and on a timer every foreground_poll_ms (new top-level setting, default 250, 0 = key/output only). The timer also re-reads the leader's start time and UID, plus cmdline when either changes, to catch exec or setuid. If the leader has exited but the group remains, keep the last seen value.
  • If the config never reads ctx.foreground, skip the per-pane evaluation and polling. Blocked on struthio reporting which ctx fields a program read (requested from the struthio session).
  • validate-config --foreground NAME[:UID], repeatable. Without it, check: no foreground program, the user's own uid, and uid 0.
  • Document the limits: sudo's use_pty means sudo -i and sudo ssh both look like sudo with uid 0; tmux, screen and the far side of ssh are invisible.

Out of scope

Showing the foreground program anywhere else (subtitle, status window, shell search). Windows support.

Let the configuration depend on a pane's **foreground program**, the leader of its pty's foreground process group, through `ctx.foreground`. Examples: a red background while it runs as root, and other colors and bindings for `ssh` or `pwsh`. The design is in [ADR 0006](https://git.jcollie.dev/jeff/harrier/src/branch/main/docs/adr/0006-foreground-program-in-ctx.md), and the term is in `GLOSSARY.md`. ## What to build - `Config.Context.foreground: ?Foreground`, holding `name` (basename of `argv[0]`), `exe: ?[]const u8` (`/proc/<pid>/exe`, null if it can't be read), `uid: u32` (effective) and `user: ?[]const u8` (passwd entry, null if there is none). It is null app-wide, on Windows, and for non-Pty panes. - App-wide evaluation with `foreground = null` decides everything that isn't per pane. Each Pty pane evaluates again with its foreground program and takes only `colors`, the non-global `keybind` entries, `bell`, `cursor`, `clipboard`, `link_detection`, `notifications`, `drag_and_drop` and `images`; its profile overrides go on top. `grapheme_width` stays fixed for the life of the pane. - If a pane's evaluation gives an app-wide field a different value, harrier ignores it, logs it once per reload, and `validate-config` reports it. - Cache evaluations keyed by the whole `ctx.foreground` value: 64 entries, LRU, cleared on reload or an appearance change. A failed evaluation is logged once per program per reload, and the pane keeps the app-wide values. - Change detection: call `tcgetpgrp` before each key is dispatched and after each output batch, and on a timer every `foreground_poll_ms` (new top-level setting, default 250, 0 = key/output only). The timer also re-reads the leader's start time and UID, plus `cmdline` when either changes, to catch `exec` or `setuid`. If the leader has exited but the group remains, keep the last seen value. - If the config never reads `ctx.foreground`, skip the per-pane evaluation and polling. **Blocked on struthio** reporting which `ctx` fields a program read (requested from the struthio session). - `validate-config --foreground NAME[:UID]`, repeatable. Without it, check: no foreground program, the user's own uid, and uid 0. - Document the limits: sudo's `use_pty` means `sudo -i` and `sudo ssh` both look like `sudo` with uid 0; tmux, screen and the far side of ssh are invisible. ## Out of scope Showing the foreground program anywhere else (subtitle, status window, shell search). Windows support.
Author
Owner

struthio can now report which ctx fields an evaluation read, in struthio commit bcdafb8 on main. It hasn't been pushed yet, so it can't be fetched. harrier currently pins e1167b6, 12 commits behind, so expect API drift when bumping.

var reads: struthio.ContextReads(Config.Context) = .empty; // EnumSet(FieldEnum(Context))
var parsed = try struthio.evalSourceReads(Config, gpa, source, &ctx, options, &reads, &diag);
if (!reads.contains(.foreground)) { /* no per-pane evaluation, no polling */ }
  • Only what evaluation actually touched counts: the branch that was taken, short-circuited and/or, and the lets and @includes that were used. Bare ctx (let c = ctx;) counts as reading every field.
  • The set is filled whether evaluation succeeds or fails. It stays empty after a parse failure.
  • It is only valid while the other ctx fields stay the same, so after an appearance change, re-check using the new evaluation's set.
  • Breaking change: Call.ctx is now the method call.ctx(). @env and @setting don't touch ctx, so they're unaffected.
struthio can now report which `ctx` fields an evaluation read, in struthio commit `bcdafb8` on main. **It hasn't been pushed yet**, so it can't be fetched. harrier currently pins `e1167b6`, 12 commits behind, so expect API drift when bumping. ```zig var reads: struthio.ContextReads(Config.Context) = .empty; // EnumSet(FieldEnum(Context)) var parsed = try struthio.evalSourceReads(Config, gpa, source, &ctx, options, &reads, &diag); if (!reads.contains(.foreground)) { /* no per-pane evaluation, no polling */ } ``` - Only what evaluation actually touched counts: the branch that was taken, short-circuited `and`/`or`, and the `let`s and `@include`s that were used. Bare `ctx` (`let c = ctx;`) counts as reading every field. - The set is filled whether evaluation succeeds or fails. It stays empty after a parse failure. - It is only valid while the other `ctx` fields stay the same, so after an appearance change, re-check using the new evaluation's set. - **Breaking change:** `Call.ctx` is now the method `call.ctx()`. `@env` and `@setting` don't touch ctx, so they're unaffected.
jeff closed this issue 2026-10-17 18:42:22 -05:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jeff/harrier#32
No description provided.