From 0591f7c39e1d70fdfe6dfdad5b9d7fba56630f5f Mon Sep 17 00:00:00 2001 From: Evelyn Krieg Date: Sat, 18 Jul 2026 05:15:13 -0500 Subject: [PATCH] screen/vte: add cursor style support via DECSCUSR (CSI Ps SP q) Parse DECSCUSR escape sequences in vte and keep track of the cursor style in screen. --- src/tsm/libtsm-int.h | 36 +++++++++++++++++++----------------- src/tsm/libtsm.h | 13 +++++++++++++ src/tsm/libtsm.sym | 6 ++++++ src/tsm/tsm-screen.c | 18 ++++++++++++++++++ src/tsm/tsm-vte.c | 37 ++++++++++++++++++++++++------------- 5 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/tsm/libtsm-int.h b/src/tsm/libtsm-int.h index bc6c381..19cee5b 100644 --- a/src/tsm/libtsm-int.h +++ b/src/tsm/libtsm-int.h @@ -127,35 +127,37 @@ struct tsm_screen { struct tsm_screen_attr def_attr_main; /* ageing */ - tsm_age_t age_cnt; /* current age counter */ - unsigned int age_reset : 1; /* age-overflow flag */ + tsm_age_t age_cnt; /* current age counter */ + unsigned int age_reset : 1; /* age-overflow flag */ /* current buffer */ - unsigned int size_x; /* width of screen */ - unsigned int size_y; /* height of screen */ - unsigned int margin_top; /* top-margin index */ - unsigned int margin_bottom; /* bottom-margin index */ - unsigned int line_num; /* real number of allocated lines */ - struct line **lines; /* active lines; copy of main/alt */ - struct line **main_lines; /* real main lines */ - struct line **alt_lines; /* real alternative lines */ - tsm_age_t age; /* whole screen age */ + unsigned int size_x; /* width of screen */ + unsigned int size_y; /* height of screen */ + unsigned int margin_top; /* top-margin index */ + unsigned int margin_bottom; /* bottom-margin index */ + unsigned int line_num; /* real number of allocated lines */ + struct line **lines; /* active lines; copy of main/alt */ + struct line **main_lines; /* real main lines */ + struct line **alt_lines; /* real alternative lines */ + tsm_age_t age; /* whole screen age */ struct tsm_scrollback sb; /* cursor: positions are always in-bound, but cursor_x might be * bigger than size_x if new-line is pending */ - unsigned int cursor_x; /* current cursor x-pos */ - unsigned int cursor_y; /* current cursor y-pos */ + unsigned int cursor_x; /* current cursor x-pos */ + unsigned int cursor_y; /* current cursor y-pos */ + + enum tsm_screen_cursor_style cstyle; /* cursor shape */ /* tab ruler */ - bool *tab_ruler; /* tab-flag for all cells of one row */ + bool *tab_ruler; /* tab-flag for all cells of one row */ /* selection */ bool sel_active; - struct selection_pos sel_begin; /* First cell selected */ - struct selection_pos sel_start; /* First cell to copy in terminal order */ - struct selection_pos sel_end; /* Last cell to copy */ + struct selection_pos sel_begin; /* First cell selected */ + struct selection_pos sel_start; /* First cell to copy in terminal order */ + struct selection_pos sel_end; /* Last cell to copy */ /* draw2 interface */ struct tsm_screen_cell *cells; diff --git a/src/tsm/libtsm.h b/src/tsm/libtsm.h index 2c369b0..0894d29 100644 --- a/src/tsm/libtsm.h +++ b/src/tsm/libtsm.h @@ -198,6 +198,16 @@ struct tsm_screen_cell { tsm_screen_attr2_t attr2; /* glyph attributes */ }; +enum tsm_screen_cursor_style { + TSM_SCREEN_CURSOR_DEFAULT = 0, + TSM_SCREEN_CURSOR_BLOCK_BLINK = 1, + TSM_SCREEN_CURSOR_BLOCK_STEADY = 2, + TSM_SCREEN_CURSOR_UNDERLINE_BLINK = 3, + TSM_SCREEN_CURSOR_UNDERLINE_STEADY = 4, + TSM_SCREEN_CURSOR_VBAR_BLINK = 5, + TSM_SCREEN_CURSOR_VBAR_STEADY = 6, +}; + typedef int (*tsm_screen_draw_cb) (struct tsm_screen *con, uint64_t id, const uint32_t *ch, @@ -296,6 +306,9 @@ tsm_age_t tsm_screen_draw(struct tsm_screen *con, tsm_screen_draw_cb draw_cb, const struct tsm_screen_cell *tsm_screen_draw2(struct tsm_screen *con); +enum tsm_screen_cursor_style tsm_screen_get_cursor_style(struct tsm_screen *con); +void tsm_screen_set_cursor_style(struct tsm_screen *con, enum tsm_screen_cursor_style type); + /** @} */ /** diff --git a/src/tsm/libtsm.sym b/src/tsm/libtsm.sym index 021de6c..7869144 100644 --- a/src/tsm/libtsm.sym +++ b/src/tsm/libtsm.sym @@ -153,3 +153,9 @@ LIBTSM_4_6 { global: tsm_screen_draw2; } LIBTSM_4_5; + +LIBTSM_4_7 { +global: + tsm_screen_get_cursor_style; + tsm_screen_set_cursor_style; +} LIBTSM_4_6; diff --git a/src/tsm/tsm-screen.c b/src/tsm/tsm-screen.c index 7c17b34..5d508ea 100644 --- a/src/tsm/tsm-screen.c +++ b/src/tsm/tsm-screen.c @@ -1697,3 +1697,21 @@ void tsm_screen_erase_screen(struct tsm_screen *con, bool protect) screen_erase_region(con, 0, 0, con->size_x - 1, con->size_y - 1, protect); } + +SHL_EXPORT +enum tsm_screen_cursor_style tsm_screen_get_cursor_style(struct tsm_screen *con) +{ + if (!con) + return 0; + + return con->cstyle; +} + +SHL_EXPORT +void tsm_screen_set_cursor_style(struct tsm_screen *con, enum tsm_screen_cursor_style type) +{ + if (!con) + return; + + con->cstyle = type; +} diff --git a/src/tsm/tsm-vte.c b/src/tsm/tsm-vte.c index 764efb6..44ec5a0 100644 --- a/src/tsm/tsm-vte.c +++ b/src/tsm/tsm-vte.c @@ -143,6 +143,7 @@ struct vte_saved_state { struct tsm_screen_attr cattr; tsm_vte_charset **gl; tsm_vte_charset **gr; + enum tsm_screen_cursor_style cursor_style; bool wrap_mode; bool origin_mode; }; @@ -782,6 +783,7 @@ static void reset_state(struct tsm_vte *vte) vte->saved_state.wrap_mode = true; vte->saved_state.gl = &vte->g0; vte->saved_state.gr = &vte->g1; + vte->saved_state.cursor_style = TSM_SCREEN_CURSOR_DEFAULT; copy_fcolor(&vte->saved_state.cattr, &vte->def_attr); copy_bcolor(&vte->saved_state.cattr, &vte->def_attr); @@ -803,6 +805,7 @@ static void save_state(struct tsm_vte *vte) vte->saved_state.gr = vte->gr; vte->saved_state.wrap_mode = vte->flags & TSM_VTE_FLAG_AUTO_WRAP_MODE; vte->saved_state.origin_mode = vte->flags & TSM_VTE_FLAG_ORIGIN_MODE; + vte->saved_state.cursor_style = tsm_screen_get_cursor_style(vte->con); } static void restore_state(struct tsm_vte *vte) @@ -815,6 +818,7 @@ static void restore_state(struct tsm_vte *vte) tsm_screen_set_def_attr(vte->con, &vte->cattr); vte->gl = vte->saved_state.gl; vte->gr = vte->saved_state.gr; + tsm_screen_set_cursor_style(vte->con, vte->saved_state.cursor_style); if (vte->saved_state.wrap_mode) { vte->flags |= TSM_VTE_FLAG_AUTO_WRAP_MODE; @@ -2134,21 +2138,28 @@ static void do_csi(struct tsm_vte *vte, uint32_t data) num = vte->csi_argv[0]; tsm_screen_repeat_char(vte->con, num); break; - case 'q': /* DECLL - Load LEDs */ - num = vte->csi_argv[0]; - if (num <= 0) { - vte->led_state = 0; - } else if (num == 1) { - vte->led_state |= TSM_VTE_LED_SCROLL_LOCK; - } else if (num == 2) { - vte->led_state |= TSM_VTE_LED_NUM_LOCK; - } else if (num == 3) { - vte->led_state |= TSM_VTE_LED_CAPS_LOCK; + case 'q': /* DECLL - Load LEDs / DECSCUSR - Set Cursor Style */ + if (vte->csi_flags & CSI_SPACE) { + num = vte->csi_argv[0]; + if (num < 0) + num = 0; + tsm_screen_set_cursor_style(vte->con, num); } else { - break; + num = vte->csi_argv[0]; + if (num <= 0) { + vte->led_state = 0; + } else if (num == 1) { + vte->led_state |= TSM_VTE_LED_SCROLL_LOCK; + } else if (num == 2) { + vte->led_state |= TSM_VTE_LED_NUM_LOCK; + } else if (num == 3) { + vte->led_state |= TSM_VTE_LED_CAPS_LOCK; + } else { + break; + } + if (vte->led_cb) + vte->led_cb(vte, vte->led_state, vte->led_data); } - if (vte->led_cb) - vte->led_cb(vte, vte->led_state, vte->led_data); break; default: llog_debug(vte, "unhandled CSI sequence %c", data);