# HG changeset patch # User Christian Brabandt # Date 1502560805 -7200 # Node ID ca4931a20f8c0811c9bc8e21c2ba28abc41b5539 # Parent 488c6139c881334be48451c505588b7e07a25e6b patch 8.0.0918: cannot get terminal window cursor shape or attributes commit https://github.com/vim/vim/commit/3cd43ccccb03b2e68df9c8a344a87e51c007c656 Author: Bram Moolenaar Date: Sat Aug 12 19:51:41 2017 +0200 patch 8.0.0918: cannot get terminal window cursor shape or attributes Problem: Cannot get terminal window cursor shape or attributes. Solution: Support cursor shape, attributes and color. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -7939,13 +7939,19 @@ term_getattr({attr}, {what}) *term_ge term_getcursor({buf}) *term_getcursor()* Get the cursor position of terminal {buf}. Returns a list with - three numbers: [rows, cols, visible]. "rows" and "cols" are - one based, the first screen cell is row 1, column 1. - "visible" is one when the cursor is visible, zero when it is - hidden. - - This is the cursor position of the terminal itself, not of the - Vim window. + two numbers and a dictionary: [rows, cols, dict]. + + "rows" and "cols" are one based, the first screen cell is row + 1, column 1. This is the cursor position of the terminal + itself, not of the Vim window. + + "dict" can have these members: + "visible" one when the cursor is visible, zero when it + is hidden. + "blink" one when the cursor is visible, zero when it + is hidden. + "shape" 1 for a block cursor, 2 for underline and 3 + for a vertical bar. {buf} must be the buffer number of a terminal window. If the buffer does not exist or is not a terminal window, an empty @@ -8035,7 +8041,7 @@ term_scrape({buf}, {row}) *term_scrap "fg" foreground color as #rrggbb "bg" background color as #rrggbb "attr" attributes of the cell, use |term_getattr()| - to get the individual flags + to get the individual flags "width" cell width: 1 or 2 {only available when compiled with the |+terminal| feature} @@ -8075,7 +8081,7 @@ term_start({cmd}, {options}) *term_st "term_rows" vertical size to use for the terminal, instead of using 'termsize' "term_cols" horizontal size to use for the terminal, - instead of using 'termsize' + instead of using 'termsize' "vertical" split the window vertically "curwin" use the current window, do not split the window; fails if the current buffer @@ -8165,7 +8171,7 @@ test_override({name}, {val}) *test_ov in a way that the test doesn't work properly. When using: > call test_override('starting', 1) -< The value of "starting" is saved. It is restored by: > +< The value of "starting" is saved. It is restored by: > call test_override('starting', 0) test_settime({expr}) *test_settime()* diff --git a/src/feature.h b/src/feature.h --- a/src/feature.h +++ b/src/feature.h @@ -1273,6 +1273,9 @@ #if !defined(FEAT_JOB_CHANNEL) && defined(FEAT_TERMINAL) # undef FEAT_TERMINAL #endif +#if defined(FEAT_TERMINAL) && !defined(CURSOR_SHAPE) +# define CURSOR_SHAPE +#endif /* * +signs Allow signs to be displayed to the left of text lines. diff --git a/src/libvterm/include/vterm.h b/src/libvterm/include/vterm.h --- a/src/libvterm/include/vterm.h +++ b/src/libvterm/include/vterm.h @@ -120,7 +120,8 @@ typedef enum { VTERM_PROP_ICONNAME, /* string */ VTERM_PROP_REVERSE, /* bool */ VTERM_PROP_CURSORSHAPE, /* number */ - VTERM_PROP_MOUSE /* number */ + VTERM_PROP_MOUSE, /* number */ + VTERM_PROP_CURSORCOLOR /* string */ } VTermProp; enum { diff --git a/src/libvterm/src/state.c b/src/libvterm/src/state.c --- a/src/libvterm/src/state.c +++ b/src/libvterm/src/state.c @@ -1504,6 +1504,10 @@ static int on_osc(const char *command, s settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2); return 1; } + else if(strneq(command, "12;", 3)) { + settermprop_string(state, VTERM_PROP_CURSORCOLOR, command + 3, cmdlen - 3); + return 1; + } else if(state->fallbacks && state->fallbacks->osc) if((*state->fallbacks->osc)(command, cmdlen, state->fbdata)) return 1; @@ -1819,6 +1823,7 @@ int vterm_state_set_termprop(VTermState switch(prop) { case VTERM_PROP_TITLE: case VTERM_PROP_ICONNAME: + case VTERM_PROP_CURSORCOLOR: /* we don't store these, just transparently pass through */ return 1; case VTERM_PROP_CURSORVISIBLE: diff --git a/src/libvterm/src/vterm.c b/src/libvterm/src/vterm.c --- a/src/libvterm/src/vterm.c +++ b/src/libvterm/src/vterm.c @@ -294,6 +294,7 @@ VTermValueType vterm_get_prop_type(VTerm case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL; case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT; case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT; + case VTERM_PROP_CURSORCOLOR: return VTERM_VALUETYPE_STRING; } return 0; /* UNREACHABLE */ } diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -3162,6 +3162,9 @@ static struct vimoption options[] = p_term("t_EI", T_CEI) p_term("t_fs", T_FS) p_term("t_IE", T_CIE) + p_term("t_SC", T_CSC) + p_term("t_EC", T_CEC) + p_term("t_SH", T_CSH) p_term("t_IS", T_CIS) p_term("t_ke", T_KE) p_term("t_ks", T_KS) diff --git a/src/proto/term.pro b/src/proto/term.pro --- a/src/proto/term.pro +++ b/src/proto/term.pro @@ -51,7 +51,10 @@ int mouse_model_popup(void); void scroll_start(void); void cursor_on(void); void cursor_off(void); -void term_cursor_shape(void); +void term_cursor_mode(int forced); +void term_cursor_color(char_u *color); +void term_cursor_blink(int blink); +void term_cursor_shape(int shape, int blink); void scroll_region_set(win_T *wp, int off); void scroll_region_reset(void); void clear_termcodes(void); diff --git a/src/proto/ui.pro b/src/proto/ui.pro --- a/src/proto/ui.pro +++ b/src/proto/ui.pro @@ -47,6 +47,7 @@ void trash_input_buf(void); int read_from_input_buf(char_u *buf, long maxlen); void fill_input_buf(int exit_on_error); void read_error_exit(void); +void ui_cursor_shape_forced(int forced); void ui_cursor_shape(void); int check_col(int col); int check_row(int row); diff --git a/src/term.c b/src/term.c --- a/src/term.c +++ b/src/term.c @@ -817,6 +817,14 @@ static struct builtin_term builtin_termc {(int)KS_MS, "y"}, {(int)KS_UT, "y"}, {(int)KS_LE, "\b"}, + {(int)KS_VI, IF_EB("\033[?25l", ESC_STR "[?25l")}, + {(int)KS_VE, IF_EB("\033[?25h", ESC_STR "[?25h")}, + {(int)KS_VS, IF_EB("\033[?12h", ESC_STR "[?12h")}, +# ifdef TERMINFO + {(int)KS_CSH, IF_EB("\033[%p1%d q", ESC_STR "[%p1%d q")}, +# else + {(int)KS_CSH, IF_EB("\033[%d q", ESC_STR "[%d q")}, +# endif # ifdef TERMINFO {(int)KS_CM, IF_EB("\033[%i%p1%d;%p2%dH", ESC_STR "[%i%p1%d;%p2%dH")}, @@ -840,6 +848,8 @@ static struct builtin_term builtin_termc {(int)KS_CIE, "\007"}, {(int)KS_TS, IF_EB("\033]2;", ESC_STR "]2;")}, {(int)KS_FS, "\007"}, + {(int)KS_CSC, IF_EB("\033]12;", ESC_STR "]12;")}, + {(int)KS_CEC, "\007"}, # ifdef TERMINFO {(int)KS_CWS, IF_EB("\033[8;%p1%d;%p2%dt", ESC_STR "[8;%p1%d;%p2%dt")}, @@ -1142,6 +1152,8 @@ static struct builtin_term builtin_termc {(int)KS_TE, "[TE]"}, {(int)KS_CIS, "[CIS]"}, {(int)KS_CIE, "[CIE]"}, + {(int)KS_CSC, "[CSC]"}, + {(int)KS_CEC, "[CEC]"}, {(int)KS_TS, "[TS]"}, {(int)KS_FS, "[FS]"}, # ifdef TERMINFO @@ -1569,6 +1581,7 @@ set_termname(char_u *term) {KS_CAB,"AB"}, {KS_CAF,"AF"}, {KS_LE, "le"}, {KS_ND, "nd"}, {KS_OP, "op"}, {KS_CRV, "RV"}, {KS_CIS, "IS"}, {KS_CIE, "IE"}, + {KS_CSC, "SC"}, {KS_CEC, "EC"}, {KS_TS, "ts"}, {KS_FS, "fs"}, {KS_CWP, "WP"}, {KS_CWS, "WS"}, {KS_CSI, "SI"}, {KS_CEI, "EI"}, @@ -2283,8 +2296,8 @@ term_is_8bit(char_u *name) /* * Translate terminal control chars from 7-bit to 8-bit: - * [ -> CSI - * ] -> + * [ -> CSI + * ] -> OSC * O -> */ static int @@ -3655,7 +3668,7 @@ cursor_off(void) * Set cursor shape to match Insert or Replace mode. */ void -term_cursor_shape(void) +term_cursor_mode(int forced) { static int showing_mode = NORMAL; char_u *p; @@ -3667,7 +3680,7 @@ term_cursor_shape(void) if ((State & REPLACE) == REPLACE) { - if (showing_mode != REPLACE) + if (forced || showing_mode != REPLACE) { if (*T_CSR != NUL) p = T_CSR; /* Replace mode cursor */ @@ -3682,18 +3695,55 @@ term_cursor_shape(void) } else if (State & INSERT) { - if (showing_mode != INSERT && *T_CSI != NUL) + if ((forced || showing_mode != INSERT) && *T_CSI != NUL) { out_str(T_CSI); /* Insert mode cursor */ showing_mode = INSERT; } } - else if (showing_mode != NORMAL) + else if (forced || showing_mode != NORMAL) { out_str(T_CEI); /* non-Insert mode cursor */ showing_mode = NORMAL; } } + +# if defined(FEAT_TERMINAL) || defined(PROTO) + void +term_cursor_color(char_u *color) +{ + if (*T_CSC != NUL) + { + out_str(T_CSC); /* set cursor color start */ + out_str_nf(color); + out_str(T_CEC); /* set cursor color end */ + out_flush(); + } +} + + void +term_cursor_blink(int blink) +{ + if (blink) + out_str(T_VS); + else + out_str(T_VE); + out_flush(); +} + +/* + * "shape" == 1: block, "shape" == 2: underline, "shape" == 3: vertical bar + */ + void +term_cursor_shape(int shape, int blink) +{ + if (*T_CSH != NUL) + { + OUT_STR(tgoto((char *)T_CSH, 0, shape * 2 - blink)); + out_flush(); + } +} +# endif #endif /* diff --git a/src/term.h b/src/term.h --- a/src/term.h +++ b/src/term.h @@ -40,6 +40,7 @@ enum SpecialKey KS_VI, /* cursor invisible */ KS_VE, /* cursor visible */ KS_VS, /* cursor very visible */ + KS_CSH, /* cursor shape */ KS_ME, /* normal mode */ KS_MR, /* reverse mode */ KS_MD, /* bold mode */ @@ -74,6 +75,8 @@ enum SpecialKey KS_ND, /* cursor right */ KS_CIS, /* set icon text start */ KS_CIE, /* set icon text end */ + KS_CSC, /* set cursor color start */ + KS_CEC, /* set cursor color end */ KS_TS, /* set window title start (to status line)*/ KS_FS, /* set window title end (from status line) */ KS_CWP, /* set window position in pixels */ @@ -128,6 +131,7 @@ extern char_u *(term_strings[]); /* c #define T_VI (TERM_STR(KS_VI)) /* cursor invisible */ #define T_VE (TERM_STR(KS_VE)) /* cursor visible */ #define T_VS (TERM_STR(KS_VS)) /* cursor very visible */ +#define T_CSH (TERM_STR(KS_CSH)) /* cursor shape */ #define T_ME (TERM_STR(KS_ME)) /* normal mode */ #define T_MR (TERM_STR(KS_MR)) /* reverse mode */ #define T_MD (TERM_STR(KS_MD)) /* bold mode */ @@ -164,6 +168,8 @@ extern char_u *(term_strings[]); /* c #define T_CIE (TERM_STR(KS_CIE)) /* set icon text end */ #define T_TS (TERM_STR(KS_TS)) /* set window title start */ #define T_FS (TERM_STR(KS_FS)) /* set window title end */ +#define T_CSC (TERM_STR(KS_CSC)) /* set cursor color start */ +#define T_CEC (TERM_STR(KS_CEC)) /* set cursor color end */ #define T_CWP (TERM_STR(KS_CWP)) /* set window position */ #define T_CGP (TERM_STR(KS_CGP)) /* get window position */ #define T_CWS (TERM_STR(KS_CWS)) /* window size */ diff --git a/src/terminal.c b/src/terminal.c --- a/src/terminal.c +++ b/src/terminal.c @@ -36,9 +36,7 @@ * that buffer, attributes come from the scrollback buffer tl_scrollback. * * TODO: - * - support different cursor shapes, colors and attributes - * - make term_getcursor() return type (none/block/bar/underline) and - * attributes (color, blink, etc.) + * - cursor shape/color/blink in the GUI * - Make argument list work on MS-Windows. #1954 * - MS-Windows: no redraw for 'updatetime' #1915 * - To set BS correctly, check get_stty(); Pass the fd of the pty. @@ -143,6 +141,9 @@ struct terminal_S { VTermPos tl_cursor_pos; int tl_cursor_visible; + int tl_cursor_blink; + int tl_cursor_shape; /* 1: block, 2: underline, 3: bar */ + char_u *tl_cursor_color; /* NULL or allocated */ int tl_using_altscreen; }; @@ -155,6 +156,8 @@ struct terminal_S { */ static term_T *first_term = NULL; +/* Terminal active in terminal_loop(). */ +static term_T *in_terminal_loop = NULL; #define MAX_ROW 999999 /* used for tl_dirty_row_end to update all rows */ #define KEY_BUF_LEN 200 @@ -256,6 +259,7 @@ term_start(char_u *cmd, jobopt_T *opt, i return; term->tl_dirty_row_end = MAX_ROW; term->tl_cursor_visible = TRUE; + term->tl_cursor_shape = VTERM_PROP_CURSORSHAPE_BLOCK; term->tl_finish = opt->jo_term_finish; ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300); @@ -517,6 +521,7 @@ free_terminal(buf_T *buf) vim_free(term->tl_title); vim_free(term->tl_status_text); vim_free(term->tl_opencmd); + vim_free(term->tl_cursor_color); vim_free(term); buf->b_term = NULL; } @@ -1158,6 +1163,35 @@ term_paste_register(int prev_c UNUSED) } } +static int did_change_cursor = FALSE; + + static void +may_set_cursor_props(term_T *term) +{ + if (in_terminal_loop == term) + { + if (term->tl_cursor_color != NULL) + term_cursor_color(term->tl_cursor_color); + else + term_cursor_color((char_u *)""); + /* do both blink and shape+blink, in case setting shape does not work */ + term_cursor_blink(term->tl_cursor_blink); + term_cursor_shape(term->tl_cursor_shape, term->tl_cursor_blink); + } +} + + static void +may_restore_cursor_props(void) +{ + if (did_change_cursor) + { + did_change_cursor = FALSE; + ui_cursor_shape_forced(TRUE); + term_cursor_color((char_u *)""); + term_cursor_blink(FALSE); + } +} + /* * Returns TRUE if the current window contains a terminal and we are sending * keys to the job. @@ -1185,10 +1219,14 @@ terminal_loop(void) { int c; int termkey = 0; + int ret; + + in_terminal_loop = curbuf->b_term; if (*curwin->w_p_tk != NUL) termkey = string_to_key(curwin->w_p_tk, TRUE); position_cursor(curwin, &curbuf->b_term->tl_cursor_pos); + may_set_cursor_props(curbuf->b_term); for (;;) { @@ -1235,7 +1273,8 @@ terminal_loop(void) { /* CTRL-\ CTRL-N : go to Terminal-Normal mode. */ term_enter_normal_mode(); - return FAIL; + ret = FAIL; + goto theend; } /* Send both keys to the terminal. */ send_keys_to_term(curbuf->b_term, prev_c, TRUE); @@ -1249,7 +1288,8 @@ terminal_loop(void) { /* CTRL-W N : go to Terminal-Normal mode. */ term_enter_normal_mode(); - return FAIL; + ret = FAIL; + goto theend; } else if (c == '"') { @@ -1260,13 +1300,22 @@ terminal_loop(void) { stuffcharReadbuff(Ctrl_W); stuffcharReadbuff(c); - return OK; + ret = OK; + goto theend; } } if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK) - return OK; + { + ret = OK; + goto theend; + } } - return FAIL; + ret = FAIL; + +theend: + in_terminal_loop = NULL; + may_restore_cursor_props(); + return ret; } /* @@ -1303,7 +1352,7 @@ term_job_ended(job_T *job) static void may_toggle_cursor(term_T *term) { - if (curbuf == term->tl_buffer) + if (in_terminal_loop == term) { if (term->tl_cursor_visible) cursor_on(); @@ -1385,6 +1434,22 @@ handle_settermprop( out_flush(); break; + case VTERM_PROP_CURSORBLINK: + term->tl_cursor_blink = value->boolean; + may_set_cursor_props(term); + break; + + case VTERM_PROP_CURSORSHAPE: + term->tl_cursor_shape = value->number; + may_set_cursor_props(term); + break; + + case VTERM_PROP_CURSORCOLOR: + vim_free(term->tl_cursor_color); + term->tl_cursor_color = vim_strsave((char_u *)value->string); + may_set_cursor_props(term); + break; + case VTERM_PROP_ALTSCREEN: /* TODO: do anything else? */ term->tl_using_altscreen = value->boolean; @@ -2076,17 +2141,30 @@ f_term_getattr(typval_T *argvars, typval f_term_getcursor(typval_T *argvars, typval_T *rettv) { buf_T *buf = term_get_buf(argvars); + term_T *term; list_T *l; + dict_T *d; if (rettv_list_alloc(rettv) == FAIL) return; if (buf == NULL) return; + term = buf->b_term; l = rettv->vval.v_list; - list_append_number(l, buf->b_term->tl_cursor_pos.row + 1); - list_append_number(l, buf->b_term->tl_cursor_pos.col + 1); - list_append_number(l, buf->b_term->tl_cursor_visible); + list_append_number(l, term->tl_cursor_pos.row + 1); + list_append_number(l, term->tl_cursor_pos.col + 1); + + d = dict_alloc(); + if (d != NULL) + { + dict_add_nr_str(d, "visible", term->tl_cursor_visible, NULL); + dict_add_nr_str(d, "blink", term->tl_cursor_blink, NULL); + dict_add_nr_str(d, "shape", term->tl_cursor_shape, NULL); + dict_add_nr_str(d, "color", 0L, term->tl_cursor_color == NULL + ? (char_u *)"" : term->tl_cursor_color); + list_append_dict(l, d); + } } /* diff --git a/src/ui.c b/src/ui.c --- a/src/ui.c +++ b/src/ui.c @@ -1942,14 +1942,14 @@ read_error_exit(void) * May update the shape of the cursor. */ void -ui_cursor_shape(void) +ui_cursor_shape_forced(int forced) { # ifdef FEAT_GUI if (gui.in_use) gui_update_cursor_later(); else # endif - term_cursor_shape(); + term_cursor_mode(forced); # ifdef MCH_CURSOR_SHAPE mch_update_cursor(); @@ -1959,6 +1959,12 @@ ui_cursor_shape(void) conceal_check_cursur_line(); # endif } + + void +ui_cursor_shape(void) +{ + ui_cursor_shape_forced(FALSE); +} #endif #if defined(FEAT_CLIPBOARD) || defined(FEAT_GUI) || defined(FEAT_RIGHTLEFT) \ diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -770,6 +770,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 918, +/**/ 917, /**/ 916,