Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@gdamore
Copy link
Owner

@gdamore gdamore commented Dec 7, 2025

This is a complete redesign of the input scanner, using logic derived from tcell. It eliminates the per-terminal special parsing for keys, so that we can process keys from almost anything.

This adds parsing support (not enabled yet) for CSI-u (kitty protocol), Xterm modifierOtherKeys, and win32-input-mode protocols. (These will be enabled in follow up.)

As a bonus, this adds suppot for focus reporting, so that we get to now when the application receives or loses focus. The mouse demo is updated to reflect that.

One consequence of this is that the key definitions for all the various control keys are gone, as these are now conveyed as a modified Key rune.

The key definitions (and also PadChar) are removed from the terminfo definitions.

This is a breaking change, but we are still pre-1.0 so we didn't have any promises anyway.

Summary by CodeRabbit

  • New Features

    • Terminal focus events and enable/disable focus support; "Hyper" modifier; new cursor styles (block/underline/bar + blinking), strikethrough, and URL enter/exit support.
  • Refactor

    • Unified input parser for keys/mouse/paste/focus; terminal capability data streamlined, removing many legacy function/arrow key mappings (may alter some key behavior).
  • Bug Fixes

    • Corrected error message typo and made Ctrl+L resize handling modifier-aware.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 7, 2025

Walkthrough

Replaces legacy ParseKeys with a new Parser class and CSI/UTF‑8 state machine, adds focus events and Screen.enableFocus, removes many legacy control-key constants and Termcap key fields across terminfo, and updates demos and TTY input loop to use the new parser and focus model.

Changes

Cohort / File(s) Summary
Event System
source/dcell/event.d
Added FocusEvent, added focus to EventType and Event union; copyright year bump.
Parser & Input
source/dcell/parser.d
New Parser class and CsiKey type; CSI/UTF‑8 state machine, centralized key maps (csiAllKeys, ss3Keys, linuxFKeys, csiUKeys, winKeys), and parse/handler methods for keys, mouse, paste, focus, OSC/Csi handling.
Key Representation
source/dcell/key.d
Removed many ctrl* key enum entries and mappings, added hyper modifier, switched control-key handling toward rune/ASCII semantics, updated KeyEvent.toString() formatting.
Screen API
source/dcell/screen.d
Removed hasKey(Key) from Screen interface; added enableFocus(bool) to public API.
TTY Implementation
source/dcell/ttyscreen.d
Removed ParseKeys usage and keys field; instantiate Parser(), updated input loop to call Parser.parse, added enableFocus() implementation and ensure focus disabled on stop.
Termcap Surface
source/dcell/termcap.d
Dropped a large set of legacy key/pad fields (padChar, keyBackspace, keyF1–F64, navigation & modified variants); added cursor/paste/style/URL/window fields (e.g., strikethrough, pasteEnd, cursorReset, cursorBlock/Underline/Bar and blinking variants, enterURL/exitURL, setWindowSize).
Terminfo Literals
source/dcell/terminfo/...
aixterm.d, alacritty.d, ansi.d, dtterm.d, gnome.d, konsole.d, linux.d, rxvt.d, screen.d, vt100.d, vt102.d, vt220.d, vt320.d, vt420.d, xfce.d, xterm.d
Removed key/pad-related initializers from many Termcap literals (padChar, keyBackspace, keyF*, keyInsert/Delete/Home/End/PgUp/PgDn, arrow keys, keyBacktab, and many shift/ctrl/meta/alt variants).
Database Adjustments
source/dcell/database.d
Removed URXVT-specific overrides that previously remapped shifted/ctrl arrow and home/end sequences.
Parser Consumers / Demos
demos/mouse/source/mouse.d, demos/colors/source/colors.d
Mouse demo enables focus (enableFocus(true)), tracks/displays focus and hyper modifier; both demos adjusted key handling for Ctrl+L via rune+modifier checks and minor formatting/year updates.
Build/Info Tool
mkinfo/source/mkinfo.d
Removed population of many deleted Termcap key fields and padding logic; minor formatting tweaks.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant TTY as TTY (fd/read)
    participant Parser as Parser
    participant EQ as EventQueue
    participant App as Application

    Note over TTY,Parser: terminal byte stream (ESC / CSI / OSC / UTF‑8 / mouse / focus)
    TTY->>Parser: feed bytes (Parser.parse/scan)
    Parser->>Parser: decode UTF‑8, handle CSI/OSC/DCS, mouse, focus
    Parser-->>EQ: post Event (Key / Mouse / Paste / Focus)
    EQ-->>App: deliver Event(s)
    App->>TTY: call enableFocus(true/false) when starting/stopping
    TTY-->>App: (optional) terminal acknowledges / focus notifications
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay attention to: source/dcell/parser.d (state machine, modifier calculation, CSI/OSC handling), source/dcell/key.d (enum/mapping removals and toString changes), source/dcell/ttyscreen.d (input loop, focus lifecycle).
  • Also review terminfo/termcap literal removals for any downstream code expecting the deleted fields (many terminfo files changed).

Possibly related PRs

Poem

🐰 I hopped through bytes and CSI streams bright,
I nudged the parser to parse through the night,
Focused on flutters, old F‑keys took flight,
Cursors blink in bars, blocks, and underlines light,
A rabbit claps paws: input now feels right.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat!: refactor the input processor' accurately and concisely describes the primary change: a breaking refactor of the input processing system, eliminating terminal-specific parsing and introducing a unified input processor.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gdamore/no-input-terminfo

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
mkinfo/source/mkinfo.d (1)

355-355: Critical bug: comparison instead of bit shift.

Line 355 uses 1 < 24 (comparison, always true1) instead of 1 << 24 (bit shift for 16M colors). This would set tc.colors to 1 instead of 16777216 for xterm-direct terminals.

-        tc.colors = 1 < 24;
+        tc.colors = 1 << 24;
source/dcell/parser.d (1)

1284-1363: Unittest failure at lone‑ESC assertion is consistent with the ESC timeout bug

The failing assertion:

assert(p.parse(['\x1b']) == false);
...
Thread.sleep(msecs(100));
assert(p.parse([]) == true);

expects ESC to be held as a partial sequence until the timeout, then emitted later. With the current msecs(0) check in scan(), parse(['\x1b']) can already return true and push a Key.esc event, tripping this test at line 1326.

Once you change the timeout comparison to use seqTime (as suggested in the scan() comment), this test—and the preceding Alt+O test that relies on the same mechanism—should pass again.

🧹 Nitpick comments (6)
demos/colors/source/colors.d (2)

143-159: Make Ctrl+L detection resilient to additional modifiers

The resize shortcut now relies on:

case Key.rune:
    if (ev.key.ch == 'l' && ev.key.mod == Modifiers.ctrl)

If Modifiers is a bitmask, this will fail when other bits are set (e.g., Ctrl+Meta+L), even though Ctrl is pressed. A more robust check is:

-            case Key.rune:
-                if (ev.key.ch == 'l' && ev.key.mod == Modifiers.ctrl)
+            case Key.rune:
+                if (ev.key.ch == 'l' && (ev.key.mod & Modifiers.ctrl) != 0)

This keeps the behavior for plain Ctrl+L while allowing combined modifiers.

Please confirm this matches the new key/modifier semantics from the input refactor.


166-167: Minor nit: fix typo in error assertion message

The assertion currently says "errror received" (three rs). Consider correcting it to "error received":

-        case EventType.error:
-            assert(0, "errror received");
+        case EventType.error:
+            assert(0, "error received");
source/dcell/key.d (1)

245-259: Verify modifier ordering in toString() is intentional.

The modifier prefix order is now Shift → Meta → Alt → Hyper → Ctrl. This differs from some common conventions (e.g., Ctrl-Shift-Alt). If this ordering is intentional for consistency with tcell or other conventions, consider adding a brief comment.

source/dcell/ttyscreen.d (1)

591-639: poll flag is now unused; consider removing to avoid confusion

poll is set based on p.empty() / finished but never read, so it’s effectively dead state now that all reads go through f.read() with blocking(true/false). Dropping it (and its assignments) would make the loop intent clearer without changing behavior.

source/dcell/parser.d (2)

749-766: Guard CSI‑u modifier decoding to avoid bogus flags when only one parameter is present

In the CSI‑u handler:

case 'u': // CSI-u kitty keyboard protocol
    if (plen > 0)
    {
        Modifiers mod = Modifiers.none;
        Key key = Key.rune;
        dchar chr = 0;
        ...
        evs ~= newKeyEvent(key, chr, calcModifier(p1));
    }
    return;

When plen == 1, p1 remains its default 0, and calcModifier(0) (which does n--) produces nonsensical modifiers. For CSI‑u, the modifier parameter is optional and defaults to “no modifiers”.

A safer implementation:

-        Modifiers mod = Modifiers.none;
+        Modifiers mod = Modifiers.none;
         Key key = Key.rune;
         dchar chr = 0;
         if (p0 in csiUKeys)
         {
             auto k = csiUKeys[p0];
             key = k.key;
         }
         else
         {
             chr = cast(dchar) p0;
         }

-        evs ~= newKeyEvent(key, chr, calcModifier(p1));
+        if (plen > 1 && p1 > 0)
+        {
+            mod = calcModifier(p1);
+        }
+        evs ~= newKeyEvent(key, chr, mod);

This keeps modifiers correct once CSI‑u is enabled without affecting current behavior.

Please double‑check CSI‑u’s exact parameter semantics against the kitty keyboard protocol spec to confirm this mapping.


666-679: handleOsc resets to ParseState.init; consider using ParseState.ini for clarity

handleOsc ends with:

// string is located in scratch.
parseState = ParseState.init;

Using .init here works because it’s the enum’s default (which is ini), but it’s atypical compared to the rest of the file where ParseState.ini is used explicitly. For readability and to avoid confusion with the “ini” state name, consider:

-        parseState = ParseState.init;
+        parseState = ParseState.ini;

Behavior is unchanged; this is just a clarity cleanup.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef7087a and 2922a60.

📒 Files selected for processing (26)
  • demos/colors/source/colors.d (2 hunks)
  • demos/mouse/source/mouse.d (2 hunks)
  • mkinfo/source/mkinfo.d (3 hunks)
  • source/dcell/database.d (0 hunks)
  • source/dcell/event.d (4 hunks)
  • source/dcell/key.d (7 hunks)
  • source/dcell/parser.d (5 hunks)
  • source/dcell/screen.d (3 hunks)
  • source/dcell/termcap.d (0 hunks)
  • source/dcell/terminfo/aixterm.d (0 hunks)
  • source/dcell/terminfo/alacritty.d (0 hunks)
  • source/dcell/terminfo/ansi.d (0 hunks)
  • source/dcell/terminfo/dtterm.d (0 hunks)
  • source/dcell/terminfo/gnome.d (0 hunks)
  • source/dcell/terminfo/konsole.d (0 hunks)
  • source/dcell/terminfo/linux.d (0 hunks)
  • source/dcell/terminfo/rxvt.d (0 hunks)
  • source/dcell/terminfo/screen.d (0 hunks)
  • source/dcell/terminfo/vt100.d (0 hunks)
  • source/dcell/terminfo/vt102.d (0 hunks)
  • source/dcell/terminfo/vt220.d (0 hunks)
  • source/dcell/terminfo/vt320.d (0 hunks)
  • source/dcell/terminfo/vt420.d (0 hunks)
  • source/dcell/terminfo/xfce.d (0 hunks)
  • source/dcell/terminfo/xterm.d (0 hunks)
  • source/dcell/ttyscreen.d (7 hunks)
💤 Files with no reviewable changes (18)
  • source/dcell/terminfo/dtterm.d
  • source/dcell/terminfo/screen.d
  • source/dcell/terminfo/vt320.d
  • source/dcell/terminfo/vt420.d
  • source/dcell/terminfo/alacritty.d
  • source/dcell/database.d
  • source/dcell/terminfo/xfce.d
  • source/dcell/terminfo/vt100.d
  • source/dcell/terminfo/aixterm.d
  • source/dcell/terminfo/vt102.d
  • source/dcell/terminfo/linux.d
  • source/dcell/terminfo/ansi.d
  • source/dcell/terminfo/gnome.d
  • source/dcell/terminfo/xterm.d
  • source/dcell/terminfo/vt220.d
  • source/dcell/terminfo/konsole.d
  • source/dcell/terminfo/rxvt.d
  • source/dcell/termcap.d
🧰 Additional context used
🪛 GitHub Actions: dmd
source/dcell/parser.d

[error] 1326-1326: dub -q test failed: unittest failure at source/dcell/parser.d:1326. Exit code 2.

🔇 Additional comments (22)
source/dcell/key.d (4)

24-26: LGTM!

Good documentation explaining why low key values are reserved for control encodings and how they're now translated.


117-121: LGTM!

The explicit ASCII mappings for basic keys (backspace, tab, esc, enter) are clear and align with standard terminal conventions.


219-231: LGTM!

The hyper modifier follows the existing bit flag pattern consistently.


268-279: LGTM!

The rune handling logic is clear: show raw character when no modifiers, "Space" for space with modifiers, and uppercase for other modified characters.

mkinfo/source/mkinfo.d (3)

1-1: LGTM!

Copyright year update is appropriate.


304-304: LGTM!

Minor spacing adjustment around cast.


435-435: LGTM!

Adding the num variable to track the index in the foreach is appropriate for the term source generation.

source/dcell/screen.d (2)

60-70: LGTM!

The opDollar method body is reformatted for better readability while maintaining identical logic.


123-131: LGTM!

The enableFocus method is a clean addition to the interface with appropriate documentation. This aligns with the new focus event support in the event system.

source/dcell/event.d (3)

27-27: LGTM!

The focus event type is appropriately added to the enum.


46-46: LGTM!

The focus field in the union follows the existing pattern for other event types.


66-71: LGTM!

The FocusEvent struct is minimal and appropriate. The single focused boolean clearly indicates focus gained (true) vs lost (false).

demos/mouse/source/mouse.d (7)

22-28: LGTM!

Indentation standardized to 4 spaces.


103-103: LGTM!

Focus format string added for status display.


108-122: LGTM!

Good additions: enableFocus(true) is called during initialization, and the focused state is properly initialized to true (assuming the window starts focused).


138-138: LGTM!

Focus state is now displayed in the status area.


188-191: LGTM!

Ctrl+L to trigger sync() is a standard terminal convention for screen refresh.


209-210: LGTM!

Hyper modifier handling added, consistent with the new Modifiers.hyper in key.d.


285-287: LGTM!

Focus event handling is clean and correctly updates the focused state.

source/dcell/ttyscreen.d (1)

228-232: Focus-mode enable/disable sequence looks correct

Using CSI ?1004 h/l here matches xterm’s focus-in/out protocol and pairing it with an unconditional flush() is appropriate. The only behavioral change is that stop() now always sends \x1b[?1004l via enableFocus(false), which is a safe cleanup even if focus reporting was never enabled.

source/dcell/parser.d (2)

35-194: Key mapping tables mirror common terminal encodings

The CsiKey struct plus csiAllKeys, ss3Keys, linuxFKeys, csiUKeys, and winKeys tables look consistent with typical xterm/aixterm/linux/kitty/Win32 encodings and cover a wide range of function and navigation keys without obvious overlaps. This should give the new parser a solid base behaviorally.


1087-1096: New focus event helper matches the event layout

newFocusEvent fills type, when, and the focus.focused field, which is exactly what the focus CSI (‘I’/‘O’) handler expects. This keeps focus reporting nicely encapsulated in the parser.

@gdamore gdamore force-pushed the gdamore/no-input-terminfo branch 2 times, most recently from 05c8925 to 39c5d7e Compare December 7, 2025 21:58
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/dcell/parser.d (1)

1087-1123: Use MonoTime.currTime() (with parentheses) in both newMouseEvent overloads

Both overloads are missing parentheses on the MonoTime.currTime function call at lines 1116 and 1131:

type: EventType.mouse, when: MonoTime.currTime, mouse: { ... }

This assigns the function symbol instead of calling it to get a MonoTime value, which will cause a compilation error. All other event constructors (newFocusEvent, newKeyEvent, newPasteEvent) correctly use MonoTime.currTime() with parentheses. Fix both instances by adding ().

♻️ Duplicate comments (1)
source/dcell/parser.d (1)

949-1049: Fix win32 handler: lone modifiers, Key.rune check typo, and event emission

Two issues in handleWinKey will either prevent compilation or yield incorrect behavior:

  1. Lone‑modifier filtering checks key instead of the virtual key code
else if (key == 0x11 || key == 0x13 || key == 0x14)
{
    // lone modifiers
    return;
}

0x11, 0x13, and 0x14 are virtual key codes (Ctrl, Alt, etc.). This should be checking p0, not the key enum, otherwise lone modifier presses will not be filtered.

  1. Repeat‑event guard compares against key.rune
for (; rpt > 0; rpt--)
{
    if (key != key.rune || chr != 0)
    {
        evs ~= newKeyEvent(key, chr, mod);
    }
}

key.rune is a typo; it should be the enum member Key.rune. As written, this won’t compile.

Suggested fix:

-        else if (key == 0x11 || key == 0x13 || key == 0x14)
+        else if (p0 == 0x11 || p0 == 0x13 || p0 == 0x14)
         {
             // lone modifiers
             return;
         }
@@
-        for (; rpt > 0; rpt--)
-        {
-            if (key != key.rune || chr != 0)
-            {
-                evs ~= newKeyEvent(key, chr, mod);
-            }
-        }
+        for (; rpt > 0; rpt--)
+        {
+            // Suppress pure-repeat events where we have no real key/char payload.
+            if (key != Key.rune || chr != 0)
+            {
+                evs ~= newKeyEvent(key, chr, mod);
+            }
+        }

This aligns the logic with the intended semantics (ignore lone modifiers and empty repeats) and fixes the compilation error.

🧹 Nitpick comments (3)
source/dcell/key.d (1)

24-27: Clarify low‑key comment now that ASCII aliases are defined

The comment says the low key range is “empty”, but backspace, tab, enter, and esc are now explicitly assigned low values. Functionally this is fine (and matches how the parser uses ASCII control codes), but the comment is now misleading.

Consider updating the comment to mention that a few well‑known control keys are explicitly aliased in this range while other control bytes are represented as Key.rune with a control modifier.

Also applies to: 116-121

source/dcell/parser.d (2)

35-262: Static CSI/SS3/Linux/win32 key maps look reasonable; consider modularization if they grow further

The KeyCode/CsiKey definitions and the csiAllKeys, ss3Keys, linuxFKeys, csiUKeys, and winKeys tables are straightforward and align with typical terminal encodings. I don’t see functional issues here.

If this table set grows much more, consider moving them into a dedicated “key tables” module to keep the parser implementation itself easier to scan, but that’s purely organizational.


333-372: Unused parser fields/flags and legacy paste path

keyCodes, partial, and the parsePaste/pasteStart/pasteEnd path appear to be unused now:

  • const KeyCode[string] keyCodes; is never read.
  • bool partial is only written in parseSequence and never read.
  • parsePaste() is private and not called anywhere in Parser; bracketed paste is now handled entirely via CSI 200/201 in handleCsi + postKey.

If terminfo‑based paste handling is no longer planned, these can be removed to reduce mental overhead; otherwise it may be worth either wiring them back into the scan path or adding a brief comment indicating they’re reserved for future use.

Also applies to: 1183-1236

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05c8925 and 39c5d7e.

📒 Files selected for processing (26)
  • demos/colors/source/colors.d (2 hunks)
  • demos/mouse/source/mouse.d (2 hunks)
  • mkinfo/source/mkinfo.d (4 hunks)
  • source/dcell/database.d (0 hunks)
  • source/dcell/event.d (4 hunks)
  • source/dcell/key.d (7 hunks)
  • source/dcell/parser.d (5 hunks)
  • source/dcell/screen.d (3 hunks)
  • source/dcell/termcap.d (0 hunks)
  • source/dcell/terminfo/aixterm.d (0 hunks)
  • source/dcell/terminfo/alacritty.d (0 hunks)
  • source/dcell/terminfo/ansi.d (0 hunks)
  • source/dcell/terminfo/dtterm.d (0 hunks)
  • source/dcell/terminfo/gnome.d (0 hunks)
  • source/dcell/terminfo/konsole.d (0 hunks)
  • source/dcell/terminfo/linux.d (0 hunks)
  • source/dcell/terminfo/rxvt.d (0 hunks)
  • source/dcell/terminfo/screen.d (0 hunks)
  • source/dcell/terminfo/vt100.d (0 hunks)
  • source/dcell/terminfo/vt102.d (0 hunks)
  • source/dcell/terminfo/vt220.d (0 hunks)
  • source/dcell/terminfo/vt320.d (0 hunks)
  • source/dcell/terminfo/vt420.d (0 hunks)
  • source/dcell/terminfo/xfce.d (0 hunks)
  • source/dcell/terminfo/xterm.d (0 hunks)
  • source/dcell/ttyscreen.d (7 hunks)
💤 Files with no reviewable changes (18)
  • source/dcell/terminfo/vt102.d
  • source/dcell/database.d
  • source/dcell/terminfo/screen.d
  • source/dcell/terminfo/vt420.d
  • source/dcell/terminfo/gnome.d
  • source/dcell/terminfo/xfce.d
  • source/dcell/terminfo/alacritty.d
  • source/dcell/terminfo/ansi.d
  • source/dcell/terminfo/konsole.d
  • source/dcell/terminfo/vt100.d
  • source/dcell/terminfo/aixterm.d
  • source/dcell/terminfo/vt220.d
  • source/dcell/terminfo/vt320.d
  • source/dcell/terminfo/xterm.d
  • source/dcell/terminfo/rxvt.d
  • source/dcell/terminfo/linux.d
  • source/dcell/terminfo/dtterm.d
  • source/dcell/termcap.d
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/dcell/screen.d
🔇 Additional comments (21)
demos/colors/source/colors.d (3)

149-156: LGTM: Ctrl+L handling updated to use rune-based detection.

The change from Key.ctrlL to checking Key.rune with ev.key.ch == 'l' and ev.key.mod == Modifiers.ctrl aligns with the PR's goal of removing per-terminal special key parsing. Control keys are now conveyed as modified runes.


103-104: RGB packing is now correct.

The red component is properly shifted by 16 bits (int(r) << 16 | int(g) << 8 | int(b)), producing the expected 0xRRGGBB format for fromHex.


167-168: Typo fix acknowledged.

The error message correction from "errror received" to "error received" is a good cleanup.

source/dcell/event.d (2)

27-27: Clean addition of focus event type to the enum.

The new focus member follows the existing naming convention and placement in EventType.


66-71: Well-structured FocusEvent definition.

The FocusEvent struct with a single focused boolean is appropriately minimal and follows the pattern of other event structs in this file.

demos/mouse/source/mouse.d (4)

112-112: Focus reporting enabled correctly.

The call to s.enableFocus(true) at startup enables focus event reporting, aligning with the new feature introduced in this PR.


128-140: UI properly expanded to display focus state.

The box height increased from 7 to 8 (Coord(42, 8)) and the focus state is displayed using the focusFmt format string. The layout accommodates the new field correctly.


286-288: Focus event handling integrated correctly.

The EventType.focus case properly updates the focused state from the event payload.


188-192: Ctrl+L handling updated consistently with colors demo.

The rune-based detection for Ctrl+L redraw follows the same pattern as in colors.d, using ev.key.ch == 'l' && ev.key.mod == Modifiers.ctrl.

source/dcell/ttyscreen.d (4)

228-232: Focus reporting implementation uses standard escape sequences.

The implementation correctly uses \x1b[?1004h to enable and \x1b[?1004l to disable focus reporting, which is the de-facto standard (DECSET/DECRST mode 1004) used by xterm and compatible terminals.


94-94: Focus properly disabled during stop sequence.

Calling enableFocus(false) before cleanup ensures focus reporting is disabled when the screen stops, which is important for proper terminal state restoration.


591-595: Parser initialization simplified.

The removal of the ParseKeys parameter and use of new Parser() aligns with the PR's objective to eliminate per-terminal special key parsing. The new Parser handles input processing without terminal-specific key tables.


609-627: Improved parse completion handling.

Capturing the finished return value from p.parse(s) and using it in the blocking decision (!p.empty() || !finished) ensures the input loop continues processing when the parser is in the middle of a multi-byte sequence, preventing incomplete escape sequence handling.

mkinfo/source/mkinfo.d (3)

345-358: Critical bug fix: RGB color count now correctly uses bitshift.

The change from 1 < 24 (comparison, yielding true/1) to 1 << 24 (bitshift, yielding 16,777,216) fixes a significant bug in the RGB color path. The Tc flag path at line 348 was already correct; this brings the -RGB flag path in line.


304-304: Minor formatting adjustment.

The cast spacing change is stylistic only.


435-435: Minor formatting adjustment in foreach.

The variable declaration formatting change is stylistic only.

source/dcell/key.d (1)

219-231: Modifier flags and KeyEvent.toString wiring look consistent

The added hyper bit and the updated toString logic (Ctrl/Shift/Meta/Alt/Hyper prefixes plus rune naming rules) are coherent with the new modifier model and the parser’s calcModifier behavior. The UTF‑8 rune handling and control‑rune mapping also look correct.

No changes needed here.

Also applies to: 242-291

source/dcell/parser.d (4)

681-858: CSI handling for focus, mouse, kitty CSI‑u, win32, and modified keys looks coherent

The handleCsi implementation correctly:

  • Rejects intermediates for now.
  • Routes <...m/M to handleMouse.
  • Emits focus in/out events for CSI I / CSI O.
  • Supports linux CSI [ ... F‑key mode.
  • Decodes kitty CSI‑u, win32 _ sequences, XTerm bracketed paste 200/201, and xterm modifyOtherKeys.

Parameter parsing with split/to!int and the use of csiAllKeys, ss3Keys, and csiUKeys looks consistent with the key tables, and the fallback logic in default avoids swallowing unknown sequences silently.

No functional issues spotted here.


860-947: Mouse handler matches XTerm protocol and debouncing behavior

handleMouse correctly:

  • Normalizes coordinates to 0‑based.
  • Handles motion vs scroll vs release semantics, using buttonDown to debounce bogus motion events.
  • Decodes buttons and wheel directions from btn & 0x43.
  • Extracts Shift/Alt/Ctrl modifiers from the corresponding bits.
  • Emits a single newMouseEvent with position, button, and modifiers.

The logic matches XTerm/SGR expectations; nothing to change here.


1051-1085: Hyper bit integration in calcModifier is consistent with Modifiers enum

calcModifier maps the CSI modifier parameter bits onto:

  • shift, alt, ctrl
  • meta (for both kitty “Super” and “super2” bits)
  • hyper (bit 16)

This lines up with the Modifiers enum (including the new hyper flag) and the prefixes emitted by KeyEvent.toString.

No changes needed.


1284-1363: Unittest exercises key parsing, ESC timeout, paste, mouse, and Unicode correctly

The updated unittest:

  • Instantiates Parser as a class (new Parser()), then checks basic CSI right‑arrow decoding.
  • Verifies ESC+SS3 timeout behavior (Thread.sleep(msecs(200)) vs seqTime == 50ms).
  • Confirms lone ESC handling, bracketed paste CSI 200/201, mouse event decoding, and UTF‑8 rune parsing.

Given the fixes above, this provides solid coverage of the new parser behavior. No additional test changes needed.

This is a complete redesign of the input scanner, using logic derived
from tcell.  It eliminates the per-terminal special parsing for keys,
so that we can process keys from almost anything.

This adds parsing support (not enabled yet) for CSI-u (kitty protocol),
Xterm modifierOtherKeys, and win32-input-mode protocols.  (These will
be enabled in follow up.)

As a bonus, this adds suppot for focus reporting, so that we get to
now when the application receives or loses focus.  The mouse demo is
updated to reflect that.

One consequence of this is that the key definitions for all the various
control keys are gone, as these are now conveyed as a modified Key rune.

The key definitions (and also PadChar) are removed from the terminfo
definitions.

This is a breaking change, but we are still pre-1.0 so we didn't have any
promises anyway.
@gdamore gdamore force-pushed the gdamore/no-input-terminfo branch from 39c5d7e to 712e16a Compare December 8, 2025 01:21
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
source/dcell/parser.d (1)

1044-1044: Verify typo fix at line 1044.

The past review comment indicates that line 1044 had a typo (key != key.rune should be Key.rune), and it was supposedly addressed in commits 848742a to 05c8925. However, the code still shows:

if (key != key.rune || chr != 0)

This should be:

if (key != Key.rune || chr != 0)

Please verify that the fix was properly applied or if this represents a regression.

🧹 Nitpick comments (1)
demos/colors/source/colors.d (1)

13-18: Nested std.random import is redundant with module‑level import

Since std.random is already imported at the module level, the inner import std.random; inside ColorBoxes is unnecessary. You can drop the nested import to keep imports a bit leaner, though it’s purely cosmetic.

Also applies to: 22-22

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39c5d7e and 712e16a.

📒 Files selected for processing (26)
  • demos/colors/source/colors.d (2 hunks)
  • demos/mouse/source/mouse.d (2 hunks)
  • mkinfo/source/mkinfo.d (4 hunks)
  • source/dcell/database.d (0 hunks)
  • source/dcell/event.d (4 hunks)
  • source/dcell/key.d (7 hunks)
  • source/dcell/parser.d (5 hunks)
  • source/dcell/screen.d (3 hunks)
  • source/dcell/termcap.d (0 hunks)
  • source/dcell/terminfo/aixterm.d (0 hunks)
  • source/dcell/terminfo/alacritty.d (0 hunks)
  • source/dcell/terminfo/ansi.d (0 hunks)
  • source/dcell/terminfo/dtterm.d (0 hunks)
  • source/dcell/terminfo/gnome.d (0 hunks)
  • source/dcell/terminfo/konsole.d (0 hunks)
  • source/dcell/terminfo/linux.d (0 hunks)
  • source/dcell/terminfo/rxvt.d (0 hunks)
  • source/dcell/terminfo/screen.d (0 hunks)
  • source/dcell/terminfo/vt100.d (0 hunks)
  • source/dcell/terminfo/vt102.d (0 hunks)
  • source/dcell/terminfo/vt220.d (0 hunks)
  • source/dcell/terminfo/vt320.d (0 hunks)
  • source/dcell/terminfo/vt420.d (0 hunks)
  • source/dcell/terminfo/xfce.d (0 hunks)
  • source/dcell/terminfo/xterm.d (0 hunks)
  • source/dcell/ttyscreen.d (7 hunks)
💤 Files with no reviewable changes (18)
  • source/dcell/terminfo/vt102.d
  • source/dcell/terminfo/konsole.d
  • source/dcell/terminfo/xfce.d
  • source/dcell/terminfo/vt320.d
  • source/dcell/terminfo/aixterm.d
  • source/dcell/terminfo/ansi.d
  • source/dcell/terminfo/vt100.d
  • source/dcell/terminfo/alacritty.d
  • source/dcell/terminfo/vt420.d
  • source/dcell/terminfo/linux.d
  • source/dcell/database.d
  • source/dcell/terminfo/screen.d
  • source/dcell/terminfo/xterm.d
  • source/dcell/terminfo/gnome.d
  • source/dcell/terminfo/dtterm.d
  • source/dcell/terminfo/rxvt.d
  • source/dcell/terminfo/vt220.d
  • source/dcell/termcap.d
🔇 Additional comments (23)
demos/colors/source/colors.d (3)

22-48: RNG state, color components, and initialization look solid

Fields, flip(), and the constructor correctly seed and use the RNG, initialize RGB channels and step sizes, and bias green changes as intended; no functional issues spotted here.


50-105: Color stepping and RGB packing into fromHex are now correct

The incremental RGB stepping with bounds checks keeps components in range and sensibly flips direction, and the background color now packs as int(r) << 16 | int(g) << 8 | int(b), matching a standard 0xRRGGBB layout and addressing the prior off‑by‑one shift.


135-175: Updated Ctrl+L handling via rune+modifier matches the new key model

Switching from Key.ctrlL to checking for Key.rune with ev.key.ch == 'l' && ev.key.mod == Modifiers.ctrl aligns this demo with the new parser’s rune+modifier representation while still enforcing “Ctrl‑L with no other modifiers” as the resize trigger; the rest of the event loop logic remains consistent.

source/dcell/event.d (1)

4-71: LGTM! Focus event integration is well-structured.

The new FocusEvent struct and its integration into the Event union follow the established pattern for other event types. The focused boolean field clearly tracks focus state.

source/dcell/key.d (3)

24-122: LGTM! Control key handling refactored cleanly.

The explicit ASCII values for common keys (backspace, tab, esc, enter, del) are clear and standard. The approach of translating control encodings to runes with the ctrl modifier (as noted in the comments) simplifies the key model.


219-231: LGTM! Hyper modifier added consistently.

The hyper modifier follows the established bitmask pattern (1 << 4) and fits naturally into the modifier scheme.


242-291: LGTM! toString() improvements enhance key event display.

The updated logic handles modifiers systematically (Ctrl-, Shift-, Hyper- prefixes), and the rune-handling refinements are sensible:

  • Raw character when no modifiers present
  • "Space" label for space with modifiers
  • Uppercase for other characters with modifiers
source/dcell/parser.d (6)

29-309: LGTM! Comprehensive key mapping tables.

The key mapping tables are well-organized and cover a wide range of terminal types and protocols:

  • CSI sequences with composite keys (mode + parameter)
  • SS3 application mode keys
  • Linux console F-keys
  • Kitty CSI-u protocol
  • Windows virtual key codes

The use of immutable tables is appropriate for this static data.


311-388: LGTM! Parser class structure is well-designed.

The public API is minimal and clear (events(), parse(), empty()), and the internal ParseState enum comprehensively covers all parsing states. The postKey() helper correctly distinguishes between pasting mode (accumulating in buffer) and normal mode (emitting events).


390-664: LGTM! State machine implementation is thorough and correct.

The parsing state machine properly handles:

  • UTF-8 multi-byte sequences with accumulation and decoding
  • Control key translation to runes with ctrl modifier
  • Escape sequence state transitions
  • ESC timeout using seqTime (the past issue with msecs(0) has been correctly fixed)

The logic is sound and comprehensive.


681-858: LGTM! CSI handler covers modern terminal protocols.

The handleCsi() method comprehensively handles:

  • Focus in/out events (lines 740-745)
  • Kitty CSI-u keyboard protocol
  • Win32-input-mode
  • XTerm modifyOtherKeys
  • Bracketed paste mode (CSI 200/201)
  • Standard CSI sequences with parameter-based lookups

The implementation correctly uses calcModifier() for modifier parsing and properly generates focus and paste events.


1051-1181: LGTM! Event generation helpers are well-implemented.

The helper methods (calcModifier(), newFocusEvent(), newKeyEvent(), newMouseEvent(), newPasteEvent()) correctly:

  • Map CSI modifier parameters to the Modifiers enum (including hyper)
  • Handle Alt-escaped keys via the escaped flag
  • Create properly-structured Event instances with timestamps

1284-1363: LGTM! Comprehensive unittest coverage.

The unittest validates:

  • SS3 sequence parsing (right arrow)
  • ESC timeout handling for Alt-escaped keys and lone ESC
  • Bracketed paste mode
  • SGR mouse events
  • UTF-8 decoding (Euro symbol)

The test properly uses Thread.sleep() to validate timeout behavior.

source/dcell/screen.d (1)

4-130: LGTM! Focus support added to Screen interface.

The new enableFocus(bool) method is well-documented and provides the expected interface for enabling focus reporting. The formatting adjustment to opDollar is cosmetic and doesn't affect behavior.

demos/mouse/source/mouse.d (2)

92-141: LGTM! Focus tracking integrated into demo.

The demo properly:

  • Enables focus reporting via s.enableFocus(true)
  • Tracks focus state with the focused boolean
  • Displays focus status in the UI

The initial assumption of focused = true is reasonable for a newly started application.


170-292: LGTM! Event handling enhanced with focus and precise modifier checking.

The event handling improvements:

  • Ctrl-L now correctly validates mod == Modifiers.ctrl (line 189), preventing unintended trigger from other modifier combinations
  • Hyper modifier display added for completeness (line 210-211)
  • Focus event properly updates the focused state (lines 286-288)
source/dcell/ttyscreen.d (3)

56-102: LGTM! Parser refactoring simplifies screen initialization.

The removal of the ParseKeys parameter from spawn() simplifies the initialization, and the addition of enableFocus(false) in stop() ensures proper cleanup of focus reporting.


228-232: LGTM! Focus reporting implementation is standard-compliant.

The enableFocus() method correctly uses the XTerm focus reporting sequences (\x1b[?1004h to enable, \x1b[?1004l to disable) and flushes immediately for proper effect.


591-643: LGTM! Input loop updated for new Parser API.

The inputLoop() refactoring:

  • Removes ParseKeys dependency, using the new Parser class
  • Captures parse() return value in finished to track parsing state
  • Correctly uses !p.empty() || !finished to determine when to use non-blocking I/O (line 627)

The blocking/non-blocking logic properly optimizes I/O by only blocking when the parser buffer is empty and parsing is complete.

mkinfo/source/mkinfo.d (4)

1-1: Copyright header year update is appropriate

Header reflects 2025 while keeping license text intact; no issues here.


300-307: Alias cast remains a safe zero‑copy handoff

tc.aliases = cast(immutable(string)[]) caps.aliases; continues to avoid reallocating alias strings while enforcing the desired immutability on the Termcap side. Given that caps.aliases ultimately points into GC‑managed infocmp output, the lifetime is sufficient for this use.


345-356: tc.colors = 1 << 24 correctly signals 24‑bit truecolor

Using 1 << 24 for both Tc and RGB flags is a clear and conventional sentinel for 24‑bit color support, and it cooperates correctly with the later tc.colors < 8 guard and the explicit setFg/setBg overrides.


435-462: mkTermSource iteration stays resilient to Termcap layout changes

The spacing tweak in foreach (num, Termcap* tc; tcs) is cosmetic, and the combination of FieldNameTuple!Termcap with the type‑based static if blocks keeps the generator aligned with the current Termcap fields (including the recent removals) without manual field maintenance.

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.

2 participants