#7

Ctrl+C and TUI modifier keybindings do not reach terminal programs

Openm-i-z-u-k-iOwneropened 1d ago

Summary

Keys that carry modifiers do not reach terminal programs. In particular Ctrl+C does not interrupt the running process, and TUI keybindings (Ctrl+P/Ctrl+N/Ctrl+D/Ctrl+W and similar) do nothing or trigger the client's own shortcut instead. Plain text typing does reach the shell, so the terminal stream and sendTyped path work; the problem is the OnKey/client.key path.

Expected: a key pressed in a focused terminal pane, including Ctrl+C, goes to Rex through client.key so Rex's Lua keymap gets the first chance and then the program receives it, exactly as in Rex.app. Custom Rex bindings and sequences (rex.bind("ctrl+b>c", ...)) should work too.

Actual: modifier keys appear to be swallowed by the client. Ctrl+C leaves a foreground process running, and TUIs never see their modifier bindings.

Reproduce

  1. Start a Rex server and rexnux, and open a session with a shell.
  2. Run sleep 300, then press Ctrl+C. The process keeps running.
  3. Run a TUI with modifier bindings (btop, lazygit, vim, etc.) and press them. The binding does not fire; some open the palette or split/close a pane instead.
  4. Add a custom Rex key sequence (rex.bind("ctrl+b>c", "window.new")) and press it. It never fires.

Ordinary typing (echo hi) works and appears in the Rex capture, so this is specific to modified/special keys.

Suspects in this client

  • internal/ui/workspace.go (appShortcut): unconditionally takes Ctrl/Cmd + K, P, N, T, D, W and , inside every terminal. That is exactly the set many TUIs bind (Ctrl+P in fzf/vim, Ctrl+N/Ctrl+D in shells and editors, Ctrl+W in readline), so those can never reach the program.
  • internal/ui/term.go (ensureTerm -> OnKey): sends only action: "press" events and never releases. It also has no path for Rex key sequences (prefix), so custom rex.bind("ctrl+b>c") bindings cannot work.
  • internal/ui/workspace.go (terminalBlock): calls tv.Focus() only when view.ActiveWindowID == w.WindowID and the block matches w.FocusedBlockID or a.focusBlock. A stale view, a preview/peek window, or a pane-tab group can leave no terminal focused, in which case OnKey is never called at all. Worth checking that the active terminal always owns keyboard focus, and that clicking a pane re-focuses its terminal (not just the Rex focus_block call).
  • internal/rex/api.go (Client.Key): KeyEvent has no prefix field. Rex documents prefix for multi-key bindings; without it the server can never resolve a sequence.
  • internal/ui/term.go (keyInfo): maps Ctrl+letters to key_* with empty text, which matches the API doc's example, but it should be checked against a real server while fixing this.

Related: #2 is the same input-routing problem for pointer events. Maybe both should be fixed together, since both come from the terminal element's input handling versus the pane card's own handlers.

Environment

  • rexnux 76952ad (main, after #6)
  • MyGo v0.7.4, terminal plugin (libghostty-vt)
  • Go 1.27.3
  • Linux/Wayland, Hyprland
  • Rex server 0.1.0-preview.12, API revision 8

Activity

1
m-i-z-u-k-i added bug1d ago