Degrade gracefully when failing to receive cursor position report

Follow up the cursor position report query with a primary device
attribute one.  When that one arrives, any cursor position response
must have arrived too. This allows us to prevent a hang on terminals
that only support primary device attribute.
This commit is contained in:
Johannes Altmanninger
2025-09-23 11:15:19 +02:00
parent 1612576d1c
commit 06ede39ec9
3 changed files with 51 additions and 24 deletions

View File

@@ -210,7 +210,7 @@ Optional Commands
and the second parameter is the column number.
Both start at 1.
This is required for terminals that either
This is used inside terminals that either
- implement the OSC 133 :ref:`click_events <term-compat-osc-133>` feature.
- advertise the :ref:`indn <term-compat-indn>` capability via :ref:`XTGETTCAP <term-compat-xtgettcap>`
@@ -284,8 +284,8 @@ Optional Commands
``\e]133;A; click_events=1\x07``
-
- Mark prompt start (OSC 133), with kitty's ``click_events`` extension.
If ``click_events`` is implemented,
the :ref:`cursor position reporting <term-compat-cursor-position-report>` feature is required.
The ``click_events`` extension enables mouse clicks to move the cursor or select pager items,
assuming that :ref:`cursor position reporting <term-compat-cursor-position-report>` is available.
- FinalTerm, kitty
* - ``\e]133;C; cmdline_url= Pt \x07``
-

View File

@@ -756,14 +756,26 @@ pub fn function_set_status(&mut self, status: bool) {
}
#[derive(Clone, Eq, PartialEq)]
pub enum CursorPositionQuery {
pub enum CursorPositionQueryKind {
MouseLeft(ViewportPosition),
ScrollbackPush,
}
#[derive(Clone, Eq, PartialEq)]
pub struct CursorPositionQuery {
pub kind: CursorPositionQueryKind,
pub result: Option<ViewportPosition>,
}
impl CursorPositionQuery {
pub fn new(kind: CursorPositionQueryKind) -> Self {
Self { kind, result: None }
}
}
#[derive(Eq, PartialEq)]
pub enum TerminalQuery {
PrimaryDeviceAttribute,
Initial,
CursorPosition(CursorPositionQuery),
}

View File

@@ -87,8 +87,8 @@
};
use crate::input::init_input;
use crate::input_common::{
stop_query, CharEvent, CharInputStyle, CursorPositionQuery, ImplicitEvent, InputData,
QueryResponseEvent, ReadlineCmd, TerminalQuery,
stop_query, CharEvent, CharInputStyle, CursorPositionQuery, CursorPositionQueryKind,
ImplicitEvent, InputData, QueryResponseEvent, ReadlineCmd, TerminalQuery,
};
use crate::io::IoChain;
use crate::key::ViewportPosition;
@@ -276,7 +276,7 @@ pub(crate) fn initial_query(
query_capabilities_via_dcs(out.by_ref(), vars);
}
out.write_command(QueryPrimaryDeviceAttribute);
Some(TerminalQuery::PrimaryDeviceAttribute)
Some(TerminalQuery::Initial)
};
RefCell::new(query)
});
@@ -1548,7 +1548,12 @@ pub fn request_cursor_position(&mut self, out: &mut Outputter, q: CursorPosition
let mut query = self.blocking_query();
assert!(query.is_none());
*query = Some(TerminalQuery::CursorPosition(q));
out.write_command(QueryCursorPosition);
{
out.begin_buffering();
out.write_command(QueryCursorPosition);
out.write_command(QueryPrimaryDeviceAttribute);
out.end_buffering();
}
drop(query);
self.save_screen_state();
}
@@ -2560,19 +2565,16 @@ fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlo
FLOG!(reader, "Mouse left click", position);
self.request_cursor_position(
&mut Outputter::stdoutput().borrow_mut(),
CursorPositionQuery::MouseLeft(position),
CursorPositionQuery::new(CursorPositionQueryKind::MouseLeft(position)),
);
}
},
CharEvent::QueryResponse(query_result) => {
let maybe_query = self.blocking_query();
let query = &maybe_query;
let mut maybe_query = self.blocking_query();
let query = &mut maybe_query;
use QueryResponseEvent::*;
let query = match (&**query, query_result) {
(
Some(TerminalQuery::PrimaryDeviceAttribute),
PrimaryDeviceAttributeResponse,
) => {
let query = match (&mut **query, query_result) {
(Some(TerminalQuery::Initial), PrimaryDeviceAttributeResponse) => {
if get_kitty_keyboard_capability() == Capability::Unknown {
set_kitty_keyboard_capability(
reader_save_screen_state,
@@ -2584,15 +2586,28 @@ fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlo
(
Some(TerminalQuery::CursorPosition(cursor_pos_query)),
CursorPositionResponse(cursor_pos),
) => {
cursor_pos_query.result = Some(cursor_pos);
maybe_query
}
(
Some(TerminalQuery::CursorPosition(cursor_pos_query)),
PrimaryDeviceAttributeResponse,
) => {
let cursor_pos_query = cursor_pos_query.clone();
drop(maybe_query);
match cursor_pos_query {
CursorPositionQuery::MouseLeft(click_position) => {
self.mouse_left_click(cursor_pos, click_position);
use CursorPositionQueryKind::*;
let cursor_pos = cursor_pos_query.result;
match cursor_pos_query.kind {
MouseLeft(click_position) => {
if let Some(cursor_pos) = cursor_pos {
self.mouse_left_click(cursor_pos, click_position);
}
}
CursorPositionQuery::ScrollbackPush => {
self.screen.push_to_scrollback(cursor_pos.y);
ScrollbackPush => {
if let Some(cursor_pos) = cursor_pos {
self.screen.push_to_scrollback(cursor_pos.y);
}
}
};
self.blocking_query()
@@ -3908,12 +3923,12 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
drop(query);
self.request_cursor_position(
&mut Outputter::stdoutput().borrow_mut(),
CursorPositionQuery::ScrollbackPush,
CursorPositionQuery::new(CursorPositionQueryKind::ScrollbackPush),
);
return;
};
match query {
TerminalQuery::PrimaryDeviceAttribute => panic!(),
TerminalQuery::Initial => panic!(),
TerminalQuery::CursorPosition(_) => {
// TODO: re-queue it I guess.
FLOG!(