diff --git a/src/terminal/terminaldisplay.cc b/src/terminal/terminaldisplay.cc index c44952857..b68c417a6 100644 --- a/src/terminal/terminaldisplay.cc +++ b/src/terminal/terminaldisplay.cc @@ -119,6 +119,11 @@ std::string Display::new_frame( bool initialized, const Framebuffer& last, const frame.append( tmp ); } + if ( f.ds.cursor_shape != frame.last_frame.ds.cursor_shape && f.ds.cursor_shape != -1 ) { + snprintf(tmp, sizeof(tmp), "\033[%d q", f.ds.cursor_shape); + frame.append(tmp); + } + /* has size changed? */ if ( ( !initialized ) || ( f.ds.get_width() != frame.last_frame.ds.get_width() ) || ( f.ds.get_height() != frame.last_frame.ds.get_height() ) ) { diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc index 5d3a1bc07..13a6750f5 100644 --- a/src/terminal/terminalframebuffer.cc +++ b/src/terminal/terminalframebuffer.cc @@ -64,9 +64,9 @@ DrawState::DrawState( int s_width, int s_height ) combining_char_row( 0 ), default_tabs( true ), tabs( s_width ), scrolling_region_top_row( 0 ), scrolling_region_bottom_row( height - 1 ), renditions( 0 ), save(), next_print_will_wrap( false ), origin_mode( false ), auto_wrap_mode( true ), insert_mode( false ), cursor_visible( true ), - reverse_video( false ), bracketed_paste( false ), mouse_reporting_mode( MOUSE_REPORTING_NONE ), - mouse_focus_event( false ), mouse_alternate_scroll( false ), mouse_encoding_mode( MOUSE_ENCODING_DEFAULT ), - application_mode_cursor_keys( false ) + reverse_video( false ), bracketed_paste( false ), cursor_shape( -1 ), + mouse_reporting_mode( MOUSE_REPORTING_NONE ), mouse_focus_event( false ), mouse_alternate_scroll( false ), + mouse_encoding_mode( MOUSE_ENCODING_DEFAULT ), application_mode_cursor_keys( false ) { reinitialize_tabs( 0 ); } diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h index 9039d4e7f..4d798beb5 100644 --- a/src/terminal/terminalframebuffer.h +++ b/src/terminal/terminalframebuffer.h @@ -255,6 +255,8 @@ class SavedCursor class DrawState { private: + static const int cursor_shape_count = 7; /* 0-6 are valid cursor control code */ + int width, height; void new_grapheme( void ); @@ -282,6 +284,7 @@ class DrawState bool cursor_visible; bool reverse_video; bool bracketed_paste; + int cursor_shape; enum MouseReportingMode { @@ -317,6 +320,12 @@ class DrawState int get_combining_char_row( void ) const { return combining_char_row; } int get_width( void ) const { return width; } int get_height( void ) const { return height; } + void set_cursor_shape(int shape) + { + if ( shape >= 0 && shape < cursor_shape_count ) { + cursor_shape = shape; + } + } void set_tab( void ); void clear_tab( int col ); @@ -355,7 +364,7 @@ class DrawState && ( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) && ( bracketed_paste == x.bracketed_paste ) && ( mouse_reporting_mode == x.mouse_reporting_mode ) && ( mouse_focus_event == x.mouse_focus_event ) && ( mouse_alternate_scroll == x.mouse_alternate_scroll ) - && ( mouse_encoding_mode == x.mouse_encoding_mode ); + && ( mouse_encoding_mode == x.mouse_encoding_mode ) && (cursor_shape == x.cursor_shape); } }; diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc index 40c41afc4..35b30c7b0 100644 --- a/src/terminal/terminalfunctions.cc +++ b/src/terminal/terminalfunctions.cc @@ -134,6 +134,17 @@ static Function func_CSI_cursormove_D( CSI, "D", CSI_cursormove ); static Function func_CSI_cursormove_H( CSI, "H", CSI_cursormove ); static Function func_CSI_cursormove_f( CSI, "f", CSI_cursormove ); +/* cursor shape */ +static void CSI_cursorshape( Framebuffer* fb, Dispatcher* dispatch ) +{ + int shape = dispatch->getparam( 0, -1 ); + + fb->ds.set_cursor_shape(shape); +} + +static Function func_CSI_cursorshape( CSI, " q", CSI_cursorshape ); + + /* device attributes */ static void CSI_DA( Framebuffer* fb __attribute( ( unused ) ), Dispatcher* dispatch ) { diff --git a/src/tests/emulation-cursor-shape.test b/src/tests/emulation-cursor-shape.test new file mode 100755 index 000000000..f59f0e447 --- /dev/null +++ b/src/tests/emulation-cursor-shape.test @@ -0,0 +1,44 @@ +#!/bin/sh + +# +# This test exercises the cursor shape ascii sequence in Mosh. +# + +# shellcheck source=e2e-test-subrs +. "$(dirname "$0")/e2e-test-subrs" +PATH=$PATH:.:$srcdir +# Top-level wrapper. +if [ $# -eq 0 ]; then + e2e-test "$0" baseline direct verify + exit +fi + +# OK, we have arguments, we're one of the test hooks. +if [ $# -ne 1 ]; then + fail "bad arguments %s\n" "$@" +fi + +baseline() +{ + while read -r shape sleep_time text; do + printf '\n\033[%d q%s' "$shape" "$text" + sleep $sleep_time + done <