Cursor style switch on Linux virtual console

Author: xeorainCreated Sep 20, 2026Updated Sep 20, 2026

Problem

I have a ZLE widget as below for cursor styles between vicmd and viins keymap modes on Linux virtual console, but it doesn't work.

bash
if [[ "$KEYMAP" == 'vicmd' ]]; then
  # Normal mode
  print -n '\033[?17;0;240c'
else
  # Insert mode
  print -n '\033[?2c'
fi

Findings

After troubleshooting, I found the root cause is the following code in internal/p10k.zsh:

bash
typeset -gi _p9k__can_hide_cursor=$(( $+terminfo[civis] && $+terminfo[cnorm] ))
if (( _p9k__can_hide_cursor )); then
  # See https://github.com/romkatv/powerlevel10k/issues/1699
  if [[ $terminfo[cnorm] == *$'\e[?25h'(|'\e'*) ]]; then
    typeset -g _p9k__cnorm=$'\e[?25h'
  else
    typeset -g _p9k__cnorm=$terminfo[cnorm]
  fi
fi

The civis and cnorm terminfo capabilities for the Linux virtual console are:

bash
civis=$'\e[?25l\e[?1c'
cnorm=$'\e[?25h\e[?0c'

The value of cnorm fails to match *$'\e[?25h'(|'\e'*) ]], so p10k always picks the full cnorm value, including \e[?0c which resets the cursor to its default style. I am not sure \e in the pattern is a typo or intended as is.

After changing '\e' to $'\e', the cursor switch triggers, but I find another issue that the cursor always become invisible soon. This is because _p9k_reset_prompt executes:

bash
(( _p9k__can_hide_cursor )) && echoti civis

which sends $'\e[?25l\e[?1c'. The \e[?25l part hides the cursor, while \e[?1c selects the invisible cursor style. However, my temporary workaround has $'\e[?0c' stripped from cnorm, causing the subsequent cursor restore (print -rn -- $_p9k__cnorm) to only sends $'\e[?25h' which doesn't restore the cursor style.

Reference