2024-03-30 16:10:12 +01:00
|
|
|
use crate::common::{
|
2024-10-12 10:49:03 +02:00
|
|
|
fish_reserved_codepoint, is_windows_subsystem_for_linux, read_blocked, shell_modes,
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
str2wcstring, ScopeGuard, WSL,
|
2024-03-30 16:10:12 +01:00
|
|
|
};
|
2023-10-08 23:22:27 +02:00
|
|
|
use crate::env::{EnvStack, Environment};
|
2025-01-04 18:22:38 -06:00
|
|
|
use crate::fd_readable_set::{FdReadableSet, Timeout};
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
use crate::flog::{FloggableDebug, FLOG};
|
2024-10-21 09:16:02 +02:00
|
|
|
use crate::fork_exec::flog_safe::FLOG_SAFE;
|
2024-04-18 10:21:28 +02:00
|
|
|
use crate::global_safety::RelaxedAtomicBool;
|
2024-03-30 16:10:12 +01:00
|
|
|
use crate::key::{
|
2025-01-04 12:36:16 +01:00
|
|
|
self, alt, canonicalize_control_char, canonicalize_keyed_control_char, char_to_symbol, ctrl,
|
|
|
|
|
function_key, shift, Key, Modifiers, ViewportPosition,
|
2024-03-30 16:10:12 +01:00
|
|
|
};
|
|
|
|
|
use crate::reader::{reader_current_data, reader_test_and_clear_interrupted};
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
use crate::terminal::TerminalCommand::{
|
|
|
|
|
ApplicationKeypadModeDisable, ApplicationKeypadModeEnable, DecrstBracketedPaste,
|
|
|
|
|
DecrstFocusReporting, DecsetBracketedPaste, DecsetFocusReporting,
|
|
|
|
|
KittyKeyboardProgressiveEnhancementsDisable, KittyKeyboardProgressiveEnhancementsEnable,
|
|
|
|
|
ModifyOtherKeysDisable, ModifyOtherKeysEnable,
|
|
|
|
|
};
|
|
|
|
|
use crate::terminal::{
|
|
|
|
|
Capability, Output, Outputter, KITTY_KEYBOARD_SUPPORTED, SCROLL_FORWARD_SUPPORTED,
|
Replace synchronized update workaround
Old versions of ConHost and Putty can't parse DCS sequences.
For this reason, we briefly switch to the alternate screen buffer while sending
DCS-format (e.g. XTGETTCAP) queries. For extra paranoia, we wrapped this
procedure in a synchronized update. This doesn't seem to be needed; neither
ConHost nor Putty show glitches when the synchronized update is omitted.
As of today, every terminal that implements XTGETTCAP also implements
synchronized updates but that might change.
Let's remove it, to reduce surprise for users and terminal developers.
As a bonus, this also fixes a glitch on Terminal.app which fails to parse
the synchronized-update query (`printf '\x1b[?2026$p'`) and echoes the "p"
(bug report ID FB17141059). Else we could work around this with another
alternate screen buffer.
Unfortunately, this change surfaces two issues with GNU screen. For one,
they don't allow apps to use the alternate screen features (the user may
allow it with "altscreen on"). Second, screen unconditionally echoes
the payload of DCS commands. A possible fix has been suggested at
https://lists.gnu.org/archive/html/screen-devel/2025-04/msg00010.html
I think this combination of behaviors is unique among terminals. I'm sure
there are more terminals that don't parse DCS commands yet, but I think almost
all terminals implement alternate screen buffer. Probably only terminal
multiplexers are prone to this issue. AFAICT very few multiplexers exists,
so we can work around those until they are fixed.
Disable XTGETTCAP queries for GNU screen specifically. Unfortunately screen
does not implement XTVERSION, so I don't know how to reliably identify
it. Instead, check STY and some commonly-used values TERM values.
This has false negatives in some edge cases.
But the worst thing that happens is that "+q696e646e" will be echoed once
at startup, which is easy to ignore, or work around with something like
function workaround --on-event fish_prompt
commandline -f repaint
functions --erase workaround
end
which I don't think we should apply by default, because it can mask other
issues.
We should give screen more time to respond. I guess I'll open an issue so
we don't forget. In doubt, we can always go back to the previous approach
(but implement it in fish script).
Alternative workaround: instead of the alternative screen buffer, we could
try something like clr_eol/clr_bol to erase the spuriously echoed text. I
tried to do this in various ways but (surprisingly) failed.
2025-04-25 21:06:37 +02:00
|
|
|
SCROLL_FORWARD_TERMINFO_CODE,
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
};
|
2024-10-12 09:00:24 +02:00
|
|
|
use crate::threads::{iothread_port, is_main_thread};
|
2023-11-25 14:37:42 -08:00
|
|
|
use crate::universal_notifier::default_notifier;
|
2024-02-27 22:25:54 +01:00
|
|
|
use crate::wchar::{encode_byte_to_char, prelude::*};
|
2024-03-30 16:10:12 +01:00
|
|
|
use crate::wutil::encoding::{mbrtowc, mbstate_t, zero_mbstate};
|
2024-12-29 14:36:11 +01:00
|
|
|
use crate::wutil::fish_wcstol;
|
2025-04-28 20:35:34 +02:00
|
|
|
use std::cell::{RefCell, RefMut};
|
2023-11-25 14:37:42 -08:00
|
|
|
use std::collections::VecDeque;
|
|
|
|
|
use std::os::fd::RawFd;
|
2024-10-12 10:49:03 +02:00
|
|
|
use std::os::unix::ffi::OsStrExt;
|
2023-11-25 14:37:42 -08:00
|
|
|
use std::ptr;
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
2025-03-04 09:23:02 +01:00
|
|
|
use std::time::Duration;
|
2023-10-08 23:22:27 +02:00
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
// The range of key codes for inputrc-style keyboard functions.
|
2024-01-01 21:29:05 +01:00
|
|
|
pub const R_END_INPUT_FUNCTIONS: usize = (ReadlineCmd::ReverseRepeatJump as usize) + 1;
|
2023-12-17 15:41:14 -08:00
|
|
|
|
2024-01-01 21:29:05 +01:00
|
|
|
/// Hackish: the input style, which describes how char events (only) are applied to the command
|
|
|
|
|
/// line. Note this is set only after applying bindings; it is not set from readb().
|
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
|
pub enum CharInputStyle {
|
|
|
|
|
// Insert characters normally.
|
|
|
|
|
Normal,
|
|
|
|
|
|
|
|
|
|
// Insert characters only if the cursor is not at the beginning. Otherwise, discard them.
|
|
|
|
|
NotFirst,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
|
|
|
#[repr(u8)]
|
|
|
|
|
pub enum ReadlineCmd {
|
|
|
|
|
BeginningOfLine,
|
|
|
|
|
EndOfLine,
|
|
|
|
|
ForwardChar,
|
|
|
|
|
BackwardChar,
|
2024-03-29 14:09:34 -05:00
|
|
|
BackwardCharPassive,
|
2024-01-01 21:29:05 +01:00
|
|
|
ForwardSingleChar,
|
2024-03-28 00:13:34 -05:00
|
|
|
ForwardCharPassive,
|
2024-01-01 21:29:05 +01:00
|
|
|
ForwardWord,
|
|
|
|
|
BackwardWord,
|
|
|
|
|
ForwardBigword,
|
|
|
|
|
BackwardBigword,
|
2024-09-21 15:55:44 +00:00
|
|
|
ForwardToken,
|
|
|
|
|
BackwardToken,
|
2024-01-01 21:29:05 +01:00
|
|
|
NextdOrForwardWord,
|
|
|
|
|
PrevdOrBackwardWord,
|
2025-01-10 09:09:35 +01:00
|
|
|
HistoryDelete,
|
2024-01-01 21:29:05 +01:00
|
|
|
HistorySearchBackward,
|
|
|
|
|
HistorySearchForward,
|
|
|
|
|
HistoryPrefixSearchBackward,
|
|
|
|
|
HistoryPrefixSearchForward,
|
|
|
|
|
HistoryPager,
|
2025-01-10 09:09:35 +01:00
|
|
|
#[deprecated]
|
2024-01-01 21:29:05 +01:00
|
|
|
HistoryPagerDelete,
|
|
|
|
|
DeleteChar,
|
|
|
|
|
BackwardDeleteChar,
|
|
|
|
|
KillLine,
|
|
|
|
|
Yank,
|
|
|
|
|
YankPop,
|
|
|
|
|
Complete,
|
|
|
|
|
CompleteAndSearch,
|
|
|
|
|
PagerToggleSearch,
|
|
|
|
|
BeginningOfHistory,
|
|
|
|
|
EndOfHistory,
|
|
|
|
|
BackwardKillLine,
|
|
|
|
|
KillWholeLine,
|
|
|
|
|
KillInnerLine,
|
|
|
|
|
KillWord,
|
|
|
|
|
KillBigword,
|
2024-09-21 15:55:44 +00:00
|
|
|
KillToken,
|
2024-01-01 21:29:05 +01:00
|
|
|
BackwardKillWord,
|
|
|
|
|
BackwardKillPathComponent,
|
|
|
|
|
BackwardKillBigword,
|
2024-09-21 15:55:44 +00:00
|
|
|
BackwardKillToken,
|
2024-01-01 21:29:05 +01:00
|
|
|
HistoryTokenSearchBackward,
|
|
|
|
|
HistoryTokenSearchForward,
|
2025-03-11 13:19:46 +08:00
|
|
|
HistoryLastTokenSearchBackward,
|
|
|
|
|
HistoryLastTokenSearchForward,
|
2024-01-01 21:29:05 +01:00
|
|
|
SelfInsert,
|
|
|
|
|
SelfInsertNotFirst,
|
|
|
|
|
TransposeChars,
|
|
|
|
|
TransposeWords,
|
|
|
|
|
UpcaseWord,
|
|
|
|
|
DowncaseWord,
|
|
|
|
|
CapitalizeWord,
|
|
|
|
|
TogglecaseChar,
|
|
|
|
|
TogglecaseSelection,
|
|
|
|
|
Execute,
|
|
|
|
|
BeginningOfBuffer,
|
|
|
|
|
EndOfBuffer,
|
|
|
|
|
RepaintMode,
|
|
|
|
|
Repaint,
|
|
|
|
|
ForceRepaint,
|
|
|
|
|
UpLine,
|
|
|
|
|
DownLine,
|
|
|
|
|
SuppressAutosuggestion,
|
|
|
|
|
AcceptAutosuggestion,
|
|
|
|
|
BeginSelection,
|
|
|
|
|
SwapSelectionStartStop,
|
|
|
|
|
EndSelection,
|
|
|
|
|
KillSelection,
|
|
|
|
|
InsertLineUnder,
|
|
|
|
|
InsertLineOver,
|
|
|
|
|
ForwardJump,
|
|
|
|
|
BackwardJump,
|
|
|
|
|
ForwardJumpTill,
|
|
|
|
|
BackwardJumpTill,
|
2024-06-28 23:23:36 +02:00
|
|
|
JumpToMatchingBracket,
|
2024-06-29 02:20:15 +02:00
|
|
|
JumpTillMatchingBracket,
|
2024-01-01 21:29:05 +01:00
|
|
|
FuncAnd,
|
|
|
|
|
FuncOr,
|
|
|
|
|
ExpandAbbr,
|
|
|
|
|
DeleteOrExit,
|
|
|
|
|
Exit,
|
2025-01-14 19:58:26 +01:00
|
|
|
ClearCommandline,
|
2024-01-01 21:29:05 +01:00
|
|
|
CancelCommandline,
|
|
|
|
|
Cancel,
|
|
|
|
|
Undo,
|
|
|
|
|
Redo,
|
|
|
|
|
BeginUndoGroup,
|
|
|
|
|
EndUndoGroup,
|
|
|
|
|
RepeatJump,
|
|
|
|
|
ClearScreenAndRepaint,
|
2024-12-21 19:41:41 +01:00
|
|
|
ScrollbackPush,
|
2024-01-01 21:29:05 +01:00
|
|
|
// NOTE: This one has to be last.
|
|
|
|
|
ReverseRepeatJump,
|
|
|
|
|
}
|
2023-11-25 14:37:42 -08:00
|
|
|
|
2025-03-30 08:33:24 +02:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
|
pub struct KeyEvent {
|
|
|
|
|
pub key: Key,
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
pub shifted_codepoint: char,
|
2025-03-30 08:33:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl KeyEvent {
|
|
|
|
|
pub(crate) fn new(modifiers: Modifiers, codepoint: char) -> Self {
|
|
|
|
|
Self::from(Key::new(modifiers, codepoint))
|
|
|
|
|
}
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
pub(crate) fn with_shifted_codepoint(
|
|
|
|
|
modifiers: Modifiers,
|
|
|
|
|
codepoint: char,
|
|
|
|
|
shifted_codepoint: Option<char>,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
key: Key::new(modifiers, codepoint),
|
|
|
|
|
shifted_codepoint: shifted_codepoint.unwrap_or_default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-30 08:33:24 +02:00
|
|
|
pub(crate) fn from_raw(codepoint: char) -> Self {
|
|
|
|
|
Self::from(Key::from_raw(codepoint))
|
|
|
|
|
}
|
|
|
|
|
pub fn from_single_byte(c: u8) -> Self {
|
|
|
|
|
Self::from(Key::from_single_byte(c))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Key> for KeyEvent {
|
|
|
|
|
fn from(key: Key) -> Self {
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
Self {
|
|
|
|
|
key,
|
|
|
|
|
shifted_codepoint: '\0',
|
|
|
|
|
}
|
2025-03-30 08:33:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::ops::Deref for KeyEvent {
|
|
|
|
|
type Target = Key;
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
|
&self.key
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::ops::DerefMut for KeyEvent {
|
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
|
&mut self.key
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
fn apply_shift(mut key: Key, do_ascii: bool, shifted_codepoint: char) -> Option<Key> {
|
|
|
|
|
if !key.modifiers.shift {
|
|
|
|
|
return Some(key);
|
|
|
|
|
}
|
|
|
|
|
if shifted_codepoint != '\0' {
|
|
|
|
|
key.codepoint = shifted_codepoint;
|
|
|
|
|
} else if do_ascii && key.codepoint.is_ascii_lowercase() {
|
|
|
|
|
// For backwards compatibility, we convert the "bind shift-a" notation to "bind A".
|
|
|
|
|
// This enables us to match "A" events which are the legacy encoding for keys that
|
|
|
|
|
// generate text -- until we request kitty's "Report all keys as escape codes".
|
|
|
|
|
// We do not currently convert non-ASCII key notation such as "bind shift-ä".
|
|
|
|
|
key.codepoint = key.codepoint.to_ascii_uppercase();
|
|
|
|
|
} else {
|
|
|
|
|
return None;
|
|
|
|
|
};
|
|
|
|
|
key.modifiers.shift = false;
|
|
|
|
|
Some(key)
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-30 08:33:24 +02:00
|
|
|
impl PartialEq<Key> for KeyEvent {
|
|
|
|
|
fn eq(&self, key: &Key) -> bool {
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
if &self.key == key {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let Some(shifted_evt) = apply_shift(self.key, false, self.shifted_codepoint) else {
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
let Some(shifted_key) = apply_shift(*key, true, '\0') else {
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
shifted_evt == shifted_key
|
2025-03-30 08:33:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
#[test]
|
|
|
|
|
fn test_key_event_eq() {
|
|
|
|
|
let none = Modifiers::default();
|
|
|
|
|
let shift = Modifiers::SHIFT;
|
|
|
|
|
let ctrl = Modifiers::CTRL;
|
|
|
|
|
let ctrl_shift = Modifiers {
|
|
|
|
|
ctrl: true,
|
|
|
|
|
shift: true,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
assert_eq!(KeyEvent::new(none, 'a'), Key::new(none, 'a'));
|
|
|
|
|
assert_ne!(KeyEvent::new(none, 'a'), Key::new(none, 'A'));
|
|
|
|
|
assert_eq!(KeyEvent::new(shift, 'a'), Key::new(shift, 'a'));
|
|
|
|
|
assert_ne!(KeyEvent::new(shift, 'a'), Key::new(none, 'A'));
|
|
|
|
|
assert_ne!(KeyEvent::new(shift, 'ä'), Key::new(none, 'Ä'));
|
|
|
|
|
// For historical reasons we canonicalize notation for ASCII keys like "shift-a" to "A",
|
|
|
|
|
// but not "shift-a" events - those should send a shifted key.
|
|
|
|
|
assert_eq!(KeyEvent::new(none, 'A'), Key::new(shift, 'a'));
|
|
|
|
|
assert_ne!(KeyEvent::new(none, 'A'), Key::new(shift, 'A'));
|
|
|
|
|
assert_eq!(KeyEvent::new(none, 'Ä'), Key::new(none, 'Ä'));
|
|
|
|
|
assert_ne!(KeyEvent::new(none, 'Ä'), Key::new(shift, 'ä'));
|
|
|
|
|
|
|
|
|
|
// FYI: for codepoints that are not letters with uppercase/lowercase versions, we use
|
|
|
|
|
// the shifted key in the canonical notation, because the unshifted one may depend on the
|
|
|
|
|
// keyboard layout.
|
|
|
|
|
let ctrl_shift_equals = KeyEvent::with_shifted_codepoint(ctrl_shift, '=', Some('+'));
|
|
|
|
|
assert_eq!(ctrl_shift_equals, Key::new(ctrl_shift, '='));
|
|
|
|
|
assert_eq!(ctrl_shift_equals, Key::new(ctrl, '+')); // canonical notation
|
|
|
|
|
assert_ne!(ctrl_shift_equals, Key::new(ctrl_shift, '+'));
|
|
|
|
|
assert_ne!(ctrl_shift_equals, Key::new(ctrl, '='));
|
|
|
|
|
|
|
|
|
|
// A event like capslock-shift-ä may or may not include a shifted codepoint.
|
|
|
|
|
//
|
|
|
|
|
// Without a shifted codepoint, we cannot easily match ctrl-Ä.
|
|
|
|
|
let caps_ctrl_shift_ä = KeyEvent::new(ctrl_shift, 'ä');
|
|
|
|
|
assert_eq!(caps_ctrl_shift_ä, Key::new(ctrl_shift, 'ä')); // canonical notation
|
|
|
|
|
assert_ne!(caps_ctrl_shift_ä, Key::new(ctrl, 'ä'));
|
|
|
|
|
assert_ne!(caps_ctrl_shift_ä, Key::new(ctrl, 'Ä')); // can't match without shifted key
|
|
|
|
|
assert_ne!(caps_ctrl_shift_ä, Key::new(ctrl_shift, 'Ä'));
|
|
|
|
|
// With a shifted codepoint, we can match the alternative notation too.
|
|
|
|
|
let caps_ctrl_shift_ä = KeyEvent::with_shifted_codepoint(ctrl_shift, 'ä', Some('Ä'));
|
|
|
|
|
assert_eq!(caps_ctrl_shift_ä, Key::new(ctrl_shift, 'ä')); // canonical notation
|
|
|
|
|
assert_ne!(caps_ctrl_shift_ä, Key::new(ctrl, 'ä'));
|
|
|
|
|
assert_eq!(caps_ctrl_shift_ä, Key::new(ctrl, 'Ä')); // matched via shifted key
|
|
|
|
|
assert_ne!(caps_ctrl_shift_ä, Key::new(ctrl_shift, 'Ä'));
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
/// Represents an event on the character input stream.
|
2024-03-02 07:58:01 +01:00
|
|
|
#[derive(Debug, Clone)]
|
2023-11-25 14:37:42 -08:00
|
|
|
pub enum CharEventType {
|
|
|
|
|
/// A character was entered.
|
2025-03-30 08:33:24 +02:00
|
|
|
Char(KeyInputEvent),
|
2023-11-25 14:37:42 -08:00
|
|
|
|
|
|
|
|
/// A readline event.
|
|
|
|
|
Readline(ReadlineCmd),
|
|
|
|
|
|
2024-03-02 07:58:01 +01:00
|
|
|
/// A shell command.
|
|
|
|
|
Command(WString),
|
|
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
/// end-of-file was reached.
|
|
|
|
|
Eof,
|
|
|
|
|
|
|
|
|
|
/// An event was handled internally, or an interrupt was received. Check to see if the reader
|
|
|
|
|
/// loop should exit.
|
|
|
|
|
CheckExit,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
2024-03-30 13:01:57 +01:00
|
|
|
pub struct ReadlineCmdEvent {
|
|
|
|
|
pub cmd: ReadlineCmd,
|
|
|
|
|
/// The sequence of characters in the input mapping which generated this event.
|
|
|
|
|
/// Note that the generic self-insert case does not have any characters, so this would be empty.
|
|
|
|
|
/// This is also empty for invalid Unicode code points, which produce multiple characters.
|
|
|
|
|
pub seq: WString,
|
|
|
|
|
}
|
2023-11-25 14:37:42 -08:00
|
|
|
|
2024-03-30 13:01:57 +01:00
|
|
|
#[derive(Debug, Clone)]
|
2025-03-30 08:33:24 +02:00
|
|
|
pub struct KeyInputEvent {
|
2024-03-30 13:01:57 +01:00
|
|
|
// The key.
|
2025-03-30 08:33:24 +02:00
|
|
|
pub key: KeyEvent,
|
2023-11-25 14:37:42 -08:00
|
|
|
// The style to use when inserting characters into the command line.
|
|
|
|
|
pub input_style: CharInputStyle,
|
|
|
|
|
/// The sequence of characters in the input mapping which generated this event.
|
|
|
|
|
/// Note that the generic self-insert case does not have any characters, so this would be empty.
|
2024-03-30 13:01:57 +01:00
|
|
|
/// This is also empty for invalid Unicode code points, which produce multiple characters.
|
2023-11-25 14:37:42 -08:00
|
|
|
pub seq: WString,
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-29 19:31:15 +01:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum ImplicitEvent {
|
|
|
|
|
/// end-of-file was reached.
|
|
|
|
|
Eof,
|
|
|
|
|
/// An event was handled internally, or an interrupt was received. Check to see if the reader
|
|
|
|
|
/// loop should exit.
|
|
|
|
|
CheckExit,
|
|
|
|
|
/// Our terminal window gained focus.
|
|
|
|
|
FocusIn,
|
|
|
|
|
/// Our terminal window lost focus.
|
|
|
|
|
FocusOut,
|
|
|
|
|
/// Request to disable mouse tracking.
|
|
|
|
|
DisableMouseTracking,
|
2025-04-26 19:24:23 +02:00
|
|
|
/// Mouse left click.
|
|
|
|
|
MouseLeft(ViewportPosition),
|
2025-04-25 23:08:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum QueryResponseEvent {
|
2025-01-25 16:07:15 +01:00
|
|
|
PrimaryDeviceAttribute,
|
2025-04-26 19:08:48 +02:00
|
|
|
CursorPositionReport(ViewportPosition),
|
2024-12-29 19:31:15 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-30 13:01:57 +01:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum CharEvent {
|
|
|
|
|
/// A character was entered.
|
2025-03-30 08:33:24 +02:00
|
|
|
Key(KeyInputEvent),
|
2024-03-30 13:01:57 +01:00
|
|
|
|
|
|
|
|
/// A readline event.
|
|
|
|
|
Readline(ReadlineCmdEvent),
|
|
|
|
|
|
|
|
|
|
/// A shell command.
|
|
|
|
|
Command(WString),
|
|
|
|
|
|
2024-12-29 19:31:15 +01:00
|
|
|
/// Any event that has no user-visible representation.
|
|
|
|
|
Implicit(ImplicitEvent),
|
2025-04-25 23:08:29 +02:00
|
|
|
|
|
|
|
|
QueryResponse(QueryResponseEvent),
|
2024-03-30 13:01:57 +01:00
|
|
|
}
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
impl FloggableDebug for CharEvent {}
|
2024-03-30 13:01:57 +01:00
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
impl CharEvent {
|
|
|
|
|
pub fn is_char(&self) -> bool {
|
2024-03-30 16:10:12 +01:00
|
|
|
matches!(self, CharEvent::Key(_))
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_readline(&self) -> bool {
|
2024-03-30 13:01:57 +01:00
|
|
|
matches!(self, CharEvent::Readline(_))
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2024-03-02 07:58:01 +01:00
|
|
|
pub fn is_readline_or_command(&self) -> bool {
|
2024-03-30 13:01:57 +01:00
|
|
|
matches!(self, CharEvent::Readline(_) | CharEvent::Command(_))
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2024-03-30 16:10:12 +01:00
|
|
|
pub fn get_char(&self) -> char {
|
|
|
|
|
let CharEvent::Key(kevt) = self else {
|
|
|
|
|
panic!("Not a char type");
|
|
|
|
|
};
|
|
|
|
|
kevt.key.codepoint
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-30 08:33:24 +02:00
|
|
|
pub fn get_key(&self) -> Option<&KeyInputEvent> {
|
2024-03-30 16:10:12 +01:00
|
|
|
match self {
|
|
|
|
|
CharEvent::Key(kevt) => Some(kevt),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
pub fn get_readline(&self) -> ReadlineCmd {
|
2024-03-30 13:01:57 +01:00
|
|
|
let CharEvent::Readline(c) = self else {
|
2023-11-25 14:37:42 -08:00
|
|
|
panic!("Not a readline type");
|
|
|
|
|
};
|
2024-03-30 13:01:57 +01:00
|
|
|
c.cmd
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2024-03-02 07:58:01 +01:00
|
|
|
pub fn get_command(&self) -> Option<&wstr> {
|
2024-03-30 13:01:57 +01:00
|
|
|
match self {
|
|
|
|
|
CharEvent::Command(c) => Some(c),
|
2024-03-02 07:58:01 +01:00
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
pub fn from_char(c: char) -> CharEvent {
|
2025-03-30 08:33:24 +02:00
|
|
|
Self::from_key(KeyEvent::from_raw(c))
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2025-03-30 08:33:24 +02:00
|
|
|
pub fn from_key(key: KeyEvent) -> CharEvent {
|
2024-03-30 16:10:12 +01:00
|
|
|
Self::from_key_seq(key, WString::new())
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-30 08:33:24 +02:00
|
|
|
pub fn from_key_seq(key: KeyEvent, seq: WString) -> CharEvent {
|
|
|
|
|
CharEvent::Key(KeyInputEvent {
|
2024-03-30 16:10:12 +01:00
|
|
|
key,
|
|
|
|
|
input_style: CharInputStyle::Normal,
|
|
|
|
|
seq,
|
|
|
|
|
})
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2024-03-30 13:01:57 +01:00
|
|
|
pub fn from_readline(cmd: ReadlineCmd) -> CharEvent {
|
|
|
|
|
Self::from_readline_seq(cmd, WString::new())
|
2024-03-02 07:58:01 +01:00
|
|
|
}
|
|
|
|
|
|
2024-03-30 13:01:57 +01:00
|
|
|
pub fn from_readline_seq(cmd: ReadlineCmd, seq: WString) -> CharEvent {
|
|
|
|
|
CharEvent::Readline(ReadlineCmdEvent { cmd, seq })
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2024-03-30 13:01:57 +01:00
|
|
|
pub fn from_check_exit() -> CharEvent {
|
2024-12-29 19:31:15 +01:00
|
|
|
CharEvent::Implicit(ImplicitEvent::CheckExit)
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Time in milliseconds to wait for another byte to be available for reading
|
|
|
|
|
/// after \x1B is read before assuming that escape key was pressed, and not an
|
|
|
|
|
/// escape sequence.
|
2024-04-30 10:28:47 +02:00
|
|
|
const WAIT_ON_ESCAPE_DEFAULT: usize = 30;
|
|
|
|
|
static WAIT_ON_ESCAPE_MS: AtomicUsize = AtomicUsize::new(WAIT_ON_ESCAPE_DEFAULT);
|
2023-11-25 14:37:42 -08:00
|
|
|
|
|
|
|
|
const WAIT_ON_SEQUENCE_KEY_INFINITE: usize = usize::MAX;
|
|
|
|
|
static WAIT_ON_SEQUENCE_KEY_MS: AtomicUsize = AtomicUsize::new(WAIT_ON_SEQUENCE_KEY_INFINITE);
|
|
|
|
|
|
|
|
|
|
/// Internal function used by readch to read one byte.
|
|
|
|
|
/// This calls select() on three fds: input (e.g. stdin), the ioport notifier fd (for main thread
|
|
|
|
|
/// requests), and the uvar notifier. This returns either the byte which was read, or one of the
|
|
|
|
|
/// special values below.
|
|
|
|
|
enum ReadbResult {
|
|
|
|
|
// A byte was successfully read.
|
|
|
|
|
Byte(u8),
|
|
|
|
|
|
|
|
|
|
// The in fd has been closed.
|
|
|
|
|
Eof,
|
|
|
|
|
|
|
|
|
|
// select() was interrupted by a signal.
|
|
|
|
|
Interrupted,
|
|
|
|
|
|
|
|
|
|
// Our uvar notifier reported a change (either through poll() or its fd).
|
|
|
|
|
UvarNotified,
|
|
|
|
|
|
|
|
|
|
// Our ioport reported a change, so service main thread requests.
|
|
|
|
|
IOPortNotified,
|
2024-03-30 16:10:12 +01:00
|
|
|
|
|
|
|
|
NothingToRead,
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2024-03-30 16:10:12 +01:00
|
|
|
fn readb(in_fd: RawFd, blocking: bool) -> ReadbResult {
|
2023-11-25 14:37:42 -08:00
|
|
|
assert!(in_fd >= 0, "Invalid in fd");
|
|
|
|
|
let mut fdset = FdReadableSet::new();
|
|
|
|
|
loop {
|
|
|
|
|
fdset.clear();
|
|
|
|
|
fdset.add(in_fd);
|
|
|
|
|
|
|
|
|
|
// Add the completion ioport.
|
|
|
|
|
let ioport_fd = iothread_port();
|
|
|
|
|
fdset.add(ioport_fd);
|
|
|
|
|
|
|
|
|
|
// Get the uvar notifier fd (possibly none).
|
|
|
|
|
let notifier = default_notifier();
|
|
|
|
|
let notifier_fd = notifier.notification_fd();
|
|
|
|
|
if let Some(notifier_fd) = notifier.notification_fd() {
|
|
|
|
|
fdset.add(notifier_fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Here's where we call select().
|
2025-02-09 15:52:29 +01:00
|
|
|
let select_res = fdset.check_readable(if blocking {
|
2025-01-04 18:22:38 -06:00
|
|
|
Timeout::Forever
|
2024-03-30 16:10:12 +01:00
|
|
|
} else {
|
2025-03-04 09:23:02 +01:00
|
|
|
Timeout::Duration(Duration::from_millis(1))
|
2024-03-30 16:10:12 +01:00
|
|
|
});
|
2023-11-25 14:37:42 -08:00
|
|
|
if select_res < 0 {
|
|
|
|
|
let err = errno::errno().0;
|
|
|
|
|
if err == libc::EINTR || err == libc::EAGAIN {
|
|
|
|
|
// A signal.
|
|
|
|
|
return ReadbResult::Interrupted;
|
|
|
|
|
} else {
|
|
|
|
|
// Some fd was invalid, so probably the tty has been closed.
|
|
|
|
|
return ReadbResult::Eof;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-30 16:10:12 +01:00
|
|
|
if blocking {
|
|
|
|
|
// select() did not return an error, so we may have a readable fd.
|
|
|
|
|
// The priority order is: uvars, stdin, ioport.
|
|
|
|
|
// Check to see if we want a universal variable barrier.
|
|
|
|
|
if let Some(notifier_fd) = notifier_fd {
|
|
|
|
|
if fdset.test(notifier_fd) && notifier.notification_fd_became_readable(notifier_fd)
|
|
|
|
|
{
|
|
|
|
|
return ReadbResult::UvarNotified;
|
|
|
|
|
}
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check stdin.
|
|
|
|
|
if fdset.test(in_fd) {
|
|
|
|
|
let mut arr: [u8; 1] = [0];
|
2024-01-20 18:45:36 +01:00
|
|
|
if read_blocked(in_fd, &mut arr) != Ok(1) {
|
2023-11-25 14:37:42 -08:00
|
|
|
// The terminal has been closed.
|
|
|
|
|
return ReadbResult::Eof;
|
|
|
|
|
}
|
2025-01-04 12:36:16 +01:00
|
|
|
let c = arr[0];
|
2025-04-01 22:59:36 +02:00
|
|
|
FLOG!(reader, "Read byte", char_to_symbol(char::from(c), true));
|
2023-11-25 14:37:42 -08:00
|
|
|
// The common path is to return a u8.
|
2025-01-04 12:36:16 +01:00
|
|
|
return ReadbResult::Byte(c);
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
if !blocking {
|
|
|
|
|
return ReadbResult::NothingToRead;
|
|
|
|
|
}
|
2023-11-25 14:37:42 -08:00
|
|
|
|
|
|
|
|
// Check for iothread completions only if there is no data to be read from the stdin.
|
|
|
|
|
// This gives priority to the foreground.
|
|
|
|
|
if fdset.test(ioport_fd) {
|
|
|
|
|
return ReadbResult::IOPortNotified;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update the wait_on_escape_ms value in response to the fish_escape_delay_ms user variable being
|
|
|
|
|
// set.
|
2023-10-08 23:22:27 +02:00
|
|
|
pub fn update_wait_on_escape_ms(vars: &EnvStack) {
|
|
|
|
|
let fish_escape_delay_ms = vars.get_unless_empty(L!("fish_escape_delay_ms"));
|
2023-11-25 14:37:42 -08:00
|
|
|
let Some(fish_escape_delay_ms) = fish_escape_delay_ms else {
|
2024-04-30 10:28:47 +02:00
|
|
|
WAIT_ON_ESCAPE_MS.store(WAIT_ON_ESCAPE_DEFAULT, Ordering::Relaxed);
|
2023-11-25 14:37:42 -08:00
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
let fish_escape_delay_ms = fish_escape_delay_ms.as_string();
|
|
|
|
|
match fish_wcstol(&fish_escape_delay_ms) {
|
|
|
|
|
Ok(val) if (10..5000).contains(&val) => {
|
|
|
|
|
WAIT_ON_ESCAPE_MS.store(val.try_into().unwrap(), Ordering::Relaxed);
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
2025-04-22 19:08:21 +02:00
|
|
|
eprintf!(
|
2023-11-25 14:37:42 -08:00
|
|
|
concat!(
|
2025-04-22 19:08:21 +02:00
|
|
|
"ignoring fish_escape_delay_ms: value '%ls' ",
|
|
|
|
|
"is not an integer or is < 10 or >= 5000 ms\n"
|
2023-11-25 14:37:42 -08:00
|
|
|
),
|
|
|
|
|
fish_escape_delay_ms
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-08 23:22:27 +02:00
|
|
|
}
|
|
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
// Update the wait_on_sequence_key_ms value in response to the fish_sequence_key_delay_ms user
|
|
|
|
|
// variable being set.
|
2023-10-08 23:22:27 +02:00
|
|
|
pub fn update_wait_on_sequence_key_ms(vars: &EnvStack) {
|
2023-11-25 14:37:42 -08:00
|
|
|
let sequence_key_time_ms = vars.get_unless_empty(L!("fish_sequence_key_delay_ms"));
|
|
|
|
|
let Some(sequence_key_time_ms) = sequence_key_time_ms else {
|
|
|
|
|
WAIT_ON_SEQUENCE_KEY_MS.store(WAIT_ON_SEQUENCE_KEY_INFINITE, Ordering::Relaxed);
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
let sequence_key_time_ms = sequence_key_time_ms.as_string();
|
|
|
|
|
match fish_wcstol(&sequence_key_time_ms) {
|
|
|
|
|
Ok(val) if (10..5000).contains(&val) => {
|
|
|
|
|
WAIT_ON_SEQUENCE_KEY_MS.store(val.try_into().unwrap(), Ordering::Relaxed);
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
2025-04-22 19:08:21 +02:00
|
|
|
eprintf!(
|
2023-11-25 14:37:42 -08:00
|
|
|
concat!(
|
2025-04-22 19:08:21 +02:00
|
|
|
"ignoring fish_sequence_key_delay_ms: value '%ls' ",
|
|
|
|
|
"is not an integer or is < 10 or >= 5000 ms\n"
|
2023-11-25 14:37:42 -08:00
|
|
|
),
|
|
|
|
|
sequence_key_time_ms
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-08 11:25:48 +02:00
|
|
|
static TERMINAL_PROTOCOLS: AtomicBool = AtomicBool::new(false);
|
2025-01-26 12:08:48 +01:00
|
|
|
static BRACKETED_PASTE: AtomicBool = AtomicBool::new(false);
|
2024-03-30 16:10:12 +01:00
|
|
|
|
2025-01-19 18:52:10 +01:00
|
|
|
static IS_TMUX: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
2025-01-26 19:40:16 +01:00
|
|
|
|
|
|
|
|
pub(crate) static IN_MIDNIGHT_COMMANDER: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
|
|
|
|
pub(crate) static IN_DVTM: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
2025-03-01 13:06:01 +01:00
|
|
|
static ITERM_NO_KITTY_KEYBOARD: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
2024-10-12 10:49:03 +02:00
|
|
|
|
|
|
|
|
pub fn terminal_protocol_hacks() {
|
|
|
|
|
use std::env::var_os;
|
2025-01-26 19:40:16 +01:00
|
|
|
IN_MIDNIGHT_COMMANDER.store(var_os("MC_TMPDIR").is_some());
|
|
|
|
|
IN_DVTM
|
|
|
|
|
.store(var_os("TERM").is_some_and(|term| term.as_os_str().as_bytes() == b"dvtm-256color"));
|
2024-10-12 10:49:03 +02:00
|
|
|
IS_TMUX.store(var_os("TMUX").is_some());
|
2025-03-01 13:06:01 +01:00
|
|
|
ITERM_NO_KITTY_KEYBOARD.store(
|
2024-10-12 10:49:03 +02:00
|
|
|
var_os("LC_TERMINAL").is_some_and(|term| term.as_os_str().as_bytes() == b"iTerm2")
|
|
|
|
|
&& var_os("LC_TERMINAL_VERSION").is_some_and(|version| {
|
|
|
|
|
let Some(version) = parse_version(&str2wcstring(version.as_os_str().as_bytes()))
|
|
|
|
|
else {
|
|
|
|
|
return false;
|
|
|
|
|
};
|
2025-03-01 13:06:01 +01:00
|
|
|
version < (3, 5, 12)
|
2024-10-12 10:49:03 +02:00
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_version(version: &wstr) -> Option<(i64, i64, i64)> {
|
|
|
|
|
let mut numbers = version.split('.');
|
|
|
|
|
let major = fish_wcstol(numbers.next()?).ok()?;
|
|
|
|
|
let minor = fish_wcstol(numbers.next()?).ok()?;
|
|
|
|
|
let patch = numbers.next()?;
|
|
|
|
|
let patch = &patch[..patch
|
|
|
|
|
.chars()
|
|
|
|
|
.position(|c| !c.is_ascii_digit())
|
|
|
|
|
.unwrap_or(patch.len())];
|
|
|
|
|
let patch = fish_wcstol(patch).ok()?;
|
|
|
|
|
Some((major, minor, patch))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_parse_version() {
|
|
|
|
|
assert_eq!(parse_version(L!("3.5.2")), Some((3, 5, 2)));
|
|
|
|
|
assert_eq!(parse_version(L!("3.5.3beta")), Some((3, 5, 3)));
|
|
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
|
2024-06-25 19:54:38 +02:00
|
|
|
pub fn terminal_protocols_enable_ifn() {
|
2025-01-26 12:08:48 +01:00
|
|
|
let did_write = RelaxedAtomicBool::new(false);
|
|
|
|
|
let _save_screen_state = ScopeGuard::new((), |()| {
|
|
|
|
|
if did_write.load() {
|
|
|
|
|
reader_current_data().map(|data| data.save_screen_state());
|
|
|
|
|
}
|
|
|
|
|
});
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
let mut out = Outputter::stdoutput().borrow_mut();
|
2025-01-26 12:08:48 +01:00
|
|
|
if !BRACKETED_PASTE.load(Ordering::Relaxed) {
|
|
|
|
|
BRACKETED_PASTE.store(true, Ordering::Release);
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
out.write_command(DecsetBracketedPaste);
|
2025-01-26 12:08:48 +01:00
|
|
|
if IS_TMUX.load() {
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
out.write_command(DecsetFocusReporting);
|
2025-01-26 12:08:48 +01:00
|
|
|
}
|
|
|
|
|
did_write.store(true);
|
|
|
|
|
}
|
|
|
|
|
let kitty_keyboard_supported = KITTY_KEYBOARD_SUPPORTED.load(Ordering::Relaxed);
|
|
|
|
|
if kitty_keyboard_supported == Capability::Unknown as _ {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-10-08 11:25:48 +02:00
|
|
|
if TERMINAL_PROTOCOLS.load(Ordering::Relaxed) {
|
2024-05-16 10:52:19 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2024-10-08 11:25:48 +02:00
|
|
|
TERMINAL_PROTOCOLS.store(true, Ordering::Release);
|
2025-01-26 12:08:48 +01:00
|
|
|
FLOG!(term_protocols, "Enabling extended keys");
|
2025-03-01 13:06:01 +01:00
|
|
|
if kitty_keyboard_supported == Capability::NotSupported as _ || ITERM_NO_KITTY_KEYBOARD.load() {
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
out.write_command(ModifyOtherKeysEnable); // XTerm's modifyOtherKeys
|
|
|
|
|
out.write_command(ApplicationKeypadModeEnable); // set application keypad mode, so the keypad keys send unique codes
|
2024-10-08 11:25:48 +02:00
|
|
|
} else {
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
out.write_command(KittyKeyboardProgressiveEnhancementsEnable);
|
2024-05-16 10:52:19 +02:00
|
|
|
}
|
2025-01-26 12:08:48 +01:00
|
|
|
did_write.store(true);
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
|
2024-10-08 11:25:48 +02:00
|
|
|
pub(crate) fn terminal_protocols_disable_ifn() {
|
2025-01-26 12:08:48 +01:00
|
|
|
let did_write = RelaxedAtomicBool::new(false);
|
|
|
|
|
let _save_screen_state = is_main_thread().then(|| {
|
|
|
|
|
ScopeGuard::new((), |()| {
|
|
|
|
|
if did_write.load() {
|
|
|
|
|
reader_current_data().map(|data| data.save_screen_state());
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
});
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
let mut out = Outputter::stdoutput().borrow_mut();
|
2025-01-26 12:08:48 +01:00
|
|
|
if BRACKETED_PASTE.load(Ordering::Acquire) {
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
out.write_command(DecrstBracketedPaste);
|
2025-01-26 12:08:48 +01:00
|
|
|
if IS_TMUX.load() {
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
out.write_command(DecrstFocusReporting);
|
2025-01-26 12:08:48 +01:00
|
|
|
}
|
2025-02-15 07:57:06 +01:00
|
|
|
BRACKETED_PASTE.store(false, Ordering::Release);
|
2025-01-26 12:08:48 +01:00
|
|
|
did_write.store(true);
|
|
|
|
|
}
|
2024-10-08 11:25:48 +02:00
|
|
|
if !TERMINAL_PROTOCOLS.load(Ordering::Acquire) {
|
|
|
|
|
return;
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
2025-01-26 12:08:48 +01:00
|
|
|
FLOG_SAFE!(term_protocols, "Disabling extended keys");
|
|
|
|
|
let kitty_keyboard_supported = KITTY_KEYBOARD_SUPPORTED.load(Ordering::Acquire);
|
|
|
|
|
assert_ne!(kitty_keyboard_supported, Capability::Unknown as _);
|
2025-03-01 13:06:01 +01:00
|
|
|
if kitty_keyboard_supported == Capability::NotSupported as _ || ITERM_NO_KITTY_KEYBOARD.load() {
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
out.write_command(ModifyOtherKeysDisable);
|
|
|
|
|
out.write_command(ApplicationKeypadModeDisable);
|
2024-10-08 11:25:48 +02:00
|
|
|
} else {
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
out.write_command(KittyKeyboardProgressiveEnhancementsDisable);
|
2024-10-12 09:00:24 +02:00
|
|
|
}
|
2024-10-08 11:25:48 +02:00
|
|
|
TERMINAL_PROTOCOLS.store(false, Ordering::Release);
|
2025-01-26 12:08:48 +01:00
|
|
|
did_write.store(true);
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
fn parse_mask(mask: u32) -> (Modifiers, bool) {
|
|
|
|
|
let modifiers = Modifiers {
|
2024-03-30 16:10:12 +01:00
|
|
|
ctrl: (mask & 4) != 0,
|
|
|
|
|
alt: (mask & 2) != 0,
|
|
|
|
|
shift: (mask & 1) != 0,
|
2025-03-30 08:33:24 +02:00
|
|
|
sup: (mask & 8) != 0,
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
};
|
|
|
|
|
let caps_lock = (mask & 64) != 0;
|
|
|
|
|
(modifiers, caps_lock)
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
|
2024-05-26 16:21:11 -07:00
|
|
|
// A data type used by the input machinery.
|
|
|
|
|
pub struct InputData {
|
|
|
|
|
// The file descriptor from which we read input, often stdin.
|
|
|
|
|
pub in_fd: RawFd,
|
|
|
|
|
|
|
|
|
|
// Queue of unread characters.
|
|
|
|
|
pub queue: VecDeque<CharEvent>,
|
|
|
|
|
|
|
|
|
|
// The current paste buffer, if any.
|
|
|
|
|
pub paste_buffer: Option<Vec<u8>>,
|
|
|
|
|
|
|
|
|
|
// The arguments to the most recently invoked input function.
|
|
|
|
|
pub input_function_args: Vec<char>,
|
|
|
|
|
|
|
|
|
|
// The return status of the most recently invoked input function.
|
|
|
|
|
pub function_status: bool,
|
|
|
|
|
|
|
|
|
|
// Transient storage to avoid repeated allocations.
|
|
|
|
|
pub event_storage: Vec<CharEvent>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl InputData {
|
|
|
|
|
/// Construct from the fd from which to read.
|
|
|
|
|
pub fn new(in_fd: RawFd) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
in_fd,
|
|
|
|
|
queue: VecDeque::new(),
|
|
|
|
|
paste_buffer: None,
|
|
|
|
|
input_function_args: Vec::new(),
|
|
|
|
|
function_status: false,
|
|
|
|
|
event_storage: Vec::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Enqueue a char event to the queue of unread characters that input_readch will return before
|
|
|
|
|
/// actually reading from fd 0.
|
|
|
|
|
pub fn queue_char(&mut self, ch: CharEvent) {
|
|
|
|
|
self.queue.push_back(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets the return status of the most recently executed input function.
|
|
|
|
|
pub fn function_set_status(&mut self, status: bool) {
|
|
|
|
|
self.function_status = status;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-26 19:08:48 +02:00
|
|
|
#[derive(Clone, Eq, PartialEq)]
|
2025-04-25 22:24:06 +02:00
|
|
|
pub enum CursorPositionQuery {
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
MouseLeft(ViewportPosition),
|
2024-12-21 19:41:41 +01:00
|
|
|
ScrollbackPush,
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-25 16:07:15 +01:00
|
|
|
#[derive(Eq, PartialEq)]
|
2025-04-25 22:24:06 +02:00
|
|
|
pub enum TerminalQuery {
|
2025-04-28 21:10:43 +02:00
|
|
|
PrimaryDeviceAttribute,
|
2025-04-25 22:24:06 +02:00
|
|
|
CursorPositionReport(CursorPositionQuery),
|
2025-01-04 01:21:52 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
/// A trait which knows how to produce a stream of input events.
|
|
|
|
|
/// Note this is conceptually a "base class" with override points.
|
|
|
|
|
pub trait InputEventQueuer {
|
2024-05-06 14:58:10 -05:00
|
|
|
/// Return the next event in the queue, or none if the queue is empty.
|
2023-11-25 14:37:42 -08:00
|
|
|
fn try_pop(&mut self) -> Option<CharEvent> {
|
2025-04-25 22:24:06 +02:00
|
|
|
if self.is_blocked_querying() {
|
2025-04-26 18:49:33 +02:00
|
|
|
use ImplicitEvent::*;
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
match self.get_input_data().queue.front()? {
|
2025-04-26 18:49:33 +02:00
|
|
|
CharEvent::QueryResponse(_) | CharEvent::Implicit(CheckExit | Eof) => {}
|
|
|
|
|
CharEvent::Key(_)
|
|
|
|
|
| CharEvent::Readline(_)
|
|
|
|
|
| CharEvent::Command(_)
|
|
|
|
|
| CharEvent::Implicit(_) => {
|
2025-01-25 16:07:15 +01:00
|
|
|
return None; // No code execution while blocked.
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-26 16:21:11 -07:00
|
|
|
self.get_input_data_mut().queue.pop_front()
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2024-11-20 14:48:20 -06:00
|
|
|
/// An "infallible" version of [`try_readch`](Self::try_readch) to be used when the input pipe
|
|
|
|
|
/// fd is expected to outlive the input reader. Will panic upon EOF.
|
|
|
|
|
#[inline(always)]
|
2023-11-25 14:37:42 -08:00
|
|
|
fn readch(&mut self) -> CharEvent {
|
2024-11-20 14:48:20 -06:00
|
|
|
match self.try_readch(/*blocking*/ true) {
|
|
|
|
|
Some(c) => c,
|
|
|
|
|
None => unreachable!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-18 10:56:56 +00:00
|
|
|
/// Function used by [`readch`](Self::readch) to read bytes from stdin until enough bytes have been read to
|
2024-11-20 14:48:20 -06:00
|
|
|
/// convert them to a wchar_t. Conversion is done using `mbrtowc`. If a character has previously
|
|
|
|
|
/// been read and then 'unread', that character is returned.
|
|
|
|
|
///
|
|
|
|
|
/// This is guaranteed to keep returning `Some(CharEvent)` so long as the input stream remains
|
|
|
|
|
/// open; `None` is only returned upon EOF as the main loop within blocks until input becomes
|
|
|
|
|
/// available.
|
|
|
|
|
///
|
|
|
|
|
/// This method is used directly by the fuzzing harness to avoid a panic on bounded inputs.
|
|
|
|
|
fn try_readch(&mut self, blocking: bool) -> Option<CharEvent> {
|
2023-11-25 14:37:42 -08:00
|
|
|
loop {
|
|
|
|
|
// Do we have something enqueued already?
|
|
|
|
|
// Note this may be initially true, or it may become true through calls to
|
|
|
|
|
// iothread_service_main() or env_universal_barrier() below.
|
|
|
|
|
if let Some(mevt) = self.try_pop() {
|
2024-11-20 14:48:20 -06:00
|
|
|
return Some(mevt);
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We are going to block; but first allow any override to inject events.
|
|
|
|
|
self.prepare_to_select();
|
|
|
|
|
if let Some(mevt) = self.try_pop() {
|
2024-11-20 14:48:20 -06:00
|
|
|
return Some(mevt);
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2024-11-20 14:48:20 -06:00
|
|
|
let rr = readb(self.get_in_fd(), blocking);
|
2023-11-25 14:37:42 -08:00
|
|
|
match rr {
|
|
|
|
|
ReadbResult::Eof => {
|
2024-12-29 19:31:15 +01:00
|
|
|
return Some(CharEvent::Implicit(ImplicitEvent::Eof));
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReadbResult::Interrupted => {
|
|
|
|
|
self.select_interrupted();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReadbResult::UvarNotified => {
|
|
|
|
|
self.uvar_change_notified();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReadbResult::IOPortNotified => {
|
2024-05-27 12:04:08 -07:00
|
|
|
self.ioport_notified();
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReadbResult::Byte(read_byte) => {
|
2024-03-30 16:10:12 +01:00
|
|
|
let mut have_escape_prefix = false;
|
|
|
|
|
let mut buffer = vec![read_byte];
|
|
|
|
|
let key_with_escape = if read_byte == 0x1b {
|
|
|
|
|
self.parse_escape_sequence(&mut buffer, &mut have_escape_prefix)
|
|
|
|
|
} else {
|
2025-03-30 08:33:24 +02:00
|
|
|
canonicalize_control_char(read_byte).map(KeyEvent::from)
|
2024-03-30 16:10:12 +01:00
|
|
|
};
|
|
|
|
|
if self.paste_is_buffering() {
|
|
|
|
|
if read_byte != 0x1b {
|
|
|
|
|
self.paste_push_char(read_byte);
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
continue;
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
let mut seq = WString::new();
|
|
|
|
|
let mut key = key_with_escape;
|
2025-03-30 08:33:24 +02:00
|
|
|
if key.is_some_and(|key| key == Key::from_raw(key::Invalid)) {
|
2024-04-03 19:56:46 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2024-12-30 08:12:32 +01:00
|
|
|
assert!(key.map_or(true, |key| key.codepoint != key::Invalid));
|
2024-03-30 16:10:12 +01:00
|
|
|
let mut consumed = 0;
|
2024-04-23 00:04:05 +02:00
|
|
|
let mut state = zero_mbstate();
|
|
|
|
|
let mut i = 0;
|
|
|
|
|
let ok = loop {
|
|
|
|
|
if i == buffer.len() {
|
|
|
|
|
buffer.push(match readb(self.get_in_fd(), /*blocking=*/ true) {
|
|
|
|
|
ReadbResult::Byte(b) => b,
|
|
|
|
|
_ => 0,
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-04-15 00:28:47 +02:00
|
|
|
match decode_input_byte(
|
2024-03-30 16:10:12 +01:00
|
|
|
&mut seq,
|
2025-04-15 09:21:11 +02:00
|
|
|
InvalidPolicy::Error,
|
2025-04-15 00:28:47 +02:00
|
|
|
&mut state,
|
2025-04-14 23:51:34 +02:00
|
|
|
&buffer[..i + 1],
|
2024-03-30 16:10:12 +01:00
|
|
|
&mut consumed,
|
2024-04-23 00:04:05 +02:00
|
|
|
) {
|
2025-04-15 00:28:47 +02:00
|
|
|
DecodeState::Incomplete => (),
|
|
|
|
|
DecodeState::Complete => {
|
2025-04-14 23:51:34 +02:00
|
|
|
if have_escape_prefix && i != 0 {
|
|
|
|
|
have_escape_prefix = false;
|
|
|
|
|
let c = seq.as_char_slice().last().unwrap();
|
|
|
|
|
key = Some(KeyEvent::from(alt(*c)));
|
|
|
|
|
}
|
|
|
|
|
if i + 1 == buffer.len() {
|
2024-04-23 00:04:05 +02:00
|
|
|
break true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-15 00:28:47 +02:00
|
|
|
DecodeState::Error => {
|
2025-04-14 23:51:34 +02:00
|
|
|
self.push_front(CharEvent::from_check_exit());
|
2024-04-23 00:04:05 +02:00
|
|
|
break false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i += 1;
|
|
|
|
|
};
|
|
|
|
|
if !ok {
|
|
|
|
|
continue;
|
2024-02-27 22:25:54 +01:00
|
|
|
}
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
let (key_evt, extra) = if let Some(key) = key {
|
|
|
|
|
(CharEvent::from_key_seq(key, seq), None)
|
2024-03-30 16:10:12 +01:00
|
|
|
} else {
|
|
|
|
|
let Some(c) = seq.chars().next() else {
|
|
|
|
|
continue;
|
|
|
|
|
};
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
(
|
2025-03-30 08:33:24 +02:00
|
|
|
CharEvent::from_key_seq(KeyEvent::from_raw(c), seq.clone()),
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
Some(seq.chars().skip(1).map(CharEvent::from_char)),
|
|
|
|
|
)
|
2024-03-30 16:10:12 +01:00
|
|
|
};
|
2025-04-25 22:24:06 +02:00
|
|
|
if self.is_blocked_querying() {
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
FLOG!(
|
|
|
|
|
reader,
|
2025-01-25 16:07:15 +01:00
|
|
|
"Still blocked on response from terminal, deferring key event",
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
key_evt
|
|
|
|
|
);
|
|
|
|
|
self.push_back(key_evt);
|
|
|
|
|
extra.map(|extra| {
|
|
|
|
|
for evt in extra {
|
|
|
|
|
self.push_back(evt);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
let vintr = shell_modes().c_cc[libc::VINTR];
|
2025-03-30 08:33:24 +02:00
|
|
|
if vintr != 0 && key.is_some_and(|key| key == Key::from_single_byte(vintr))
|
|
|
|
|
{
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
FLOG!(
|
|
|
|
|
reader,
|
2025-01-25 16:07:15 +01:00
|
|
|
"Received interrupt key, giving up waiting for response from terminal"
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
);
|
2025-04-25 22:24:06 +02:00
|
|
|
let ok = stop_query(self.blocking_query());
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
assert!(ok);
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
extra.map(|extra| self.insert_front(extra));
|
|
|
|
|
return Some(key_evt);
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
2024-11-20 14:48:20 -06:00
|
|
|
ReadbResult::NothingToRead => return None,
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn try_readb(&mut self, buffer: &mut Vec<u8>) -> Option<u8> {
|
|
|
|
|
let ReadbResult::Byte(next) = readb(self.get_in_fd(), /*blocking=*/ false) else {
|
|
|
|
|
return None;
|
|
|
|
|
};
|
|
|
|
|
buffer.push(next);
|
|
|
|
|
Some(next)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_escape_sequence(
|
|
|
|
|
&mut self,
|
|
|
|
|
buffer: &mut Vec<u8>,
|
|
|
|
|
have_escape_prefix: &mut bool,
|
2025-03-30 08:33:24 +02:00
|
|
|
) -> Option<KeyEvent> {
|
2025-01-05 11:48:32 +01:00
|
|
|
assert!(buffer.len() <= 2);
|
|
|
|
|
let recursive_invocation = buffer.len() == 2;
|
2024-03-30 16:10:12 +01:00
|
|
|
let Some(next) = self.try_readb(buffer) else {
|
|
|
|
|
if !self.paste_is_buffering() {
|
2025-03-30 08:33:24 +02:00
|
|
|
return Some(KeyEvent::from_raw(key::Escape));
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
return None;
|
|
|
|
|
};
|
2025-03-30 08:33:24 +02:00
|
|
|
let invalid = KeyEvent::from_raw(key::Invalid);
|
2025-01-05 11:48:32 +01:00
|
|
|
if recursive_invocation && next == b'\x1b' {
|
2024-08-11 11:23:49 +02:00
|
|
|
return Some(
|
|
|
|
|
match self.parse_escape_sequence(buffer, have_escape_prefix) {
|
|
|
|
|
Some(mut nested_sequence) => {
|
2025-03-30 08:33:24 +02:00
|
|
|
if nested_sequence == invalid.key {
|
|
|
|
|
return Some(KeyEvent::from_raw(key::Escape));
|
2024-12-30 08:12:32 +01:00
|
|
|
}
|
2024-08-11 11:23:49 +02:00
|
|
|
nested_sequence.modifiers.alt = true;
|
|
|
|
|
nested_sequence
|
|
|
|
|
}
|
2024-12-30 08:12:32 +01:00
|
|
|
_ => invalid,
|
2024-08-11 11:23:49 +02:00
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
if next == b'[' {
|
|
|
|
|
// potential CSI
|
2024-12-30 07:47:54 +01:00
|
|
|
return Some(self.parse_csi(buffer).unwrap_or(invalid));
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
if next == b'O' {
|
|
|
|
|
// potential SS3
|
2024-12-30 07:47:54 +01:00
|
|
|
return Some(self.parse_ss3(buffer).unwrap_or(invalid));
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
2025-01-05 11:48:32 +01:00
|
|
|
if !recursive_invocation && next == b'P' {
|
|
|
|
|
// potential DCS
|
|
|
|
|
return Some(self.parse_dcs(buffer).unwrap_or(invalid));
|
|
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
match canonicalize_control_char(next) {
|
|
|
|
|
Some(mut key) => {
|
|
|
|
|
key.modifiers.alt = true;
|
2025-03-30 08:33:24 +02:00
|
|
|
Some(KeyEvent::from(key))
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
*have_escape_prefix = true;
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-30 08:33:24 +02:00
|
|
|
fn parse_csi(&mut self, buffer: &mut Vec<u8>) -> Option<KeyEvent> {
|
2024-10-24 10:36:00 -05:00
|
|
|
// The maximum number of CSI parameters is defined by NPAR, nominally 16.
|
2024-10-24 10:28:04 -05:00
|
|
|
let mut params = [[0_u32; 4]; 16];
|
2024-12-30 07:47:54 +01:00
|
|
|
let Some(mut c) = self.try_readb(buffer) else {
|
2025-03-30 08:33:24 +02:00
|
|
|
return Some(KeyEvent::from(ctrl('[')));
|
2024-12-30 07:47:54 +01:00
|
|
|
};
|
|
|
|
|
let mut next_char = |zelf: &mut Self| zelf.try_readb(buffer).unwrap_or(0xff);
|
2024-03-30 16:10:12 +01:00
|
|
|
let private_mode;
|
|
|
|
|
if matches!(c, b'?' | b'<' | b'=' | b'>') {
|
|
|
|
|
// private mode
|
|
|
|
|
private_mode = Some(c);
|
|
|
|
|
c = next_char(self);
|
|
|
|
|
} else {
|
|
|
|
|
private_mode = None;
|
|
|
|
|
}
|
|
|
|
|
let mut count = 0;
|
|
|
|
|
let mut subcount = 0;
|
|
|
|
|
while count < 16 && c >= 0x30 && c <= 0x3f {
|
|
|
|
|
if c.is_ascii_digit() {
|
2024-11-20 14:53:39 -06:00
|
|
|
// Return None on invalid ascii numeric CSI parameter exceeding u32 bounds
|
2025-01-26 14:52:42 +01:00
|
|
|
match params[count][subcount]
|
2024-11-20 14:53:39 -06:00
|
|
|
.checked_mul(10)
|
2025-01-26 14:52:42 +01:00
|
|
|
.and_then(|result| result.checked_add(u32::from(c - b'0')))
|
|
|
|
|
{
|
|
|
|
|
Some(c) => params[count][subcount] = c,
|
|
|
|
|
None => return invalid_sequence(buffer),
|
|
|
|
|
};
|
2024-03-30 16:10:12 +01:00
|
|
|
} else if c == b':' && subcount < 3 {
|
|
|
|
|
subcount += 1;
|
|
|
|
|
} else if c == b';' {
|
|
|
|
|
count += 1;
|
|
|
|
|
subcount = 0;
|
|
|
|
|
} else {
|
2024-11-20 14:53:39 -06:00
|
|
|
// Unexpected character or unrecognized CSI
|
2024-03-30 16:10:12 +01:00
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
c = next_char(self);
|
|
|
|
|
}
|
|
|
|
|
if c != b'$' && !(0x40..=0x7e).contains(&c) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
let masked_key = |codepoint: char, shifted_codepoint: Option<char>| {
|
2024-03-30 16:10:12 +01:00
|
|
|
let mask = params[1][0].saturating_sub(1);
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
let (mut modifiers, caps_lock) = parse_mask(mask);
|
|
|
|
|
|
|
|
|
|
// An event like "capslock-shift-=" should have a shifted codepoint ("+") to enable
|
|
|
|
|
// fish to match "bind +".
|
|
|
|
|
//
|
|
|
|
|
// With letters that are affected by capslock, capslock and shift cancel each
|
|
|
|
|
// other out ("capslock-shift-ä"), unless there is another modifier to imply that
|
|
|
|
|
// capslock should be ignored.
|
|
|
|
|
//
|
|
|
|
|
// So if shift is the only modifier, we should consume it, but not if the event is
|
|
|
|
|
// something like "capslock-shift-delete" because delete is not affected by capslock.
|
|
|
|
|
//
|
|
|
|
|
// Normally, we could consume shift by translating to the shifted key.
|
|
|
|
|
// While capslock is on however, we don't get a shifted key, see
|
|
|
|
|
// https://github.com/kovidgoyal/kitty/issues/8493.
|
|
|
|
|
//
|
|
|
|
|
// Do it by trying to find out ourselves whether the key is affected by capslock.
|
|
|
|
|
//
|
|
|
|
|
// Alternatively, we could relax our exact matching semantics, and make "bind ä"
|
|
|
|
|
// match the "shift-ä" event, as suggested in the kitty issue.
|
|
|
|
|
if caps_lock
|
|
|
|
|
&& modifiers == Modifiers::SHIFT
|
|
|
|
|
&& !codepoint.to_uppercase().eq(Some(codepoint).into_iter())
|
|
|
|
|
{
|
|
|
|
|
modifiers.shift = false;
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
KeyEvent::with_shifted_codepoint(modifiers, codepoint, shifted_codepoint)
|
2024-03-30 16:10:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let key = match c {
|
|
|
|
|
b'$' => {
|
2025-01-13 21:49:57 +01:00
|
|
|
if next_char(self) == b'y' {
|
Replace synchronized update workaround
Old versions of ConHost and Putty can't parse DCS sequences.
For this reason, we briefly switch to the alternate screen buffer while sending
DCS-format (e.g. XTGETTCAP) queries. For extra paranoia, we wrapped this
procedure in a synchronized update. This doesn't seem to be needed; neither
ConHost nor Putty show glitches when the synchronized update is omitted.
As of today, every terminal that implements XTGETTCAP also implements
synchronized updates but that might change.
Let's remove it, to reduce surprise for users and terminal developers.
As a bonus, this also fixes a glitch on Terminal.app which fails to parse
the synchronized-update query (`printf '\x1b[?2026$p'`) and echoes the "p"
(bug report ID FB17141059). Else we could work around this with another
alternate screen buffer.
Unfortunately, this change surfaces two issues with GNU screen. For one,
they don't allow apps to use the alternate screen features (the user may
allow it with "altscreen on"). Second, screen unconditionally echoes
the payload of DCS commands. A possible fix has been suggested at
https://lists.gnu.org/archive/html/screen-devel/2025-04/msg00010.html
I think this combination of behaviors is unique among terminals. I'm sure
there are more terminals that don't parse DCS commands yet, but I think almost
all terminals implement alternate screen buffer. Probably only terminal
multiplexers are prone to this issue. AFAICT very few multiplexers exists,
so we can work around those until they are fixed.
Disable XTGETTCAP queries for GNU screen specifically. Unfortunately screen
does not implement XTVERSION, so I don't know how to reliably identify
it. Instead, check STY and some commonly-used values TERM values.
This has false negatives in some edge cases.
But the worst thing that happens is that "+q696e646e" will be echoed once
at startup, which is easy to ignore, or work around with something like
function workaround --on-event fish_prompt
commandline -f repaint
functions --erase workaround
end
which I don't think we should apply by default, because it can mask other
issues.
We should give screen more time to respond. I guess I'll open an issue so
we don't forget. In doubt, we can always go back to the previous approach
(but implement it in fish script).
Alternative workaround: instead of the alternative screen buffer, we could
try something like clr_eol/clr_bol to erase the spuriously echoed text. I
tried to do this in various ways but (surprisingly) failed.
2025-04-25 21:06:37 +02:00
|
|
|
// DECRPM/DECRQM
|
2024-03-30 16:10:12 +01:00
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
match params[0][0] {
|
2025-03-30 08:33:24 +02:00
|
|
|
23 | 24 => KeyEvent::from(shift(
|
2024-03-30 16:10:12 +01:00
|
|
|
char::from_u32(u32::from(function_key(11)) + params[0][0] - 23).unwrap(), // rxvt style
|
2025-03-30 08:33:24 +02:00
|
|
|
)),
|
2024-03-30 16:10:12 +01:00
|
|
|
_ => return None,
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-14 15:39:53 +02:00
|
|
|
b'A' => masked_key(key::Up, None),
|
|
|
|
|
b'B' => masked_key(key::Down, None),
|
|
|
|
|
b'C' => masked_key(key::Right, None),
|
|
|
|
|
b'D' => masked_key(key::Left, None),
|
2024-03-30 16:10:12 +01:00
|
|
|
b'E' => masked_key('5', None), // Numeric keypad
|
|
|
|
|
b'F' => masked_key(key::End, None), // PC/xterm style
|
|
|
|
|
b'H' => masked_key(key::Home, None), // PC/xterm style
|
|
|
|
|
b'M' | b'm' => {
|
|
|
|
|
self.disable_mouse_tracking();
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
// Generic X10 or modified VT200 sequence, or extended (SGR/1006) mouse
|
|
|
|
|
// reporting mode, with semicolon-separated parameters for button code, Px,
|
|
|
|
|
// and Py, ending with 'M' for button press or 'm' for button release.
|
2024-03-30 16:10:12 +01:00
|
|
|
let sgr = private_mode == Some(b'<');
|
|
|
|
|
if !sgr && c == b'm' {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
2025-01-26 14:52:42 +01:00
|
|
|
let Some(button) = (if sgr {
|
|
|
|
|
Some(params[0][0])
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
} else {
|
2025-01-26 14:52:42 +01:00
|
|
|
u32::from(next_char(self)).checked_sub(32)
|
|
|
|
|
}) else {
|
|
|
|
|
return invalid_sequence(buffer);
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
};
|
2025-01-26 14:52:42 +01:00
|
|
|
let mut convert = |param| {
|
|
|
|
|
(if sgr {
|
|
|
|
|
Some(param)
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
} else {
|
2025-01-26 14:52:42 +01:00
|
|
|
u32::from(next_char(self)).checked_sub(32)
|
|
|
|
|
})
|
|
|
|
|
.and_then(|coord| coord.checked_sub(1))
|
|
|
|
|
.and_then(|coord| usize::try_from(coord).ok())
|
|
|
|
|
};
|
|
|
|
|
let Some(x) = convert(params[1][0]) else {
|
|
|
|
|
return invalid_sequence(buffer);
|
|
|
|
|
};
|
|
|
|
|
let Some(y) = convert(params[2][0]) else {
|
|
|
|
|
return invalid_sequence(buffer);
|
|
|
|
|
};
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
let position = ViewportPosition { x, y };
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
let (modifiers, _caps_lock) = parse_mask((button >> 2) & 0x07);
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
let code = button & 0x43;
|
|
|
|
|
if code != 0 || c != b'M' || modifiers.is_some() {
|
2024-03-30 16:10:12 +01:00
|
|
|
return None;
|
|
|
|
|
}
|
2025-04-26 19:24:23 +02:00
|
|
|
self.push_front(CharEvent::Implicit(ImplicitEvent::MouseLeft(position)));
|
2024-03-30 16:10:12 +01:00
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
b't' => {
|
|
|
|
|
self.disable_mouse_tracking();
|
|
|
|
|
// VT200 button released in mouse highlighting mode at valid text location. 5 chars.
|
|
|
|
|
let _ = next_char(self);
|
|
|
|
|
let _ = next_char(self);
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
b'T' => {
|
|
|
|
|
self.disable_mouse_tracking();
|
|
|
|
|
// VT200 button released in mouse highlighting mode past end-of-line. 9 characters.
|
2024-10-24 11:22:52 -05:00
|
|
|
for _ in 0..6 {
|
2024-03-30 16:10:12 +01:00
|
|
|
let _ = next_char(self);
|
|
|
|
|
}
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
b'P' => masked_key(function_key(1), None),
|
|
|
|
|
b'Q' => masked_key(function_key(2), None),
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
b'R' => {
|
2025-01-26 14:52:42 +01:00
|
|
|
let Some(y) = params[0][0]
|
|
|
|
|
.checked_sub(1)
|
|
|
|
|
.and_then(|y| usize::try_from(y).ok())
|
|
|
|
|
else {
|
|
|
|
|
return invalid_sequence(buffer);
|
|
|
|
|
};
|
|
|
|
|
let Some(x) = params[1][0]
|
|
|
|
|
.checked_sub(1)
|
|
|
|
|
.and_then(|x| usize::try_from(x).ok())
|
|
|
|
|
else {
|
|
|
|
|
return invalid_sequence(buffer);
|
|
|
|
|
};
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
FLOG!(reader, "Received cursor position report y:", y, "x:", x);
|
2025-04-26 19:08:48 +02:00
|
|
|
let cursor_pos = ViewportPosition { x, y };
|
|
|
|
|
self.push_front(CharEvent::QueryResponse(
|
|
|
|
|
QueryResponseEvent::CursorPositionReport(cursor_pos),
|
|
|
|
|
));
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
return None;
|
|
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
b'S' => masked_key(function_key(4), None),
|
|
|
|
|
b'~' => match params[0][0] {
|
|
|
|
|
1 => masked_key(key::Home, None), // VT220/tmux style
|
|
|
|
|
2 => masked_key(key::Insert, None),
|
|
|
|
|
3 => masked_key(key::Delete, None),
|
|
|
|
|
4 => masked_key(key::End, None), // VT220/tmux style
|
|
|
|
|
5 => masked_key(key::PageUp, None),
|
|
|
|
|
6 => masked_key(key::PageDown, None),
|
|
|
|
|
7 => masked_key(key::Home, None), // rxvt style
|
|
|
|
|
8 => masked_key(key::End, None), // rxvt style
|
|
|
|
|
11..=15 => masked_key(
|
|
|
|
|
char::from_u32(u32::from(function_key(1)) + params[0][0] - 11).unwrap(),
|
|
|
|
|
None,
|
|
|
|
|
),
|
|
|
|
|
17..=21 => masked_key(
|
|
|
|
|
char::from_u32(u32::from(function_key(6)) + params[0][0] - 17).unwrap(),
|
|
|
|
|
None,
|
|
|
|
|
),
|
|
|
|
|
23 | 24 => masked_key(
|
|
|
|
|
char::from_u32(u32::from(function_key(11)) + params[0][0] - 23).unwrap(),
|
|
|
|
|
None,
|
|
|
|
|
),
|
2025-03-30 08:33:24 +02:00
|
|
|
25 | 26 => KeyEvent::from(shift(
|
|
|
|
|
char::from_u32(u32::from(function_key(3)) + params[0][0] - 25).unwrap(),
|
|
|
|
|
)), // rxvt style
|
2024-08-11 10:32:51 +02:00
|
|
|
27 => {
|
|
|
|
|
let key =
|
|
|
|
|
canonicalize_keyed_control_char(char::from_u32(params[2][0]).unwrap());
|
|
|
|
|
masked_key(key, None)
|
|
|
|
|
}
|
2025-03-30 08:33:24 +02:00
|
|
|
28 | 29 => KeyEvent::from(shift(
|
|
|
|
|
char::from_u32(u32::from(function_key(5)) + params[0][0] - 28).unwrap(),
|
|
|
|
|
)), // rxvt style
|
|
|
|
|
31 | 32 => KeyEvent::from(shift(
|
|
|
|
|
char::from_u32(u32::from(function_key(7)) + params[0][0] - 31).unwrap(),
|
|
|
|
|
)), // rxvt style
|
|
|
|
|
33 | 34 => KeyEvent::from(shift(
|
|
|
|
|
char::from_u32(u32::from(function_key(9)) + params[0][0] - 33).unwrap(),
|
|
|
|
|
)), // rxvt style
|
2024-03-30 16:10:12 +01:00
|
|
|
200 => {
|
|
|
|
|
self.paste_start_buffering();
|
2024-12-30 07:47:54 +01:00
|
|
|
return None;
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
201 => {
|
|
|
|
|
self.paste_commit();
|
2024-12-30 07:47:54 +01:00
|
|
|
return None;
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
_ => return None,
|
|
|
|
|
},
|
2025-01-25 16:07:15 +01:00
|
|
|
b'c' if private_mode == Some(b'?') => {
|
2025-04-25 23:08:29 +02:00
|
|
|
self.push_front(CharEvent::QueryResponse(
|
|
|
|
|
QueryResponseEvent::PrimaryDeviceAttribute,
|
|
|
|
|
));
|
2025-01-25 16:07:15 +01:00
|
|
|
return None;
|
|
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
b'u' => {
|
2025-01-03 22:19:41 +01:00
|
|
|
if private_mode == Some(b'?') {
|
|
|
|
|
FLOG!(
|
|
|
|
|
reader,
|
|
|
|
|
"Received kitty progressive enhancement flags, marking as supported"
|
|
|
|
|
);
|
2025-01-26 12:08:48 +01:00
|
|
|
KITTY_KEYBOARD_SUPPORTED.store(Capability::Supported as _, Ordering::Release);
|
2025-01-03 22:19:41 +01:00
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-30 16:10:12 +01:00
|
|
|
// Treat numpad keys the same as their non-numpad counterparts. Could add a numpad modifier here.
|
|
|
|
|
let key = match params[0][0] {
|
2025-01-01 21:32:40 +01:00
|
|
|
57361 => key::PrintScreen,
|
|
|
|
|
57363 => key::Menu,
|
2024-04-02 18:19:11 +02:00
|
|
|
57399 => '0',
|
|
|
|
|
57400 => '1',
|
|
|
|
|
57401 => '2',
|
|
|
|
|
57402 => '3',
|
|
|
|
|
57403 => '4',
|
|
|
|
|
57404 => '5',
|
|
|
|
|
57405 => '6',
|
|
|
|
|
57406 => '7',
|
|
|
|
|
57407 => '8',
|
|
|
|
|
57408 => '9',
|
|
|
|
|
57409 => '.',
|
|
|
|
|
57410 => '/',
|
|
|
|
|
57411 => '*',
|
|
|
|
|
57412 => '-',
|
|
|
|
|
57413 => '+',
|
2024-03-30 16:10:12 +01:00
|
|
|
57414 => key::Enter,
|
2024-04-02 18:19:11 +02:00
|
|
|
57415 => '=',
|
2024-03-30 16:10:12 +01:00
|
|
|
57417 => key::Left,
|
|
|
|
|
57418 => key::Right,
|
|
|
|
|
57419 => key::Up,
|
|
|
|
|
57420 => key::Down,
|
|
|
|
|
57421 => key::PageUp,
|
|
|
|
|
57422 => key::PageDown,
|
|
|
|
|
57423 => key::Home,
|
|
|
|
|
57424 => key::End,
|
|
|
|
|
57425 => key::Insert,
|
|
|
|
|
57426 => key::Delete,
|
|
|
|
|
cp => canonicalize_keyed_control_char(char::from_u32(cp).unwrap()),
|
|
|
|
|
};
|
|
|
|
|
masked_key(
|
|
|
|
|
key,
|
|
|
|
|
Some(canonicalize_keyed_control_char(
|
|
|
|
|
char::from_u32(params[0][1]).unwrap(),
|
|
|
|
|
)),
|
|
|
|
|
)
|
|
|
|
|
}
|
2025-03-30 08:33:24 +02:00
|
|
|
b'Z' => KeyEvent::from(shift(key::Tab)),
|
2024-03-30 16:10:12 +01:00
|
|
|
b'I' => {
|
2024-12-29 19:31:15 +01:00
|
|
|
self.push_front(CharEvent::Implicit(ImplicitEvent::FocusIn));
|
2024-12-30 07:47:54 +01:00
|
|
|
return None;
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
b'O' => {
|
2024-12-29 19:31:15 +01:00
|
|
|
self.push_front(CharEvent::Implicit(ImplicitEvent::FocusOut));
|
2024-12-30 07:47:54 +01:00
|
|
|
return None;
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
_ => return None,
|
|
|
|
|
};
|
|
|
|
|
Some(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn disable_mouse_tracking(&mut self) {
|
|
|
|
|
// fish recognizes but does not actually support mouse reporting. We never turn it on, and
|
|
|
|
|
// it's only ever enabled if a program we spawned enabled it and crashed or forgot to turn
|
|
|
|
|
// it off before exiting. We turn it off here to avoid wasting resources.
|
|
|
|
|
FLOG!(reader, "Disabling mouse tracking");
|
|
|
|
|
|
|
|
|
|
// We shouldn't directly manipulate stdout from here, so we ask the reader to do it.
|
2024-12-29 19:31:15 +01:00
|
|
|
self.push_front(CharEvent::Implicit(ImplicitEvent::DisableMouseTracking));
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
|
2025-03-30 08:33:24 +02:00
|
|
|
fn parse_ss3(&mut self, buffer: &mut Vec<u8>) -> Option<KeyEvent> {
|
2024-03-30 16:10:12 +01:00
|
|
|
let mut raw_mask = 0;
|
2024-12-30 07:47:54 +01:00
|
|
|
let Some(mut code) = self.try_readb(buffer) else {
|
2025-03-30 08:33:24 +02:00
|
|
|
return Some(KeyEvent::from(alt('O')));
|
2024-12-30 07:47:54 +01:00
|
|
|
};
|
|
|
|
|
while (b'0'..=b'9').contains(&code) {
|
2024-03-30 16:10:12 +01:00
|
|
|
raw_mask = raw_mask * 10 + u32::from(code - b'0');
|
|
|
|
|
code = self.try_readb(buffer).unwrap_or(0xff);
|
|
|
|
|
}
|
Allow explicit shift modifier for non-ASCII letters, fix capslock behavior
We canonicalize "ctrl-shift-i" to "ctrl-I".
Both when deciphering this notation (as given to builtin bind),
and when receiving it as a key event ("\e[105;73;6u")
This has problems:
A. Our bind notation canonicalization only works for 26 English letters.
For example, "ctrl-shift-ä" is not supported -- only "ctrl-Ä" is.
We could try to fix that but this depends on the keyboard layout.
For example "bind alt-shift-=" and "bind alt-+" are equivalent on a "us"
layout but not on a "de" layout.
B. While capslock is on, the key event won't include a shifted key ("73" here).
This is due a quirk in the kitty keyboard protocol[^1]. This means that
fish_key_reader's canonicalization doesn't work (unless we call toupper()
ourselves).
I think we want to support both notations.
It's recommended to match all of these (in this order) when pressing
"ctrl-shift-i".
1. bind ctrl-shift-i do-something
2. bind ctrl-shift-I do-something
3. bind ctrl-I do-something
4. bind ctrl-i do-something
Support 1 and 3 for now, allowing both bindings to coexist. No priorities
for now. This solves problem A, and -- if we take care to use the explicit
shift notation -- problem B.
For keys that are not affected by capslock, problem B does not apply. In this
case, recommend the shifted notation ("alt-+" instead of "alt-shift-=")
since that seems more intuitive.
Though if we prioritized "alt-shift-=" over "alt-+" as per the recommendation,
that's an argument against the shifted key.
Example output for some key events:
$ fish_key_reader -cV
# decoded from: \e\[61:43\;4u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[61:43\;68u
bind alt-+ 'do something' # recommended notation
bind alt-shift-= 'do something'
# decoded from: \e\[105:73\;6u
bind ctrl-I 'do something'
bind ctrl-shift-i 'do something' # recommended notation
# decoded from: \e\[105\;70u
bind ctrl-shift-i 'do something'
Due to the capslock quirk, the last one has only one matching representation
since there is no shifted key. We could decide to match ctrl-shift-i events
(that don't have a shifted key) to ctrl-I bindings (for ASCII letters), as
before this patch. But that case is very rare, it should only happen when
capslock is on, so it's probably not even a breaking change.
The other way round is supported -- we do match ctrl-I events (typically
with shifted key) to ctrl-shift-i bindings (but only for ASCII letters).
This is mainly for backwards compatibility.
Also note that, bindings without other modifiers currently need to use the
shifted key (like "Ä", not "shift-ä"), since we still get a legacy encoding,
until we request "Report all keys as escape codes".
[^1]: <https://github.com/kovidgoyal/kitty/issues/8493>
2025-03-30 08:33:24 +02:00
|
|
|
let (modifiers, _caps_lock) = parse_mask(raw_mask.saturating_sub(1));
|
2024-03-30 16:10:12 +01:00
|
|
|
#[rustfmt::skip]
|
|
|
|
|
let key = match code {
|
2025-03-30 08:33:24 +02:00
|
|
|
b' ' => KeyEvent::new(modifiers, key::Space),
|
|
|
|
|
b'A' => KeyEvent::new(modifiers, key::Up),
|
|
|
|
|
b'B' => KeyEvent::new(modifiers, key::Down),
|
|
|
|
|
b'C' => KeyEvent::new(modifiers, key::Right),
|
|
|
|
|
b'D' => KeyEvent::new(modifiers, key::Left),
|
|
|
|
|
b'F' => KeyEvent::new(modifiers, key::End),
|
|
|
|
|
b'H' => KeyEvent::new(modifiers, key::Home),
|
|
|
|
|
b'I' => KeyEvent::new(modifiers, key::Tab),
|
|
|
|
|
b'M' => KeyEvent::new(modifiers, key::Enter),
|
|
|
|
|
b'P' => KeyEvent::new(modifiers, function_key(1)),
|
|
|
|
|
b'Q' => KeyEvent::new(modifiers, function_key(2)),
|
|
|
|
|
b'R' => KeyEvent::new(modifiers, function_key(3)),
|
|
|
|
|
b'S' => KeyEvent::new(modifiers, function_key(4)),
|
|
|
|
|
b'X' => KeyEvent::new(modifiers, '='),
|
|
|
|
|
b'j' => KeyEvent::new(modifiers, '*'),
|
|
|
|
|
b'k' => KeyEvent::new(modifiers, '+'),
|
|
|
|
|
b'l' => KeyEvent::new(modifiers, ','),
|
|
|
|
|
b'm' => KeyEvent::new(modifiers, '-'),
|
|
|
|
|
b'n' => KeyEvent::new(modifiers, '.'),
|
|
|
|
|
b'o' => KeyEvent::new(modifiers, '/'),
|
|
|
|
|
b'p' => KeyEvent::new(modifiers, '0'),
|
|
|
|
|
b'q' => KeyEvent::new(modifiers, '1'),
|
|
|
|
|
b'r' => KeyEvent::new(modifiers, '2'),
|
|
|
|
|
b's' => KeyEvent::new(modifiers, '3'),
|
|
|
|
|
b't' => KeyEvent::new(modifiers, '4'),
|
|
|
|
|
b'u' => KeyEvent::new(modifiers, '5'),
|
|
|
|
|
b'v' => KeyEvent::new(modifiers, '6'),
|
|
|
|
|
b'w' => KeyEvent::new(modifiers, '7'),
|
|
|
|
|
b'x' => KeyEvent::new(modifiers, '8'),
|
|
|
|
|
b'y' => KeyEvent::new(modifiers, '9'),
|
2024-03-30 16:10:12 +01:00
|
|
|
_ => return None,
|
|
|
|
|
};
|
|
|
|
|
Some(key)
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2025-03-01 12:14:17 +01:00
|
|
|
fn parse_xtversion(&mut self, buffer: &mut Vec<u8>) {
|
|
|
|
|
assert!(buffer.len() == 3);
|
|
|
|
|
loop {
|
|
|
|
|
match self.try_readb(buffer) {
|
|
|
|
|
None => return,
|
|
|
|
|
Some(b'\x1b') => break,
|
|
|
|
|
Some(_) => continue,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if self.try_readb(buffer) != Some(b'\\') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if buffer[3] != b'|' {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
FLOG!(
|
|
|
|
|
reader,
|
|
|
|
|
format!(
|
|
|
|
|
"Received XTVERSION response: {}",
|
|
|
|
|
str2wcstring(&buffer[4..buffer.len() - 2]),
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-30 08:33:24 +02:00
|
|
|
fn parse_dcs(&mut self, buffer: &mut Vec<u8>) -> Option<KeyEvent> {
|
2025-01-05 11:48:32 +01:00
|
|
|
assert!(buffer.len() == 2);
|
|
|
|
|
let Some(success) = self.try_readb(buffer) else {
|
2025-03-30 08:33:24 +02:00
|
|
|
return Some(KeyEvent::from(alt('P')));
|
2025-01-05 11:48:32 +01:00
|
|
|
};
|
|
|
|
|
let success = match success {
|
|
|
|
|
b'0' => false,
|
|
|
|
|
b'1' => true,
|
2025-03-01 12:14:17 +01:00
|
|
|
b'>' => {
|
|
|
|
|
self.parse_xtversion(buffer);
|
|
|
|
|
return None;
|
|
|
|
|
}
|
2025-01-05 11:48:32 +01:00
|
|
|
_ => return None,
|
|
|
|
|
};
|
|
|
|
|
if self.try_readb(buffer)? != b'+' {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
if self.try_readb(buffer)? != b'r' {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
while self.try_readb(buffer)? != b'\x1b' {}
|
|
|
|
|
if self.try_readb(buffer)? != b'\\' {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
buffer.pop();
|
|
|
|
|
buffer.pop();
|
2025-01-06 21:09:41 +01:00
|
|
|
// \e P 1 r + Pn ST
|
|
|
|
|
// \e P 0 r + msg ST
|
|
|
|
|
let buffer = &buffer[5..];
|
2025-01-05 11:48:32 +01:00
|
|
|
if !success {
|
2025-01-06 21:09:41 +01:00
|
|
|
FLOG!(
|
|
|
|
|
reader,
|
|
|
|
|
format!(
|
|
|
|
|
"Received XTGETTCAP failure response: {}",
|
|
|
|
|
str2wcstring(&parse_hex(buffer)?),
|
|
|
|
|
)
|
|
|
|
|
);
|
2025-01-05 11:48:32 +01:00
|
|
|
return None;
|
|
|
|
|
}
|
2025-01-06 21:09:41 +01:00
|
|
|
let mut buffer = buffer.splitn(2, |&c| c == b'=');
|
2025-01-05 11:48:32 +01:00
|
|
|
let key = buffer.next().unwrap();
|
|
|
|
|
let key = parse_hex(key)?;
|
2025-04-17 13:16:48 +02:00
|
|
|
if let Some(value) = buffer.next() {
|
|
|
|
|
let value = parse_hex(value)?;
|
|
|
|
|
FLOG!(
|
|
|
|
|
reader,
|
|
|
|
|
format!(
|
|
|
|
|
"Received XTGETTCAP response: {}={:?}",
|
|
|
|
|
str2wcstring(&key),
|
|
|
|
|
str2wcstring(&value)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
FLOG!(
|
|
|
|
|
reader,
|
|
|
|
|
format!("Received XTGETTCAP response: {}", str2wcstring(&key))
|
|
|
|
|
);
|
|
|
|
|
}
|
Stop reading terminfo database
Our use of the terminfo database in /usr/share/terminfo/$TERM is both
1. a way for users to configure app behavior in their terminal (by
setting TERM, copying around and modifying terminfo files)
2. a way for terminal emulator developers to advertise support for
backwards-incompatible features that are not otherwise easily observable.
To 1: this is not ideal (it's very easy to break things). There's not many
things that realistically need configuration; let's use shell variables
instead.
To 2: in practice, feature-probing via terminfo is often wrong. There's not
many backwards-incompatible features that need this; for the ones that do
we can still use terminfo capabilities but query the terminal via XTGETTCAP
directly, skipping the file (which may not exist on the same system as
the terminal).
---
Get rid of terminfo. If anyone finds a $TERM where we need different behavior,
we can hardcode that into fish.
* Allow to override this with `fish_features=no-ignore-terminfo fish`
Not sure if we should document this, since it's supposed to be removed soon,
and if someone needs this (which we don't expect), we'd like to know.
* This is supported on a best-effort basis; it doesn't match the previous
behavior exactly. For simplicity of implementation, it will not change
the fact that we now:
* use parm_left_cursor (CSI Ps D) instead of cursor_left (CSI D) if
terminfo claims the former is supported
* no longer support eat_newline_glitch, which seems no longer present
on today's ConEmu and ConHost
* Tested as described in https://github.com/fish-shell/fish-shell/pull/11345#discussion_r2030121580
* add `man fish-terminal-compatibility` to state our assumptions.
This could help terminal emulator developers.
* assume `parm_up_cursor` is supported if the terminal supports XTGETTCAP
* Extract all control sequences to src/terminal_command.rs.
* Remove the "\x1b(B" prefix from EXIT_ATTRIBUTE_MODE. I doubt it's really
needed.
* assume it's generally okay to output 256 colors
Things have improved since commit 3669805627 (Improve compatibility with
0-16 color terminals., 2016-07-21).
Apparently almost every actively developed terminal supports it, including
Terminal.app and GNU screen.
* That is, we default `fish_term256` to true and keep it only as a way to
opt out of the the full 256 palette (e.g. switching to the 16-color
palette).
* `TERM=xterm-16color` has the same opt-out effect.
* `TERM` is generally ignored but add back basic compatiblity by turning
off color for "ansi-m", "linux-m" and "xterm-mono"; these are probably
not set accidentally.
* Since `TERM` is (mostly) ignored, we don't need the magic "xterm" in
tests. Unset it instead.
* Note that our pexpect tests used a dumb terminal because:
1. it makes fish do a full redraw of the commandline everytime, making it
easier to write assertions.
2. it disables all control sequences for colors, etc, which we usually
don't want to test explicitly.
I don't think TERM=dumb has any other use, so it would be better
to print escape sequences unconditionally, and strip them in
the test driver (leaving this for later, since it's a bit more involved).
Closes #11344
Closes #11345
2025-03-20 22:02:38 +01:00
|
|
|
if key == SCROLL_FORWARD_TERMINFO_CODE.as_bytes() {
|
2025-01-05 11:48:32 +01:00
|
|
|
SCROLL_FORWARD_SUPPORTED.store(true);
|
|
|
|
|
FLOG!(reader, "Scroll forward is supported");
|
|
|
|
|
}
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-30 10:28:47 +02:00
|
|
|
fn readch_timed_esc(&mut self) -> Option<CharEvent> {
|
2023-11-25 14:37:42 -08:00
|
|
|
self.readch_timed(WAIT_ON_ESCAPE_MS.load(Ordering::Relaxed))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn readch_timed_sequence_key(&mut self) -> Option<CharEvent> {
|
|
|
|
|
let wait_on_sequence_key_ms = WAIT_ON_SEQUENCE_KEY_MS.load(Ordering::Relaxed);
|
|
|
|
|
if wait_on_sequence_key_ms == WAIT_ON_SEQUENCE_KEY_INFINITE {
|
|
|
|
|
return Some(self.readch());
|
|
|
|
|
}
|
|
|
|
|
self.readch_timed(wait_on_sequence_key_ms)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Like readch(), except it will wait at most wait_time_ms milliseconds for a
|
|
|
|
|
/// character to be available for reading.
|
2024-05-06 14:58:10 -05:00
|
|
|
/// Return None on timeout, the event on success.
|
2023-11-25 14:37:42 -08:00
|
|
|
fn readch_timed(&mut self, wait_time_ms: usize) -> Option<CharEvent> {
|
|
|
|
|
if let Some(evt) = self.try_pop() {
|
|
|
|
|
return Some(evt);
|
|
|
|
|
}
|
2024-05-16 10:52:19 +02:00
|
|
|
terminal_protocols_enable_ifn();
|
2023-11-25 14:37:42 -08:00
|
|
|
|
|
|
|
|
// We are not prepared to handle a signal immediately; we only want to know if we get input on
|
|
|
|
|
// our fd before the timeout. Use pselect to block all signals; we will handle signals
|
|
|
|
|
// before the next call to readch().
|
|
|
|
|
let mut sigs: libc::sigset_t = unsafe { std::mem::zeroed() };
|
|
|
|
|
unsafe { libc::sigfillset(&mut sigs) };
|
|
|
|
|
|
|
|
|
|
// pselect expects timeouts in nanoseconds.
|
|
|
|
|
const NSEC_PER_MSEC: u64 = 1000 * 1000;
|
|
|
|
|
const NSEC_PER_SEC: u64 = NSEC_PER_MSEC * 1000;
|
|
|
|
|
let wait_nsec: u64 = (wait_time_ms as u64) * NSEC_PER_MSEC;
|
Revert libc time_t changes
This was based on a misunderstanding.
On musl, 64-bit time_t on 32-bit architectures was introduced in version 1.2.0,
by introducing new symbols. The old symbols still exist, to allow programs compiled against older versions
to keep running on 1.2.0+, preserving ABI-compatibility. (see musl commit 38143339646a4ccce8afe298c34467767c899f51)
Programs compiled against 1.2.0+ will get the new symbols, and will therefore think time_t is 64-bit.
Unfortunately, rust's libc crate uses its own definition of these types, and does not check for musl version.
Currently, it includes the pre-1.2.0 32-bit type.
That means:
- If you run on a 32-bit system like i686
- ... and compile against a C-library other than libc
- ... and pass it a time_t-containing struct like timespec or stat
... you need to arrange for that library to be built against musl <1.2.0.
Or, as https://github.com/ericonr/rust-time64 says:
> Therefore, for "old" 32-bit targets (riscv32 is supposed to default to time64),
> any Rust code that interacts with C code built on musl after 1.2.0,
> using types based on time_t (arguably, the main ones are struct timespec and struct stat) in their interface,
> will be completely miscompiled.
However, while fish runs on i686 and compiles against pcre2, we do not pass pcre2 a time_t.
Our only uses of time_t are confined to interactions with libc, in which case with musl we would simply use the legacy ABI.
I have compiled an i686 fish against musl to confirm and can find no issue.
This reverts commit 55196ee2a0430d920ea7a2c89a6e322615f78334.
This reverts commit 4992f8896633fb8ca8d89e09f02330cd49395485.
This reverts commit 46c8ba2c9fec77195091ddcf7ee0bb3d9a6e5f54.
This reverts commit 3a9b4149da7d44b8648702f17d9e9eef651e56f9.
This reverts commit 5f9e9cbe741025231acfb24dc900433e1c6738ac.
This reverts commit 338579b78ca2ba0aab108304bc33a53fddeb11ba.
This reverts commit d19e5508d7b406da6813edb9d0a6909094d20e5a.
This reverts commit b64045dc189ec58b6bd3dea71e1441e00876904c.
Closes #10634
2024-08-27 11:15:27 +02:00
|
|
|
let timeout = libc::timespec {
|
2023-11-25 14:37:42 -08:00
|
|
|
tv_sec: (wait_nsec / NSEC_PER_SEC).try_into().unwrap(),
|
|
|
|
|
tv_nsec: (wait_nsec % NSEC_PER_SEC).try_into().unwrap(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// We have one fd of interest.
|
|
|
|
|
let mut fdset: libc::fd_set = unsafe { std::mem::zeroed() };
|
|
|
|
|
let in_fd = self.get_in_fd();
|
|
|
|
|
unsafe {
|
|
|
|
|
libc::FD_ZERO(&mut fdset);
|
|
|
|
|
libc::FD_SET(in_fd, &mut fdset);
|
|
|
|
|
};
|
Revert libc time_t changes
This was based on a misunderstanding.
On musl, 64-bit time_t on 32-bit architectures was introduced in version 1.2.0,
by introducing new symbols. The old symbols still exist, to allow programs compiled against older versions
to keep running on 1.2.0+, preserving ABI-compatibility. (see musl commit 38143339646a4ccce8afe298c34467767c899f51)
Programs compiled against 1.2.0+ will get the new symbols, and will therefore think time_t is 64-bit.
Unfortunately, rust's libc crate uses its own definition of these types, and does not check for musl version.
Currently, it includes the pre-1.2.0 32-bit type.
That means:
- If you run on a 32-bit system like i686
- ... and compile against a C-library other than libc
- ... and pass it a time_t-containing struct like timespec or stat
... you need to arrange for that library to be built against musl <1.2.0.
Or, as https://github.com/ericonr/rust-time64 says:
> Therefore, for "old" 32-bit targets (riscv32 is supposed to default to time64),
> any Rust code that interacts with C code built on musl after 1.2.0,
> using types based on time_t (arguably, the main ones are struct timespec and struct stat) in their interface,
> will be completely miscompiled.
However, while fish runs on i686 and compiles against pcre2, we do not pass pcre2 a time_t.
Our only uses of time_t are confined to interactions with libc, in which case with musl we would simply use the legacy ABI.
I have compiled an i686 fish against musl to confirm and can find no issue.
This reverts commit 55196ee2a0430d920ea7a2c89a6e322615f78334.
This reverts commit 4992f8896633fb8ca8d89e09f02330cd49395485.
This reverts commit 46c8ba2c9fec77195091ddcf7ee0bb3d9a6e5f54.
This reverts commit 3a9b4149da7d44b8648702f17d9e9eef651e56f9.
This reverts commit 5f9e9cbe741025231acfb24dc900433e1c6738ac.
This reverts commit 338579b78ca2ba0aab108304bc33a53fddeb11ba.
This reverts commit d19e5508d7b406da6813edb9d0a6909094d20e5a.
This reverts commit b64045dc189ec58b6bd3dea71e1441e00876904c.
Closes #10634
2024-08-27 11:15:27 +02:00
|
|
|
let res = unsafe {
|
|
|
|
|
libc::pselect(
|
|
|
|
|
in_fd + 1,
|
|
|
|
|
&mut fdset,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
&timeout,
|
|
|
|
|
&sigs,
|
|
|
|
|
)
|
|
|
|
|
};
|
2023-11-25 14:37:42 -08:00
|
|
|
|
|
|
|
|
// Prevent signal starvation on WSL causing the `torn_escapes.py` test to fail
|
2024-05-20 14:06:50 -05:00
|
|
|
if is_windows_subsystem_for_linux(WSL::V1) {
|
2023-11-25 14:37:42 -08:00
|
|
|
// Merely querying the current thread's sigmask is sufficient to deliver a pending signal
|
|
|
|
|
let _ = unsafe { libc::pthread_sigmask(0, ptr::null(), &mut sigs) };
|
|
|
|
|
}
|
|
|
|
|
if res > 0 {
|
|
|
|
|
return Some(self.readch());
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-26 16:21:11 -07:00
|
|
|
/// Return the fd from which to read.
|
|
|
|
|
fn get_in_fd(&self) -> RawFd {
|
|
|
|
|
self.get_input_data().in_fd
|
|
|
|
|
}
|
2023-11-25 14:37:42 -08:00
|
|
|
|
2024-05-26 16:21:11 -07:00
|
|
|
/// Return the input data. This is to be implemented by the concrete type.
|
|
|
|
|
fn get_input_data(&self) -> &InputData;
|
|
|
|
|
fn get_input_data_mut(&mut self) -> &mut InputData;
|
2023-11-25 14:37:42 -08:00
|
|
|
|
2024-03-30 16:10:12 +01:00
|
|
|
// Support for "bracketed paste"
|
|
|
|
|
// The way it works is that we acknowledge our support by printing
|
|
|
|
|
// \e\[?2004h
|
|
|
|
|
// then the terminal will "bracket" every paste in
|
|
|
|
|
// \e\[200~ and \e\[201~
|
|
|
|
|
// Every character in between those two will be part of the paste and should not cause a binding to execute (like \n executing commands).
|
|
|
|
|
//
|
|
|
|
|
// We enable it after every command and disable it before, see the terminal protocols logic.
|
|
|
|
|
//
|
|
|
|
|
// Support for this seems to be ubiquitous - emacs enables it unconditionally (!) since 25.1
|
|
|
|
|
// (though it only supports it since then, it seems to be the last term to gain support).
|
|
|
|
|
//
|
|
|
|
|
// See http://thejh.net/misc/website-terminal-copy-paste.
|
2024-05-26 16:21:11 -07:00
|
|
|
|
|
|
|
|
fn paste_start_buffering(&mut self) {
|
|
|
|
|
self.get_input_data_mut().paste_buffer = Some(Vec::new());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn paste_is_buffering(&self) -> bool {
|
|
|
|
|
self.get_input_data().paste_buffer.is_some()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn paste_push_char(&mut self, b: u8) {
|
|
|
|
|
self.get_input_data_mut()
|
|
|
|
|
.paste_buffer
|
|
|
|
|
.as_mut()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.push(b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn paste_commit(&mut self) {
|
|
|
|
|
self.get_input_data_mut().paste_buffer = None;
|
|
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
/// Enqueue a character or a readline function to the queue of unread characters that
|
|
|
|
|
/// readch will return before actually reading from fd 0.
|
|
|
|
|
fn push_back(&mut self, ch: CharEvent) {
|
2024-05-26 16:21:11 -07:00
|
|
|
self.get_input_data_mut().queue.push_back(ch);
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Add a character or a readline function to the front of the queue of unread characters. This
|
|
|
|
|
/// will be the next character returned by readch.
|
|
|
|
|
fn push_front(&mut self, ch: CharEvent) {
|
2024-05-26 16:21:11 -07:00
|
|
|
self.get_input_data_mut().queue.push_front(ch);
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Find the first sequence of non-char events, and promote them to the front.
|
|
|
|
|
fn promote_interruptions_to_front(&mut self) {
|
|
|
|
|
// Find the first sequence of non-char events.
|
|
|
|
|
// EOF is considered a char: we don't want to pull EOF in front of real chars.
|
2024-05-26 16:21:11 -07:00
|
|
|
let queue = &mut self.get_input_data_mut().queue;
|
2024-12-29 19:31:15 +01:00
|
|
|
let is_char = |evt: &CharEvent| {
|
|
|
|
|
evt.is_char() || matches!(evt, CharEvent::Implicit(ImplicitEvent::Eof))
|
|
|
|
|
};
|
2023-11-25 14:37:42 -08:00
|
|
|
// Find the index of the first non-char event.
|
|
|
|
|
// If there's none, we're done.
|
|
|
|
|
let Some(first): Option<usize> = queue.iter().position(|e| !is_char(e)) else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
let last = queue
|
|
|
|
|
.range(first..)
|
|
|
|
|
.position(is_char)
|
|
|
|
|
.map_or(queue.len(), |x| x + first);
|
|
|
|
|
// Move the non-char events to the front, retaining their order.
|
|
|
|
|
let elems: Vec<CharEvent> = queue.drain(first..last).collect();
|
|
|
|
|
for elem in elems.into_iter().rev() {
|
|
|
|
|
queue.push_front(elem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Add multiple readline events to the front of the queue of unread characters.
|
|
|
|
|
/// The order of the provided events is not changed, i.e. they are not inserted in reverse
|
|
|
|
|
/// order. That is, the first element in evts will be the first element returned.
|
|
|
|
|
fn insert_front<I>(&mut self, evts: I)
|
|
|
|
|
where
|
|
|
|
|
I: IntoIterator<Item = CharEvent>,
|
|
|
|
|
I::IntoIter: DoubleEndedIterator,
|
|
|
|
|
{
|
2024-05-26 16:21:11 -07:00
|
|
|
let queue = &mut self.get_input_data_mut().queue;
|
2023-11-25 14:37:42 -08:00
|
|
|
let iter = evts.into_iter().rev();
|
|
|
|
|
queue.reserve(iter.size_hint().0);
|
|
|
|
|
for evt in iter {
|
|
|
|
|
queue.push_front(evt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Forget all enqueued readline events in the front of the queue.
|
|
|
|
|
fn drop_leading_readline_events(&mut self) {
|
2024-05-26 16:21:11 -07:00
|
|
|
let queue = &mut self.get_input_data_mut().queue;
|
2023-11-25 14:37:42 -08:00
|
|
|
while let Some(evt) = queue.front() {
|
2024-03-02 07:58:01 +01:00
|
|
|
if evt.is_readline_or_command() {
|
2023-11-25 14:37:42 -08:00
|
|
|
queue.pop_front();
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-25 22:24:06 +02:00
|
|
|
fn blocking_query(&self) -> RefMut<'_, Option<TerminalQuery>>;
|
|
|
|
|
fn is_blocked_querying(&self) -> bool {
|
|
|
|
|
self.blocking_query().is_some()
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
}
|
2025-01-25 16:07:15 +01:00
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
/// Override point for when we are about to (potentially) block in select(). The default does
|
|
|
|
|
/// nothing.
|
|
|
|
|
fn prepare_to_select(&mut self) {}
|
|
|
|
|
|
2024-03-30 16:10:12 +01:00
|
|
|
/// Called when select() is interrupted by a signal.
|
2024-05-26 16:21:11 -07:00
|
|
|
fn select_interrupted(&mut self) {}
|
2023-11-25 14:37:42 -08:00
|
|
|
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
fn enqueue_interrupt_key(&mut self) {
|
|
|
|
|
let vintr = shell_modes().c_cc[libc::VINTR];
|
|
|
|
|
if vintr != 0 {
|
2025-03-30 08:33:24 +02:00
|
|
|
let interrupt_evt = CharEvent::from_key(KeyEvent::from_single_byte(vintr));
|
2025-04-25 22:24:06 +02:00
|
|
|
if stop_query(self.blocking_query()) {
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
FLOG!(
|
|
|
|
|
reader,
|
2025-01-25 16:07:15 +01:00
|
|
|
"Received interrupt, giving up on waiting for terminal response"
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
);
|
|
|
|
|
self.push_back(interrupt_evt);
|
|
|
|
|
} else {
|
|
|
|
|
self.push_front(interrupt_evt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
/// Override point for when when select() is interrupted by the universal variable notifier.
|
|
|
|
|
/// The default does nothing.
|
|
|
|
|
fn uvar_change_notified(&mut self) {}
|
|
|
|
|
|
2024-05-27 12:04:08 -07:00
|
|
|
/// Override point for when the ioport is ready.
|
|
|
|
|
/// The default does nothing.
|
|
|
|
|
fn ioport_notified(&mut self) {}
|
|
|
|
|
|
2024-05-26 16:21:11 -07:00
|
|
|
/// Reset the function status.
|
|
|
|
|
fn get_function_status(&self) -> bool {
|
|
|
|
|
self.get_input_data().function_status
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-06 14:58:10 -05:00
|
|
|
/// Return if we have any lookahead.
|
2023-11-25 14:37:42 -08:00
|
|
|
fn has_lookahead(&self) -> bool {
|
2024-05-26 16:21:11 -07:00
|
|
|
!self.get_input_data().queue.is_empty()
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-15 00:28:47 +02:00
|
|
|
pub(crate) enum DecodeState {
|
|
|
|
|
Incomplete,
|
|
|
|
|
Complete,
|
|
|
|
|
Error,
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-15 09:21:11 +02:00
|
|
|
#[derive(Eq, PartialEq)]
|
|
|
|
|
pub(crate) enum InvalidPolicy {
|
|
|
|
|
Error,
|
|
|
|
|
Passthrough,
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-15 00:28:47 +02:00
|
|
|
pub(crate) fn decode_input_byte(
|
|
|
|
|
out_seq: &mut WString,
|
2025-04-15 09:21:11 +02:00
|
|
|
invalid_policy: InvalidPolicy,
|
2025-04-15 00:28:47 +02:00
|
|
|
state: &mut mbstate_t,
|
|
|
|
|
buffer: &[u8],
|
|
|
|
|
consumed: &mut usize,
|
|
|
|
|
) -> DecodeState {
|
|
|
|
|
use DecodeState::*;
|
|
|
|
|
let mut res: char = '\0';
|
|
|
|
|
let read_byte = *buffer.last().unwrap();
|
|
|
|
|
if crate::libc::MB_CUR_MAX() == 1 {
|
|
|
|
|
// single-byte locale, all values are legal
|
|
|
|
|
// FIXME: this looks wrong, this falsely assumes that
|
|
|
|
|
// the single-byte locale is compatible with Unicode upper-ASCII.
|
|
|
|
|
res = read_byte.into();
|
|
|
|
|
out_seq.push(res);
|
|
|
|
|
return Complete;
|
|
|
|
|
}
|
2025-04-15 09:21:11 +02:00
|
|
|
let mut invalid = |out_seq: &mut WString, log_error: fn()| match invalid_policy {
|
|
|
|
|
InvalidPolicy::Error => {
|
|
|
|
|
(log_error)();
|
|
|
|
|
Error
|
|
|
|
|
}
|
|
|
|
|
InvalidPolicy::Passthrough => {
|
|
|
|
|
for &b in &buffer[*consumed..] {
|
|
|
|
|
out_seq.push(encode_byte_to_char(b));
|
|
|
|
|
}
|
|
|
|
|
*consumed = buffer.len();
|
|
|
|
|
Complete
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-04-15 00:28:47 +02:00
|
|
|
let mut codepoint = u32::from(res);
|
|
|
|
|
match unsafe {
|
|
|
|
|
mbrtowc(
|
|
|
|
|
std::ptr::addr_of_mut!(codepoint),
|
|
|
|
|
std::ptr::addr_of!(read_byte).cast(),
|
|
|
|
|
1,
|
|
|
|
|
state,
|
|
|
|
|
)
|
|
|
|
|
} as isize
|
|
|
|
|
{
|
|
|
|
|
-1 => {
|
2025-04-15 09:21:11 +02:00
|
|
|
return invalid(out_seq, || FLOG!(reader, "Illegal input encoding"));
|
2025-04-15 00:28:47 +02:00
|
|
|
}
|
|
|
|
|
-2 => {
|
|
|
|
|
// Sequence not yet complete.
|
|
|
|
|
return Incomplete;
|
|
|
|
|
}
|
|
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
if let Some(res) = char::from_u32(codepoint) {
|
|
|
|
|
// Sequence complete.
|
|
|
|
|
if !fish_reserved_codepoint(res) {
|
|
|
|
|
*consumed += 1;
|
|
|
|
|
out_seq.push(res);
|
|
|
|
|
return Complete;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-15 09:21:11 +02:00
|
|
|
invalid(out_seq, || FLOG!(reader, "Illegal codepoint"))
|
2025-04-15 00:28:47 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-25 22:24:06 +02:00
|
|
|
pub(crate) fn stop_query(mut query: RefMut<'_, Option<TerminalQuery>>) -> bool {
|
|
|
|
|
query.take().is_some()
|
2025-02-01 09:08:04 +01:00
|
|
|
}
|
|
|
|
|
|
2025-03-30 08:33:24 +02:00
|
|
|
fn invalid_sequence(buffer: &[u8]) -> Option<KeyEvent> {
|
2025-01-26 14:52:42 +01:00
|
|
|
FLOG!(
|
|
|
|
|
reader,
|
|
|
|
|
"Error: invalid escape sequence: ",
|
|
|
|
|
DisplayBytes(buffer)
|
|
|
|
|
);
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct DisplayBytes<'a>(&'a [u8]);
|
|
|
|
|
|
|
|
|
|
impl<'a> std::fmt::Display for DisplayBytes<'a> {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
for (i, &c) in self.0.iter().enumerate() {
|
|
|
|
|
if i != 0 {
|
|
|
|
|
write!(f, " ")?;
|
|
|
|
|
}
|
2025-04-01 22:59:36 +02:00
|
|
|
write!(f, "{}", char_to_symbol(char::from(c), i == 0))?;
|
2025-01-26 14:52:42 +01:00
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 14:37:42 -08:00
|
|
|
/// A simple, concrete implementation of InputEventQueuer.
|
|
|
|
|
pub struct InputEventQueue {
|
2024-05-26 16:21:11 -07:00
|
|
|
data: InputData,
|
2025-04-25 22:24:06 +02:00
|
|
|
blocking_query: RefCell<Option<TerminalQuery>>,
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl InputEventQueue {
|
2025-04-18 20:37:52 +02:00
|
|
|
pub fn new(in_fd: RawFd) -> Self {
|
|
|
|
|
Self {
|
2024-05-26 16:21:11 -07:00
|
|
|
data: InputData::new(in_fd),
|
2025-04-25 22:24:06 +02:00
|
|
|
blocking_query: RefCell::new(None),
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl InputEventQueuer for InputEventQueue {
|
2024-05-26 16:21:11 -07:00
|
|
|
fn get_input_data(&self) -> &InputData {
|
|
|
|
|
&self.data
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
|
|
|
|
|
2024-05-26 16:21:11 -07:00
|
|
|
fn get_input_data_mut(&mut self) -> &mut InputData {
|
|
|
|
|
&mut self.data
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
2024-03-30 16:10:12 +01:00
|
|
|
|
|
|
|
|
fn select_interrupted(&mut self) {
|
|
|
|
|
if reader_test_and_clear_interrupted() != 0 {
|
Move cursor on mouse click via kitty's OSC 133 click_events=1
When the user clicks somewhere in the prompt, kitty asks the shell
to move the cursor there (since there is not much else to do).
This is currently implemented by sending an array of
forward-char-passive commands. This has problems, for example it
is really slow on large command lines (probably because we repaint
everytime).
Implement kitty's `click_events=1` flag to set the
position directly. To convert from terminal-coordinates
to fish-coordinates, query [CSI 6 n Report Cursor
Position](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
and use it to compute the left prompt's terminal-coordinates (which
are (0, 0) in fish-coordinates).
Unfortunately this doesn't yet work correctly while the terminal
is scrolled. This is probably because the cursor position is wrong
if off-screen. To fix that we could probably record the cursor
position while not scrolled, but it doesn't seem terribly important
(the existing implementation also doesn't get it right).
We still turn off mouse reporting. If we turned it on, it
would be harder to select text in the terminal itself (not fish).
This would typically mean that mouse-drag will alter fish's
selection and shift+mouse-drag or alt+mouse-drag can be used.
To improve this, we could try to synchronize the selection: if parts
of the fish commandline are selected in the terminal's selection,
copy that to fish's selection and vice versa.
Or maybe there is an intuitive criteria, like: whenever we receive a
mouse event outside fish, turn off mouse reporting, and turn it back on
whenver we receive new keyboard input. One problem is that we lose
one event (though we could send it back to the terminal). Another
problem is we would turn it back on too late in some scenarios.
Closes #10932
2024-12-21 19:41:41 +01:00
|
|
|
self.enqueue_interrupt_key();
|
2024-03-30 16:10:12 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-04-25 22:24:06 +02:00
|
|
|
fn blocking_query(&self) -> RefMut<'_, Option<TerminalQuery>> {
|
|
|
|
|
self.blocking_query.borrow_mut()
|
2025-04-28 20:35:34 +02:00
|
|
|
}
|
2023-11-25 14:37:42 -08:00
|
|
|
}
|
2025-01-05 11:48:32 +01:00
|
|
|
|
|
|
|
|
fn parse_hex(hex: &[u8]) -> Option<Vec<u8>> {
|
|
|
|
|
if hex.len() % 2 != 0 {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
let mut result = vec![0; hex.len() / 2];
|
|
|
|
|
let mut i = 0;
|
|
|
|
|
while i < hex.len() {
|
|
|
|
|
let d1 = char::from(hex[i]).to_digit(16)?;
|
|
|
|
|
let d2 = char::from(hex[i + 1]).to_digit(16)?;
|
|
|
|
|
let decoded = u8::try_from(16 * d1 + d2).unwrap();
|
|
|
|
|
result[i / 2] = decoded;
|
|
|
|
|
i += 2;
|
|
|
|
|
}
|
|
|
|
|
Some(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_parse_hex() {
|
2025-03-08 10:52:27 -08:00
|
|
|
assert_eq!(parse_hex(b"3d"), Some(vec![61]));
|
2025-01-05 11:48:32 +01:00
|
|
|
}
|