From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5784 invoked from network); 12 Nov 2004 12:07:45 -0000 Received: from news.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.88) by ns1.primenet.com.au with SMTP; 12 Nov 2004 12:07:45 -0000 Received: (qmail 7807 invoked from network); 12 Nov 2004 12:07:40 -0000 Received: from sunsite.dk (130.225.247.90) by a.mx.sunsite.dk with SMTP; 12 Nov 2004 12:07:40 -0000 Received: (qmail 29754 invoked by alias); 12 Nov 2004 12:04:34 -0000 Mailing-List: contact zsh-users-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 8209 Received: (qmail 29744 invoked from network); 12 Nov 2004 12:04:33 -0000 Received: from unknown (HELO a.mx.sunsite.dk) (130.225.247.88) by sunsite.dk with SMTP; 12 Nov 2004 12:04:33 -0000 Received: (qmail 5156 invoked from network); 12 Nov 2004 12:04:14 -0000 Received: from smtp-out4.blueyonder.co.uk (195.188.213.7) by a.mx.sunsite.dk with SMTP; 12 Nov 2004 12:03:15 -0000 Received: from sc ([82.41.214.33]) by smtp-out4.blueyonder.co.uk with Microsoft SMTPSVC(5.0.2195.6713); Fri, 12 Nov 2004 12:03:34 +0000 Date: Fri, 12 Nov 2004 12:02:48 +0000 From: Stephane Chazelas To: Zsh users list Subject: [tip] mouse and mouse-wheel support! Message-ID: <20041112120248.GC4461@sc> Mail-Followup-To: Zsh users list References: <20041111122011.GB4451@sc> Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20041111122011.GB4451@sc> User-Agent: Mutt/1.5.6i X-OriginalArrivalTime: 12 Nov 2004 12:03:34.0850 (UTC) FILETIME=[A2BF6220:01C4C8AF] X-Spam-Checker-Version: SpamAssassin 2.63 on a.mx.sunsite.dk X-Spam-Level: * X-Spam-Status: No, hits=1.5 required=6.0 tests=RCVD_IN_SORBS autolearn=no version=2.63 X-Spam-Hits: 1.5 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 and , not only zsh (readline (bash, gdb, rc...), emacs, vim...). ### code begin # Add that to your X resource file to have wheel-mouse support # and when ZLE_USE_MOUSE is off under xterm # (remove the #) #!!!BEGIN #XTerm.VT100.translations: #override\ # Mod4 ,: string(0x1b) string("[M ") dired-button()\n\ # Mod4 ,: string(0x1b) string("[M!") dired-button()\n\ # Mod4 ,: string(0x1b) string("[M") string(0x22) dired-button()\n\ # Mod4 ,: string(0xe)\n\ # Mod4 ,: 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[Mdired-button()(^X\EG) 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 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