Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
mouse and X clipboard support
- X-seq: zsh-users 8544
- From: Stephane Chazelas <Stephane_Chazelas@xxxxxxxx>
- To: Zsh Users List <zsh-users@xxxxxxxxxx>
- Subject: mouse and X clipboard support
- Date: Wed, 23 Feb 2005 17:56:02 +0000
- Mail-followup-to: Zsh Users List <zsh-users@xxxxxxxxxx>
- Mailing-list: contact zsh-users-help@xxxxxxxxxx; run by ezmlm
Hi,
you may already be aware of http://stchaz.free.fr/mouse.zsh
a mouse support extension for zsh (as a zsh script).
I've updated it to also include support for the X clipboard
selection (zsh cut buffer is exported to/from the clipboard).
You need either the xclip or xsel utility.
I've not tested it much yet, but I find it already quite useful
(to exchange strings between commands/windows...).
You may find the file attached, and soon at
http://stchaz.free.fr/ when I have fixed the latest bugs.
--
Stephane
# zsh mouse (and X clipboard) support v1.2-testing
#
# QUICKSTART: jump to "how to use" below.
#
# currently supported:
# - VT200 mouse tracking (at least xterm, gnome-terminal, rxvt)
# - GPM on Linux little-endian systems such as i386 (at least)
# - X clipboard handling if xsel(1) or xclip(1) is available (see
# note below).
#
# addionnaly, if you are using xterm and don't want to use the mouse
# tracking system, you can map some button click events so that they
# send \E[M<bt>^X[<y><x> where <bt> is the character 0x20 + (0, 1, 2)
# <x>,<y> are the coordinate of the mouse pointer. This is usually done
# by adding those lines to your resource file for XTerm (~/.Xdefaults
# for example):
#
# XTerm.VT100.translations: #override\
# Mod4 <Btn1Up>: ignore()\n\
# Mod4 <Btn2Up>: ignore()\n\
# Mod4 <Btn3Up>: ignore()\n\
# Mod4 <Btn1Down>: string(0x1b) string("[M ") dired-button()\n\
# Mod4 <Btn2Down>: string(0x1b) string("[M!") dired-button()\n\
# Mod4 <Btn3Down>: string(0x1b) string("[M") string(0x22) dired-button()\n\
# Mod4 <Btn4Down>,<Btn4Up>: string(0x10)\n\
# Mod4 <Btn5Down>,<Btn5Up>: string(0xe)
#
# That maps the button click events with the modifier 4 (when you hold
# the <Super> Key [possibly Windows keys] under recent versions of
# XFree86). The last two lines are for an easy support of the mouse
# wheel (map the mouse wheel events to ^N and ^P)
#
# Remember that even if you use the mouse tracking, you can still have
# access to the normal xterm selection mechanism by holding the <Shift>
# key.
#
# Note about X selection.
# By default, xterm uses the PRIMARY selection instead of CLIPBOARD
# for copy-paste. You may prefer changing that if you want
# <Shift-Insert> to insert the CLIPBOARD and a better communication
# between xterm and clipboard based applications like mozilla.
# A way to do that is to add those resources:
# XTerm.VT100.translations: #override\
# Shift ~Ctrl <KeyPress> Insert:insert-selection(\
# CLIPBOARD, CUT_BUFFER0, PRIMARY) \n\
# Shift Ctrl <KeyPress> Insert:insert-selection(\
# PRIMARY, CUT_BUFFER0, CLIPBOARD) \n\
# ~Ctrl ~Meta<BtnUp>: select-end(PRIMARY,CUT_BUFFER0,CLIPBOARD)
#
# and to run a clipboard manager application such as xclipboard
# (whose invocation you may want to put in your X session startup
# file). (<Shift-Ctrl-Insert> inserts the PRIMARY selection as does
# the middle mouse button)
#
# How to use:
#
# add to your ~/.zshrc:
# . /path/to/this-file
# zle-toggle-mouse
#
# and if you want to be able to toggle on/off the mouse support:
# bindkey -M emacs '\em' zle-toggle-mouse
# # <Esc>m to toggle the mouse in emacs mode
# bindkey -M vicmd M zle-toggle-mouse
# # M for vi (cmd) mode
#
# clicking on the button 1:
# moves the cursor to the pointed location
# clicking on the button 2:
# inserts zsh cutbuffer at pointed location. If $DISPLAY is set and
# either the xsel(1) or xclip(1) command is available, then it's the
# content of the X clipboard instead that is pasted (and stored into
# zsh cutbuffer).
# clicking on the button 3:
# stores the text between the cursor and the pointed localion
# into zsh cutbuffer. Additionaly, if $DISPLAY is set and either the
# xclip(1) or xsel(1) command is available, that text is put on the
# clipboard.
#
# If xsel or xlip is available, and $DISPLAY is set (and you're in a
# xterm-like terminal (even though that feature is terminal
# independant)), all the keys (actually widgets) that deal with zsh
# cut buffer have been modified so that the X CLIPBOARD selection is
# used. So <Ctrl-U>, <Ctrl-W>... will put the killed region on the X
# clipboard. vi mode "p" or emacs "<Ctrl-Y>" will paste the X CLIPBOARD
# selection. Only the keys that delete one character are not affected
# (<Del>, <Backspace>, <x>). The X selection is fetched when you use one
# of the paste functions. You can also explicitely fetch the clipboard
# (without inserting) by pressing <Ctrl-Insert> (not on all terminals)
# or <Ctrl-X>X (emacs) or X (vi cmd). This way it is made available to
# zsh through the emacs killring or vi numbered registers.
#
# for GPM, you may change the list of modifiers (Shift, Alt...) that
# need to be on for the event to be accepted (see below).
#
# bugs:
# - the GPM support was not much tested (was tested with gpm 1.19.6 on
# a linux 2.6.9, AMD Athlon)
# - mouse positionning doesn't work properly in "vared" if a prompt
# was provided (vared -p <prompt>)
# Author:
# Stephane Chazelas <Stephane_Chazelas@xxxxxxxx>
#
# Changes:
# v1.2 support for vi-mode. X clipboard mirroring zsh cut buffer
# when possible.
# v1.1 2005-02-20: support for X selection through xsel or xclip
# v1.0 2004-11-18: initial release
zle-error() {
local IFS=" "
if [[ -n $WIDGET ]]; then
# error message if zle active
zle -M -- "$*"
else
# on stderr otherwise
print -ru2 -- "$*"
fi
}
zle-update-mouse-driver() {
# default is no mouse support
[[ -n $ZLE_USE_MOUSE ]] && zle-error 'Sorry: mouse not supported'
ZLE_USE_MOUSE=
}
if [[ $TERM = *xterm* ||
$TERM = *rxvt* ||
$TERM = *screen* ||
($TERM = *linux* && -S /dev/gpmctl)
]]; then
set-status() { return $1; }
set-x-selection() { return 0; }
get-x-selection() { return 1; }
zle-mouse() {
local last_status=$?
emulate -L zsh
setopt extendedglob # for (#b)
local bt mx my cy i
if (( $# == 4 )); then
# We've been called by gpm-handler -> GPM event
bt=$1
mx=$2
my=$3
last_status=$4
else
# either xterm mouse tracking or binded xterm event
# read the event from the terminal
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 event is \E[M<btn>dired-button()(^X\EG<x><y>)
read -k mx
read -k mx
read -k my
(( my = #my - 31 ))
(( mx = #mx - 31 ))
else
# that's a VT200 mouse tracking event
(( my = #my - 32 ))
(( mx = #mx - 32 ))
[[ $bt == 3 ]] && return 0
# Process on press, discard release
fi
fi
print -n '\e[6n' # query cursor position
local match mbegin mend buf=
while read -k i && buf+=$i && [[ $buf != *\[([0-9]##)\;[0-9]##R ]]; do :; done
# read response from terminal.
# note that we may also get a mouse tracking btn-release event,
# which would then be discarded.
[[ $buf = (#b)*\[([0-9]##)\;[0-9]##R ]] || return
cy=$match[1] # we don't need cx
local cur_prompt
# trying to guess the current prompt
case $CONTEXT in
(vared)
if [[ $0 = zcalc ]]; then
cur_prompt=${ZCALCPROMPT-'%1v> '}
setopt nopromptsubst nopromptbang promptpercent
# (ZCALCPROMPT is expanded with (%))
fi;;
# if vared is passed a prompt, we're lost
(select)
cur_prompt=$PS3;;
(cont)
cur_prompt=$PS2;;
(start)
cur_prompt=$PS1;;
esac
# if promptsubst, then we need first to do the expansions (to
# be able to remove the visual effects) and disable further
# expansions
[[ -o promptsubst ]] && cur_prompt=${${(e)cur_prompt}//(#b)([\\\$\`])/\\$match}
# restore the exit status in case $PS<n> relies on it
set-status $last_status
# remove the visual effects and do the prompt expansion
cur_prompt=${(S%%)cur_prompt//(#b)(%([BSUbsu]|{*%})|(%[^BSUbsu{}]))/$match[3]}
# we're now looping over the whole editing buffer (plus the last
# line of the prompt) to compute the (x,y) position of each char. We
# store the characters i for which x(i) <= mx < x(i+1) for every
# value of y in the pos array. We also get the Y(CURSOR), so that at
# the end, we're able to say which pos element is the right one
local -a pos # array holding the possible positions of
# the mouse pointer
local -i n 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
n=0
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'$'\200'-$'\237'])
# characters like ^M
n=2;;
(*)
n=1;;
esac
while
(( x >= mx )) && : ${pos[y]=$i}
(( x >= COLUMNS )) && (( x=0, y++ ))
(( n > 0 ))
do
(( x++, n-- ))
done
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 $bt in
(0)
# Button 1. Move cursor.
CURSOR=$mouse_CURSOR
;;
(1)
# Button 2. Insert selection at mouse cursor postion.
get-x-selection
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
set-x-selection $CUTBUFFER
;;
esac
}
zle -N zle-mouse
if [[ $TERM = *linux* && -S /dev/gpmctl ]]; then
# GPM mouse support
if zmodload -i zsh/net/socket; then
zle-update-mouse-driver() {
if [[ -n $ZLE_USE_MOUSE ]]; then
if (( ! $+ZSH_GPM_FD )); then
if zsocket -d 9 /dev/gpmctl; then
ZSH_GPM_FD=$REPLY
# gpm initialisation:
# request single click events with given modifiers
local -A modifiers
modifiers=(
none 0
shift 1
altgr 2
ctrl 4
alt 8
left-shift 16
right-shift 32
left-ctrl 64
right-ctrl 128
caps-shift 256
)
local min max
# modifiers that need to be on
min=$((modifiers[none]))
# modifiers that may be on
max=$min
# send 16 bytes:
# 1-2: LE short: requested events (btn down = 0x0004)
# 3-4: LE short: event passed through (~GPM_HARD=0xFEFF)
# 5-6: LE short: min modifiers
# 7-8: LE short: max modifiers
# 9-12: LE int: pid
# 13-16: LE int: virtual console number
print -u$ZSH_GPM_FD -n "\4\0\377\376\\$(([##8]min&255
))\\$(([##8]min>>8))\\$(([##8]max&255))\\$(([##8]max>>8
))\\$(([##8]$$&255))\\$(([##8]$$>>8&255))\\$((
[##8]$$>>16&255))\\$(( [##8]$$>>24))\\$((
[##8]${TTY#/dev/tty}))\0\0\0"
zle -F $ZSH_GPM_FD gpm-handler
else
zle-error 'Error: unable to connect to GPM'
ZLE_USE_MOUSE=
fi
fi
else
# ZLE_USE_MOUSE disabled, close GPM connection
if (( $+ZSH_GPM_FD )); then
eval "exec $ZSH_GPM_FD>&-"
# what if $ZSH_GPM_FD > 9 ?
zle -F $ZSH_GPM_FD # remove the handler
unset ZSH_GPM_FD
fi
fi
}
gpm-handler() {
local last_status=$?
local event i
if read -u$1 -k28 event; then
local buttons x y
(( buttons = ##$event[1] ))
(( x = ##$event[9] + ##$event[10] << 8 ))
(( y = ##$event[11] + ##$event[12] << 8 ))
zle zle-mouse $(( (5 - (buttons & -buttons)) / 2 )) $x $y $last_status
zle -R # redraw buffer
else
zle -M 'Error: connection to GPM lost'
ZLE_USE_MOUSE=
zle-update-mouse-driver
fi
}
fi
else
# xterm-like mouse support
zmodload -i zsh/parameter # needed for $functions
zle-update-mouse-driver() {
if [[ -n $WIDGET ]]; then
if [[ -n $ZLE_USE_MOUSE ]]; then
print -n '\e[?1000h'
else
print -n '\e[?1000l'
fi
fi
}
if
# find a command to read from/write to the X selections
if whence xsel > /dev/null 2>&1; then
x_selection_tool="xsel -b"
elif whence xclip > /dev/null 2>&1; then
x_selection_tool="xclip -sel c"
fi
then
eval '
get-x-selection() {
(( $+DISPLAY )) || return 1
local r
r=$('$x_selection_tool' -o < /dev/null 2> /dev/null && print .)
r=${r%.}
if [[ -n $r ]]; then
killring=("$CUTBUFFER" "${(@)killring[1,-2]}")
CUTBUFFER=$r
fi
}
set-x-selection() {
(( ! $+DISPLAY )) ||
print -rn -- "$1" | '$x_selection_tool' -i 2> /dev/null
}'
# redefine the *kill* widgets so that they also update the X
# selection
for w in ${widgets[(I).*kill*]} .vi-delete .vi-change .vi-change-whole-line .vi-change-eol; do
eval '
'${w#.}'() {
zle '$w'
set-x-selection $CUTBUFFER
}
zle -N '${w#.}
done
# pasting operations paste the X clipboard.
for w in yank vi-put-before vi-put-after; do
eval '
'$w'() {
get-x-selection
zle '.$w'
}
zle -N '$w
done
# get the X selection into zsh cut buffer on <Ctrl-Insert>
zle -N get-x-selection
if (( $+terminfo[kSI] )); then
bindkey -M emacs "$terminfo[kSI]" get-x-selection
bindkey -M viins "$terminfo[kSI]" get-x-selection
bindkey -M vicmd "$terminfo[kSI]" get-x-selection
fi
# hardcode ^[[2;5~ which is sent by <Ctrl-Insert> on xterm
bindkey -M emacs '\e[2;5~' get-x-selection
bindkey -M viins '\e[2;5~' get-x-selection
bindkey -M vicmd '\e[2;5~' get-x-selection
# some terminals don't have a <Ctrl-Insert>
bindkey -M vicmd X get-x-selection
bindkey -M emacs '^XX' get-x-selection
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
bindkey -M emacs '\e[M' zle-mouse
bindkey -M viins '\e[M' zle-mouse
bindkey -M vicmd '\e[M' zle-mouse
fi
fi
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
zle-update-mouse-driver
}
zle -N zle-toggle-mouse
Messages sorted by:
Reverse Date,
Date,
Thread,
Author