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. and the second parameter is the column number.
Both start at 1. 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. - 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>` - 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`` ``\e]133;A; click_events=1\x07``
- -
- Mark prompt start (OSC 133), with kitty's ``click_events`` extension. - Mark prompt start (OSC 133), with kitty's ``click_events`` extension.
If ``click_events`` is implemented, The ``click_events`` extension enables mouse clicks to move the cursor or select pager items,
the :ref:`cursor position reporting <term-compat-cursor-position-report>` feature is required. assuming that :ref:`cursor position reporting <term-compat-cursor-position-report>` is available.
- FinalTerm, kitty - FinalTerm, kitty
* - ``\e]133;C; cmdline_url= Pt \x07`` * - ``\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)] #[derive(Clone, Eq, PartialEq)]
pub enum CursorPositionQuery { pub enum CursorPositionQueryKind {
MouseLeft(ViewportPosition), MouseLeft(ViewportPosition),
ScrollbackPush, 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)] #[derive(Eq, PartialEq)]
pub enum TerminalQuery { pub enum TerminalQuery {
PrimaryDeviceAttribute, Initial,
CursorPosition(CursorPositionQuery), CursorPosition(CursorPositionQuery),
} }

View File

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