diff --git a/user/apars_csi.c b/user/apars_csi.c index 193b79b..fc2c3e1 100644 --- a/user/apars_csi.c +++ b/user/apars_csi.c @@ -112,9 +112,9 @@ apars_handle_csi(char leadchar, const int *params, int count, char interchar, ch // switch_csi_NoLeadInterDollar(opts); // break; -// case ' ': -// switch_csi_NoLeadInterSpace(opts); -// break; + case ' ': + switch_csi_NoLeadInterSpace(opts); + break; // case ',': // switch_csi_NoLeadInterComma(opts); @@ -409,6 +409,36 @@ switch_csi_NoLeadInterBang(CSI_Data *opts) } +/** + * CSI none Pm SP key + */ +static inline void ICACHE_FLASH_ATTR +switch_csi_NoLeadInterSpace(CSI_Data *opts) +{ + int n; + switch(opts->key) { + case 'q': + // DECSCUSR + // CSI Ps SP q + // Set cursor style (DECSCUSR, VT520). + // Ps = 0 -> blinking block. + // Ps = 1 -> blinking block (default). + // Ps = 2 -> steady block. + // Ps = 3 -> blinking underline. + // Ps = 4 -> steady underline. + // Ps = 5 -> blinking bar (xterm). + // Ps = 6 -> steady bar (xterm). + n = opts->n[0]; + if (n > 6) n = 1; // use default if bad value set + screen_cursor_shape((enum CursorShape) n); + break; + + default: + warn_bad_csi(); + } +} + + /** * CSI > Pm inter key */ @@ -649,8 +679,10 @@ do_csi_set_private_option(CSI_Data *opts) mouse_tracking.focus_tracking); } else if (n == 12) { - // TODO Cursor blink on/off - ansi_noimpl("Cursor blink toggle"); + screen_cursor_blink(yn); + } + else if (n == 25) { + screen_set_cursor_visible(yn); } else if (n == 40) { // allow/disallow 80->132 mode @@ -659,7 +691,7 @@ do_csi_set_private_option(CSI_Data *opts) } else if (n == 45) { // reverse wrap-around - ansi_noimpl("Reverse Wraparound"); + screen_reverse_wrap_enable(yn); } else if (n == 69) { // horizontal margins @@ -691,9 +723,6 @@ do_csi_set_private_option(CSI_Data *opts) // Bracketed paste mode ansi_noimpl("Bracketed paste"); } - else if (n == 25) { - screen_set_cursor_visible(yn); - } else if (n == 800) { // ESPTerm: Toggle display of buttons termconf_scratch.show_buttons = yn; screen_notifyChange(CHANGE_CONTENT); // this info is included in the screen preamble diff --git a/user/screen.c b/user/screen.c index 9cf94ee..965132e 100644 --- a/user/screen.c +++ b/user/screen.c @@ -49,11 +49,12 @@ static struct { bool numpad_alt_mode; //!< DECNKM - Numpad Application Mode bool cursors_alt_mode; //!< DECCKM - Cursors Application mode - bool newline_mode; //!< LNM - CR automatically sends LF - bool reverse; //!< DECSCNM - Reverse video + bool newline_mode; //!< LNM - CR automatically sends LF + bool reverse; //!< DECSCNM - Reverse video bool insert_mode; //!< IRM - Insert mode (move rest of the line to the right) bool cursor_visible; //!< DECTCEM - Cursor visible + enum CursorShape cursor_shape; // Vertical margin bounds (inclusive start/end of scrolling region) int vm0; @@ -90,6 +91,7 @@ typedef struct { /** Options saved with cursor */ bool auto_wrap; //!< DECAWM - Wrapping when EOL + bool reverse_wraparound; //!< Reverse-wraparound Mode. DECSET 45 bool origin_mode; //!< DECOM - absolute positioning is relative to vertical margins } CursorTypeDef; @@ -165,6 +167,7 @@ terminal_restore_defaults(void) termconf->loopback = 0; termconf->show_buttons = 1; termconf->show_config_links = 1; + termconf->def_cursor_shape = CURSOR_BLOCK_BL; } /** @@ -208,6 +211,13 @@ terminal_apply_settings_noclear(void) changed = 1; } + // Migrate to v3 + if (termconf->config_version < 4) { + dbg("termconf: Updating to version 4"); + termconf->def_cursor_shape = CURSOR_BLOCK_BL; + changed = 1; + } + termconf->config_version = TERMCONF_VERSION; // Validation... @@ -268,30 +278,6 @@ cursor_reset(void) screen_reset_sgr(); } -/** - * Reset the cursor position & colors - */ -static void ICACHE_FLASH_ATTR -decopt_reset(void) -{ - scr.cursor_visible = true; - scr.insert_mode = false; - cursor.origin_mode = false; - cursor.auto_wrap = true; -} - -/** - * Reset the cursor (\e[0m) - */ -void ICACHE_FLASH_ATTR -screen_reset_sgr(void) -{ - cursor.fg = termconf_scratch.default_fg; - cursor.bg = termconf_scratch.default_bg; - cursor.attrs = 0; - cursor.inverse = false; -} - /** * Reset the screen - called by ESC c */ @@ -314,6 +300,18 @@ screen_reset_on_resize(void) NOTIFY_DONE(); } +/** + * Reset the cursor (\e[0m) + */ +void ICACHE_FLASH_ATTR +screen_reset_sgr(void) +{ + cursor.fg = termconf_scratch.default_fg; + cursor.bg = termconf_scratch.default_bg; + cursor.attrs = 0; + cursor.inverse = false; +} + /** * Reset the screen - called by ESC c */ @@ -324,7 +322,14 @@ screen_reset(void) NOTIFY_LOCK(); cursor_reset(); - decopt_reset(); + + // DECopts + scr.cursor_visible = true; + scr.insert_mode = false; + cursor.origin_mode = false; + cursor.auto_wrap = true; + cursor.reverse_wraparound = false; + scr.cursor_shape = termconf->def_cursor_shape; scr.numpad_alt_mode = false; scr.cursors_alt_mode = false; @@ -838,6 +843,33 @@ screen_set_scrolling_region(int from, int to) //region --- Cursor manipulation --- +/** set cursor type */ +void ICACHE_FLASH_ATTR +screen_cursor_shape(enum CursorShape shape) +{ + NOTIFY_LOCK(); + if (shape == CURSOR_DEFAULT) shape = termconf->def_cursor_shape; + scr.cursor_shape = shape; + NOTIFY_DONE(); +} + +/** set cursor blink option */ +void ICACHE_FLASH_ATTR +screen_cursor_blink(bool blink) +{ + NOTIFY_LOCK(); + if (blink) { + if (scr.cursor_shape == CURSOR_BLOCK) scr.cursor_shape = CURSOR_BLOCK_BL; + if (scr.cursor_shape == CURSOR_BAR) scr.cursor_shape = CURSOR_BAR_BL; + if (scr.cursor_shape == CURSOR_UNDERLINE) scr.cursor_shape = CURSOR_UNDERLINE_BL; + } else { + if (scr.cursor_shape == CURSOR_BLOCK_BL) scr.cursor_shape = CURSOR_BLOCK; + if (scr.cursor_shape == CURSOR_BAR_BL) scr.cursor_shape = CURSOR_BAR; + if (scr.cursor_shape == CURSOR_UNDERLINE_BL) scr.cursor_shape = CURSOR_UNDERLINE; + } + NOTIFY_DONE(); +} + /** * Set cursor position */ @@ -921,7 +953,34 @@ screen_cursor_move(int dy, int dx, bool scroll) cursor.x += dx; cursor.y += dy; if (cursor.x >= (int)W) cursor.x = W - 1; - if (cursor.x < (int)0) cursor.x = 0; + if (cursor.x < (int)0) { + if (cursor.auto_wrap && cursor.reverse_wraparound) { + // this is mimicking a behavior from xterm that allows any number of steps backwards with reverse wraparound enabled + int steps = -cursor.x; + if(steps > 1000) steps = 1; // avoid something stupid causing infinite loop here + for(;steps>0;steps--) { + if (cursor.x > 0) { + // backspace should go to col 79 if "hanging" after 80 (as if it never actually left the 80th col) + cursor.hanging = false; + cursor.x--; + } + else { + if (cursor.y > 0) { + // end of previous line + cursor.y--; + cursor.x = W - 1; + } + else { + // end of screen, end of line (wrap around) + cursor.y = R1; + cursor.x = W - 1; + } + } + } + } else { + cursor.x = 0; + } + } if (cursor.y < R0) { if (was_inside) { @@ -1030,6 +1089,15 @@ screen_wrap_enable(bool enable) cursor.auto_wrap = enable; } +/** + * Enable reverse wraparound + */ +void ICACHE_FLASH_ATTR +screen_reverse_wrap_enable(bool enable) +{ + cursor.reverse_wraparound = enable; +} + /** * Set cursor foreground color */ @@ -1170,11 +1238,7 @@ screen_putchar(const char *ch) goto done; case BS: - if (cursor.x > 0) { - // backspace should go to col 79 if "hanging" after 80 (as if it never actually left the 80th col) - cursor.hanging = false; - cursor.x--; - } + screen_cursor_move(0, -1, false); // we should not wrap around, and backspace should not even clear the cell (verified in xterm) goto done; @@ -1402,17 +1466,18 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data) bufput_2B(W); bufput_2B(cursor.y); bufput_2B(cursor.x); - bufput_2B(( - (scr.cursor_visible ? 1<<0 : 0) | - (cursor.hanging ? 1<<1 : 0) | - (scr.cursors_alt_mode ? 1<<2 : 0) | - (scr.numpad_alt_mode ? 1<<3 : 0) | - (termconf->fn_alt_mode ? 1<<4 : 0) | - ((mouse_tracking.mode>MTM_NONE) ? 1<<5 : 0) | // disables context menu - ((mouse_tracking.mode>=MTM_NORMAL) ? 1<<6 : 0) | // disables selecting - (termconf_scratch.show_buttons ? 1<<7 : 0) | - (termconf_scratch.show_config_links ? 1<<8 : 0) - )); + bufput_2B( + (scr.cursor_visible ? 1<<0 : 0) | + (cursor.hanging ? 1<<1 : 0) | + (scr.cursors_alt_mode ? 1<<2 : 0) | + (scr.numpad_alt_mode ? 1<<3 : 0) | + (termconf->fn_alt_mode ? 1<<4 : 0) | + ((mouse_tracking.mode>MTM_NONE) ? 1<<5 : 0) | // disables context menu + ((mouse_tracking.mode>=MTM_NORMAL) ? 1<<6 : 0) | // disables selecting + (termconf_scratch.show_buttons ? 1<<7 : 0) | + (termconf_scratch.show_config_links ? 1<<8 : 0) | + ((scr.cursor_shape&0x03)<<9) // 9,10,11 - cursor shape based on DECSCUSR + ); } int i = ss->index; diff --git a/user/screen.h b/user/screen.h index d89d10a..9c98f57 100644 --- a/user/screen.h +++ b/user/screen.h @@ -54,10 +54,20 @@ /** Maximum screen size (determines size of the static data array) */ #define MAX_SCREEN_SIZE (80*25) -#define TERMCONF_VERSION 3 +#define TERMCONF_VERSION 4 // --- Persistent Settings --- +enum CursorShape { + CURSOR_BLOCK_BL = 0, + CURSOR_DEFAULT = 1, // this is translated to a user configured style + CURSOR_BLOCK = 2, + CURSOR_UNDERLINE_BL = 3, + CURSOR_UNDERLINE = 4, + CURSOR_BAR_BL = 5, + CURSOR_BAR = 6, +}; + typedef struct { u32 width; u32 height; @@ -75,6 +85,7 @@ typedef struct { bool show_buttons; bool show_config_links; char btn_msg[TERM_BTN_COUNT][TERM_BTN_MSG_LEN]; + enum CursorShape def_cursor_shape; } TerminalConfigBundle; // Live config @@ -173,6 +184,10 @@ void screen_scroll_down(unsigned int lines); // --- Cursor control --- +/** Set cursor shape */ +void screen_cursor_shape(enum CursorShape shape); +/** Toggle cursor blink (modifies shape) */ +void screen_cursor_blink(bool blink); /** Set cursor position */ void screen_cursor_set(int y, int x); /** Read cursor pos to given vars */ @@ -196,6 +211,8 @@ void screen_set_insert_mode(bool insert); void screen_set_newline_mode(bool nlm); /** Enable auto wrap */ void screen_wrap_enable(bool enable); +/** Enable reverse wrap-around */ +void screen_reverse_wrap_enable(bool enable); /** Set scrolling region */ void screen_set_scrolling_region(int from, int to); /** Enable or disable origin remap to top left of scrolling region */