Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

[tip] mouse and mouse-wheel support!



Ok, thanks to the help of Peter, Bart and Andy, here is a new
version.

Improvements:

- it handles the multi-line buffers correctly (except in minor
  corner cases I didn't bother to fix)
- the prompt handling has been improved
- now there are two mouse modes:
   o the first one, when ZLE_USE_MOUSE is set to 1, that uses
     xterm mouse tracking (works with rxvt, gnome-terminal,
     xterm but doesn't support mouse wheel)
   o a new one that will probably work only with xterm (note
     that (XFree86) xterm has 256 color, UTF8 support, a better
     mouse support, a better font display... than rxvt or
     gnome-terminal so why bother with other terminals).
     You need to modify your resource file (~/.Xdefaults-host or
     ~/.Xdefaults or $XENVIRONMENT or $XAPP_RESDIR or
     $XUSERFILESEARCHPATH..., see your man page for X) as
     described below.

Note that the mouse wheel behavior is not connected to those
functions, it's just that the translation table tells Xterm to
send ^N and ^P characters on mouse wheel events (with modifier
Mod4).

So, it should work with every application that recognizes those
keys as <Down> and <Up>, not only zsh (readline (bash, gdb,
rc...), emacs, vim...).
     
### code begin
# Add that to your X resource file to have wheel-mouse support
# and <Mod4-Button> when ZLE_USE_MOUSE is off under xterm
# (remove the #)
#!!!BEGIN
#XTerm.VT100.translations:             #override\
#        Mod4 <Btn1Down>,<Btn1Up>: string(0x1b) string("[M ") dired-button()\n\
#        Mod4 <Btn2Down>,<Btn2Up>: string(0x1b) string("[M!") dired-button()\n\
#        Mod4 <Btn3Down>,<Btn3Up>: string(0x1b) string("[M") string(0x22) dired-button()\n\
#        Mod4 <Btn5Down>,<Btn5Up>: string(0xe)\n\
#        Mod4 <Btn4Down>,<Btn4Up>: string(0x10)
#!!!END
# note that you need to hold whatever key puts you in the "Mod4"
# (for me, it's the MS Windows keys aka "Super", see xmodmap -pm
# for details)

if [[ $TERM = *xterm* || $TERM = *rxvt* ]]; then
  zmodload -i zsh/parameter

  set-status() { return $1; }

  zle-xterm-mouse() {
    local last_status=$?
    emulate -L zsh
    setopt extendedglob # for (#b)
    local bt mx my cy i buf

    read -k bt # mouse button, x, y reported after \e[M
    bt=$((#bt & 7))
    read -k mx
    read -k my
    if [[ $mx = $'\030' ]]; then
      # assume btns were mapped to \E[M<btn>dired-button()(^X\EG<x><y>)
      read -k mx
      read -k mx
      read -k my
      (( my = #my - 31 ))
      (( mx = #mx - 31 ))
      ZLE_MOUSE_BUTTON=$bt
    else
      (( my = #my - 32 ))
      (( mx = #mx - 32 ))

      if [[ $bt != 3 ]]; then
        # Process on release, but record the button on press.
        ZLE_MOUSE_BUTTON=$bt
        return 0
      fi
    fi

    print -n '\e[6n' # query cursor position
    while read -k i && [[ $i != R ]]; do buf+=$i; done

    local match mbegin mend
    [[ $buf = (#b)??(*)\;* ]] || return
    cy=$match[1]
    # we don't need cx

    local cur_prompt
    if [[ -n $PREBUFFER ]]; then
      cur_prompt=$PS2
      # decide wether we're at the PS2 or PS1 prompt
    else
      cur_prompt=$PS1
    fi

    [[ -o promptsubst ]] && cur_prompt=${${(e)cur_prompt}//(#b)([\\\$\`])/\\$match}

    # restore the exit status in case $PS<n> relies on it
    set-status $last_status
    cur_prompt=${(S%%)cur_prompt//(#b)(%([BSUbsu]|{*%})|(%[^BSUbsu{}]))/$match[3]}

    local -a pos # array holding the possible positions of
                 # the mouse pointer
    local -i x=0 y=1 cursor=$((${#cur_prompt}+$CURSOR+1))
    local Y
    buf=$cur_prompt$BUFFER
    for ((i=1; i<=$#buf; i++)); do
      (( i == cursor )) && Y=$y
      case $buf[i] in
        ($'\n') # newline
          : ${pos[y]=$i}
          (( y++, x=0 ));;
        ($'\t') # tab advance til next tab stop
          (( x = x/8*8+8 ));;
        ([$'\0'-$'\037'$'\0200'-$'\0237'])
          # characters like ^M
          (( x += 2 ));;
          # may cause trouble if spanned on two lines but well...
        (*)
          (( x++ ));;
      esac
      (( x >= mx )) && : ${pos[y]=$i}
      (( x >= COLUMNS )) && (( x=0, y++ ))
    done
    : ${pos[y]=$i} ${Y:=$y}

    local mouse_CURSOR
    if ((my + Y - cy > y)); then
      mouse_CURSOR=$#BUFFER
    elif ((my + Y - cy < 1)); then
      mouse_CURSOR=0
    else
      mouse_CURSOR=$(($pos[my + Y - cy] - ${#cur_prompt} - 1))
    fi

    case $ZLE_MOUSE_BUTTON in
      (0)
        # Button 1.  Move cursor.
        CURSOR=$mouse_CURSOR
      ;;

      (1)
        # Button 2.  Insert selection at mouse cursor postion.
        BUFFER=$BUFFER[1,mouse_CURSOR]$CUTBUFFER$BUFFER[mouse_CURSOR+1,-1]
        (( CURSOR = $mouse_CURSOR + $#CUTBUFFER ))
      ;;

      (2)
        # Button 3.  Copy from cursor to mouse to cutbuffer.
        killring=("$CUTBUFFER" "${(@)killring[1,-2]}")
        if (( mouse_CURSOR < CURSOR )); then
          CUTBUFFER=$BUFFER[mouse_CURSOR+1,CURSOR+1]
        else
          CUTBUFFER=$BUFFER[CURSOR+1,mouse_CURSOR+1]
        fi
      ;;
    esac
  }

  zle-toggle-mouse() {
    # If no prefix, toggle state.
    # If positive prefix, turn on.
    # If zero or negative prefix, turn off.

    # Allow this to be used as a normal function, too.
    if [[ -n $1 ]]; then
      local PREFIX=$1
    fi
    if (( $+PREFIX )); then
      if (( PREFIX > 0 )); then
        ZLE_USE_MOUSE=1
      else
        ZLE_USE_MOUSE=
      fi
    else
      if [[ -n $ZLE_USE_MOUSE ]]; then
        ZLE_USE_MOUSE=
      else
        ZLE_USE_MOUSE=1
      fi
    fi
    if [[ -n $WIDGET ]]; then
      # Zle is currently active.
      # Make sure it's turned on or off straight away if required.
      if [[ -n $ZLE_USE_MOUSE ]]; then
        print -n '\e[?1000h'
      else
        print -n '\e[?1000l'
      fi
    fi
  }

  if [[ $functions[precmd] != *ZLE_USE_MOUSE* ]]; then
    functions[precmd]+='
    [[ -n $ZLE_USE_MOUSE ]] && print -n '\''\e[?1000h'\'
  fi
  if [[ $functions[preexec] != *ZLE_USE_MOUSE* ]]; then
    functions[preexec]+='
    [[ -n $ZLE_USE_MOUSE ]] && print -n '\''\e[?1000l'\'
  fi

  zle -N zle-xterm-mouse
  bindkey '\e[M' zle-xterm-mouse

  zle -N zle-toggle-mouse
fi
### code end

-- 
Stéphane



Messages sorted by: Reverse Date, Date, Thread, Author