Add ft's zsh configuration to links section.
[zsh-lovers.git] / zsh_people / stchaz / mouse.zsh
1 # zsh mouse (and X clipboard) support v1.4
2 #
3 # QUICKSTART: jump to "how to use" below.
4 #
5 # currently supported:
6 #  - VT200 mouse tracking (at least xterm, gnome-terminal, rxvt)
7 #  - GPM on Linux little-endian systems such as i386 (at least)
8 #  - X clipboard handling if xsel(1) or xclip(1) is available (see
9 #    note below).
10
11 # addionnaly, if you are using xterm and don't want to use the mouse
12 # tracking system, you can map some button click events so that they
13 # send \E[M<bt>^X[<y><x> where <bt> is the character 0x20 + (0, 1, 2)
14 # <x>,<y> are the coordinate of the mouse pointer. This is usually done
15 # by adding those lines to your resource file for XTerm (~/.Xdefaults
16 # for example):
17 #
18 # XTerm.VT100.translations:             #override\
19 #        Mod4 <Btn1Up>: ignore()\n\
20 #        Mod4 <Btn2Up>: ignore()\n\
21 #        Mod4 <Btn3Up>: ignore()\n\
22 #        Mod4 <Btn1Down>: string(0x1b) string("[M ") dired-button()\n\
23 #        Mod4 <Btn2Down>: string(0x1b) string("[M!") dired-button()\n\
24 #        Mod4 <Btn3Down>: string(0x1b) string("[M") string(0x22) dired-button()\n\
25 #        Mod4 <Btn4Down>,<Btn4Up>: string(0x10)\n\
26 #        Mod4 <Btn5Down>,<Btn5Up>: string(0xe)
27 #
28 # That maps the button click events with the modifier 4 (when you hold
29 # the <Super> Key [possibly Windows keys] under recent versions of
30 # XFree86). The last two lines are for an easy support of the mouse
31 # wheel (map the mouse wheel events to ^N and ^P)
32 #
33 # Remember that even if you use the mouse tracking, you can still have
34 # access to the normal xterm selection mechanism by holding the <Shift>
35 # key.
36 #
37 # Note about X selection.
38 #    By default, xterm uses the PRIMARY selection instead of CLIPBOARD
39 #    for copy-paste. You may prefer changing that if you want
40 #    <Shift-Insert> to insert the CLIPBOARD and a better communication
41 #    between xterm and clipboard based applications like mozilla.
42 #    A way to do that is to add those resources:
43 #    XTerm.VT100.translations:             #override\
44 #      Shift ~Ctrl <KeyPress> Insert:insert-selection(\
45 #        CLIPBOARD, CUT_BUFFER0, PRIMARY) \n\
46 #      Shift Ctrl <KeyPress> Insert:insert-selection(\
47 #        PRIMARY, CUT_BUFFER0, CLIPBOARD) \n\
48 #      ~Ctrl ~Meta<BtnUp>: select-end(PRIMARY,CUT_BUFFER0,CLIPBOARD)
49 #
50 #    and to run a clipboard manager application such as xclipboard
51 #    (whose invocation you may want to put in your X session startup
52 #    file). (<Shift-Ctrl-Insert> inserts the PRIMARY selection as does
53 #    the middle mouse button). (without xclipboard, the clipboard
54 #    content is lost whenever the text is no more selected).
55 #
56 # How to use:
57 #
58 # add to your ~/.zshrc:
59 #  . /path/to/this-file
60 #  zle-toggle-mouse
61 #
62 # and if you want to be able to toggle on/off the mouse support:
63 # bindkey -M emacs '\em' zle-toggle-mouse
64 # # <Esc>m to toggle the mouse in emacs mode
65 # bindkey -M vicmd M zle-toggle-mouse
66 # # M for vi (cmd) mode
67 #
68 # clicking on the button 1:
69 #   moves the cursor to the pointed location
70 # clicking on the button 2:
71 #   inserts zsh cutbuffer at pointed location. If $DISPLAY is set and
72 #   either the xsel(1) or xclip(1) command is available, then it's the
73 #   content of the X clipboard instead that is pasted (and stored into
74 #   zsh cutbuffer).
75 # clicking on the button 3:
76 #   stores the text between the cursor and the pointed localion
77 #   into zsh cutbuffer. Additionaly, if $DISPLAY is set and either the
78 #   xclip(1) or xsel(1) command is available, that text is put on the
79 #   clipboard.
80 #
81 # If xsel or xlip is available, and $DISPLAY is set (and you're in a
82 # xterm-like terminal (even though that feature is terminal
83 # independant)), all the keys (actually widgets) that deal with zsh
84 # cut buffer have been modified so that the X CLIPBOARD selection is
85 # used. So <Ctrl-U>, <Ctrl-W>... will put the killed region on the X
86 # clipboard. vi mode "p" or emacs "<Ctrl-Y>" will paste the X CLIPBOARD
87 # selection. Only the keys that delete one character are not affected
88 # (<Del>, <Backspace>, <x>). Additionnaly, the primary selection (what
89 # is mouse highlighted and that you paste with the middle button) is put
90 # on the clipboard (and so made available to zsh) when you press
91 # <Meta-Insert> or <Ctrl-Insert> or <Ctrl-X>X (emacs mode) or X (vicmd
92 # mode). (note that your terminal may already do that by default, also
93 # note that your terminal may paste the primary selection and not the
94 # clipboard on <Shift-Insert>, you may change that if you find it
95 # confusing (see above))
96 #
97 # for GPM, you may change the list of modifiers (Shift, Alt...) that
98 # need to be on for the event to be accepted (see below).
99 #
100 # kterm: same as for xterm, but replace XTerm with KTerm in the resource
101 # customization
102 # hanterm: same as for xterm, but replace XTerm with Hanterm in the
103 # resource customization.
104 # Eterm: the paste(clipboard) actions don't seem to work, future
105 # versions of mouse.zsh may include support for X cutbuffers or revert
106 # back to PRIMARY selection to provide a better support for Eterm.
107 # gnome-terminal: you may want to bind some keys to Edit->{copy,paste}
108 # multi-gnome-terminal: selection looks mostly bogus to me
109 # rxvt,aterm,[ckgt]aterm,mlterm,pterm: no support for clipboard.
110 # GNUstep terminal: no mouse support but support for clipboard via menu
111 # KDE x-terminal-emulator: works OK except mouse button3 that is mapped
112 # to the context menu. Use Ctrl-Insert to put the selection on the
113 # clipboard.
114 # dtterm: no mouse support but the selection works OK.
115
116 # bugs:
117 #   - the GPM support was not much tested (was tested with gpm 1.19.6 on
118 #     a linux 2.6.9, AMD Athlon)
119 #   - mouse positionning doesn't work properly in "vared" if a prompt
120 #     was provided (vared -p <prompt>)
121 #
122 # Todo:
123 #   - write proper documentation
124 #   - customization through zstyles.
125 #
126 # Author:
127 #   Stephane Chazelas <Stephane_Chazelas@yahoo.fr>
128 #
129 # Changes:
130 #  v1.4 2005-03-01: <Ctrl-W><Ctrl-W> puts both words on the cut buffer
131 #       support for CUT_BUFFER0 via xprop.
132 #  v1.3 2005-02-28: support for more X terminals, tidy-up, separate
133 #       mouse support from clipboard support
134 #  v1.2 2005-02-24: support for vi-mode. X clipboard mirroring zsh cut buffer
135 #       when possible. Bug fixes.
136 #  v1.1 2005-02-20: support for X selection through xsel or xclip
137 #  v1.0 2004-11-18: initial release
138
139 # UTILITY FUNCTIONS
140
141 zle-error() {
142   local IFS=" "
143   if [[ -n $WIDGET ]]; then
144     # error message if zle active
145     zle -M -- "$*"
146   else
147     # on stderr otherwise
148     print -ru2 -- "$*"
149   fi
150 }
151
152 # SELECTION/CLIPBOARD FUNCTIONS
153
154 set-x-clipboard() { return 0; }
155 get-x-clipboard() { return 1; }
156
157 if
158   # find a command to read from/write to the X selections
159   if whence xsel > /dev/null 2>&1; then
160     x_selection_tool="xsel -p"
161     x_clipboard_tool="xsel -b"
162   elif whence xclip > /dev/null 2>&1; then
163     x_selection_tool="xclip -sel p"
164     x_clipboard_tool="xclip -sel c"
165   fi
166 then
167   eval '
168     get-x-clipboard() {
169       (( $+DISPLAY )) || return 1
170       local r
171       r=$('$x_clipboard_tool' -o < /dev/null 2> /dev/null && print .)
172       r=${r%.}
173       if [[ -n $r && $r != $CUTBUFFER ]]; then
174         killring=("$CUTBUFFER" "${(@)killring[1,-2]}")
175         CUTBUFFER=$r
176       fi
177     }
178     set-x-clipboard() {
179       (( ! $+DISPLAY )) ||
180         print -rn -- "$1" | '$x_clipboard_tool' -i 2> /dev/null
181     }
182     push-x-cut_buffer0() {
183       # retrieve the CUT_BUFFER0 property via xprop and store it on the
184       # CLIPBOARD selection
185       (( $+DISPLAY )) || return 1
186       local r
187       r=$(xprop -root -notype 8s \$0 CUT_BUFFER0 2> /dev/null) || return 1
188       r=${r#CUT_BUFFER0\"}
189       r=${r%\"}
190       r=${r//\'\''/\\\'\''}
191       eval print -rn -- \$\'\''$r\'\'' | '$x_clipboard_tool' -i 2> /dev/null
192     }
193     push-x-selection() {
194       # puts the PRIMARY selection onto the CLIPBOARD
195       # failing that call push-x-cut_buffer0
196       (( $+DISPLAY )) || return 1
197       local r
198       if r=$('$x_selection_tool' -o < /dev/null 2> /dev/null && print .) &&
199         r=${r%?} &&
200         [[ -n $r ]]; then
201         print -rn -- $r | '$x_clipboard_tool' -i 2> /dev/null
202       else
203         push-x-cut_buffer0
204       fi
205     }
206   '
207   # redefine the copying widgets so that they update the clipboard.
208   for w in copy-region-as-kill vi-delete vi-yank vi-change vi-change-whole-line vi-change-eol; do
209     eval '
210       '$w'() {
211         zle .'$w'
212         set-x-clipboard $CUTBUFFER
213       }
214       zle -N '$w
215   done
216
217   # that's a bit more complicated for those ones as we have to
218   # re-implement the special behavior that does that if you call several
219   # of those widgets in sequence, the text on the clipboard is the
220   # whole text cut, not just the text cut by the latest widget.
221   for w in ${widgets[(I).*kill-*]}; do
222     if [[ $w = *backward* ]]; then
223       e='$CUTBUFFER$scb'
224     else
225       e='$scb$CUTBUFFER'
226     fi
227     eval '
228       '${w#.}'() {
229         local scb=$CUTBUFFER
230         local slw=$LASTWIDGET
231         local sbl=${#BUFFER}
232
233         zle '$w'
234         (( $sbl == $#BUFFER )) && return
235         if [[ $slw = (.|)(backward-|)kill-* ]]; then
236           killring=("${(@)killring[2,-1]}")
237           CUTBUFFER='$e'
238         fi
239         set-x-clipboard $CUTBUFFER
240       }
241       zle -N '${w#.}
242   done
243   
244   zle -N push-x-selection
245   zle -N push-x-cut_buffer0
246
247   # put the current selection on the clipboard upon <Ctrl-Insert>
248   # <Meta-Insert> <Ctrl-X>X or X:
249   if (( $+terminfo[kSI] )); then
250     bindkey -M emacs "$terminfo[kSI]" push-x-selection
251     bindkey -M viins "$terminfo[kSI]" push-x-selection
252     bindkey -M vicmd "$terminfo[kSI]" push-x-selection
253   fi
254   if (( $+terminfo[kich1] )); then
255     # <Meta-Insert> according to terminfo
256     bindkey -M emacs "\e$terminfo[kich1]" push-x-selection
257     bindkey -M viins "\e$terminfo[kich1]" push-x-selection
258     bindkey -M vicmd "\e$terminfo[kich1]" push-x-selection
259   fi
260   # hardcode ^[[2;3~ which is sent by <Meta-Insert> on xterm
261   bindkey -M emacs '\e[2;3~' push-x-selection
262   bindkey -M viins '\e[2;3~' push-x-selection
263   bindkey -M vicmd '\e[2;3~' push-x-selection
264   # hardcode ^[^[[2;5~ which is sent by <Meta-Insert> on some terminals
265   bindkey -M emacs '\e\e[2~' push-x-selection
266   bindkey -M viins '\e\e[2~' push-x-selection
267   bindkey -M vicmd '\e\e[2~' push-x-selection
268
269   # hardcode ^[[2;5~ which is sent by <Ctrl-Insert> on xterm
270   # some terminals have already such a feature builtin (gnome/KDE
271   # terminals), others have no distinguishable character sequence sent
272   # by <Ctrl-Insert>
273   bindkey -M emacs '\e[2;5~' push-x-selection
274   bindkey -M viins '\e[2;5~' push-x-selection
275   bindkey -M vicmd '\e[2;5~' push-x-selection
276
277   # for terminal without an insert key:
278   bindkey -M vicmd X push-x-selection
279   bindkey -M emacs '^XX' push-x-selection
280
281   # the convoluted stuff below is to work around two problems:
282   #  1- we can't just redefine the widgets as then yank-pop would
283   #  stop working
284   #  2- we can't just rebind the keys to <Ctrl-Insert><other-key> as
285   #  then we'll loose the numeric argument
286   propagate-numeric() {
287     # next key (\e[0-dum) is mapped to <Ctrl-Insert>, plus the
288     # targeted widget with NUMERIC restored.
289     case $KEYMAP in
290       vicmd)
291         bindkey -M vicmd -s '\e[0-dum' $'\e[1-dum'$NUMERIC${KEYS/x/};;
292       *)
293         bindkey -M $KEYMAP -s '\e[0-dum' $'\e[1-dum'${NUMERIC//(#m)?/$'\e'$MATCH}${KEYS/x/};;
294     esac
295   }
296   zle -N get-x-clipboard
297   zle -N propagate-numeric
298   bindkey -M emacs '\e[1-dum' get-x-clipboard
299   bindkey -M vicmd '\e[1-dum' get-x-clipboard
300   bindkey -M emacs '\e[2-dum' yank
301   bindkey -M emacs '\e[2-xdum' propagate-numeric
302   bindkey -M emacs -s '^Y' $'\e[2-xdum\e[0-dum'
303   bindkey -M vicmd '\e[3-dum' vi-put-before
304   bindkey -M vicmd '\e[3-xdum' propagate-numeric
305   bindkey -M vicmd -s 'P' $'\e[3-xdum\e[0-dum'
306   bindkey -M vicmd '\e[4-dum' vi-put-after
307   bindkey -M vicmd '\e[4-xdum' propagate-numeric
308   bindkey -M vicmd -s 'p' $'\e[4-xdum\e[0-dum'
309 fi
310
311
312 # MOUSE FUNCTIONS
313
314 zle-update-mouse-driver() {
315   # default is no mouse support
316   [[ -n $ZLE_USE_MOUSE ]] && zle-error 'Sorry: mouse not supported'
317   ZLE_USE_MOUSE=
318 }
319
320
321 if [[ $TERM = *[xeEk]term* ||
322       $TERM = *mlterm* ||
323       $TERM = *rxvt* ||
324       $TERM = *screen* ||
325       ($TERM = *linux* && -S /dev/gpmctl)
326    ]]; then
327
328   set-status() { return $1; }
329
330   handle-mouse-event() {
331     emulate -L zsh
332     local bt=$1
333
334     case $bt in
335       3)
336         return 0;; # Process on press, discard release
337         # mlterm sends 3 on mouse-wheel-up but also on every button
338         # release, so it's unusable
339       64)
340         # eterm, rxvt, gnome/KDE terminal mouse wheel
341         zle up-line-or-history
342         return;;
343       4|65)
344         # mlterm or eterm, rxvt, gnome/KDE terminal mouse wheel
345         zle down-line-or-history
346         return;;
347     esac
348     local mx=$2 my=$3 last_status=$4
349     local cx cy i
350     setopt extendedglob
351
352     print -n '\e[6n' # query cursor position
353
354     local match mbegin mend buf=
355
356     while read -k i && buf+=$i && [[ $buf != *\[([0-9]##)\;[0-9]##R ]]; do :; done
357     # read response from terminal.
358     # note that we may also get a mouse tracking btn-release event,
359     # which would then be discarded.
360
361     [[ $buf = (#b)*\[([0-9]##)\;[0-9]##R ]] || return
362     cy=$match[1] # we don't need cx
363
364     local cur_prompt
365
366     # trying to guess the current prompt
367     case $CONTEXT in
368       (vared)
369         if [[ $0 = zcalc ]]; then
370           cur_prompt=${ZCALCPROMPT-'%1v> '}
371           setopt nopromptsubst nopromptbang promptpercent
372           # (ZCALCPROMPT is expanded with (%))
373         fi;;
374         # if vared is passed a prompt, we're lost
375       (select)
376         cur_prompt=$PS3;;
377       (cont)
378         cur_prompt=$PS2;;
379       (start)
380         cur_prompt=$PS1;;
381     esac
382
383     # if promptsubst, then we need first to do the expansions (to
384     # be able to remove the visual effects) and disable further
385     # expansions
386     [[ -o promptsubst ]] && cur_prompt=${${(e)cur_prompt}//(#b)([\\\$\`])/\\$match}
387
388     # restore the exit status in case $PS<n> relies on it
389     set-status $last_status
390
391     # remove the visual effects and do the prompt expansion
392     cur_prompt=${(S%%)cur_prompt//(#b)(%([BSUbsu]|{*%})|(%[^BSUbsu{}]))/$match[3]}
393
394     # we're now looping over the whole editing buffer (plus the last
395     # line of the prompt) to compute the (x,y) position of each char. We
396     # store the characters i for which x(i) <= mx < x(i+1) for every
397     # value of y in the pos array. We also get the Y(CURSOR), so that at
398     # the end, we're able to say which pos element is the right one
399     
400     local -a pos # array holding the possible positions of
401                  # the mouse pointer
402     local -i n x=0 y=1 cursor=$((${#cur_prompt}+$CURSOR+1))
403     local Y
404
405     buf=$cur_prompt$BUFFER
406     for ((i=1; i<=$#buf; i++)); do
407       (( i == cursor )) && Y=$y
408       n=0
409       case $buf[i] in
410         ($'\n') # newline
411           : ${pos[y]=$i}
412           (( y++, x=0 ));;
413         ($'\t') # tab advance til next tab stop
414           (( x = x/8*8+8 ));;
415         ([$'\0'-$'\037'$'\200'-$'\237'])
416           # characters like ^M
417           n=2;;
418         (*)
419           n=1;;
420       esac
421       while
422         (( x >= mx )) && : ${pos[y]=$i}
423         (( x >= COLUMNS )) && (( x=0, y++ ))
424         (( n > 0 ))
425       do
426         (( x++, n-- ))
427       done
428     done
429     : ${pos[y]=$i} ${Y:=$y}
430
431     local mouse_CURSOR
432     if ((my + Y - cy > y)); then
433       mouse_CURSOR=$#BUFFER
434     elif ((my + Y - cy < 1)); then
435       mouse_CURSOR=0
436     else
437       mouse_CURSOR=$(($pos[my + Y - cy] - ${#cur_prompt} - 1))
438     fi
439
440     case $bt in
441       (0)
442         # Button 1.  Move cursor.
443         CURSOR=$mouse_CURSOR
444       ;;
445
446       (1)
447         # Button 2.  Insert selection at mouse cursor postion.
448         get-x-clipboard
449         BUFFER=$BUFFER[1,mouse_CURSOR]$CUTBUFFER$BUFFER[mouse_CURSOR+1,-1]
450         (( CURSOR = $mouse_CURSOR + $#CUTBUFFER ))
451       ;;
452
453       (2)
454         # Button 3.  Copy from cursor to mouse to cutbuffer.
455         killring=("$CUTBUFFER" "${(@)killring[1,-2]}")
456         if (( mouse_CURSOR < CURSOR )); then
457           CUTBUFFER=$BUFFER[mouse_CURSOR+1,CURSOR+1]
458         else
459           CUTBUFFER=$BUFFER[CURSOR+1,mouse_CURSOR+1]
460         fi
461         set-x-clipboard $CUTBUFFER
462       ;;
463     esac
464   }
465
466   handle-xterm-mouse-event() {
467     local last_status=$?
468     emulate -L zsh
469     local bt mx my
470     
471     # either xterm mouse tracking or binded xterm event
472     # read the event from the terminal
473     read -k bt # mouse button, x, y reported after \e[M
474     bt=$((#bt & 0x47))
475     read -k mx
476     read -k my
477     if [[ $mx = $'\030' ]]; then
478       # assume event is \E[M<btn>dired-button()(^X\EG<x><y>)
479       read -k mx
480       read -k mx
481       read -k my
482       (( my = #my - 31 ))
483       (( mx = #mx - 31 ))
484     else
485       # that's a VT200 mouse tracking event
486       (( my = #my - 32 ))
487       (( mx = #mx - 32 ))
488     fi
489     handle-mouse-event $bt $mx $my $last_status
490   }
491
492   zle -N handle-xterm-mouse-event
493
494   if [[ $TERM = *linux* && -S /dev/gpmctl ]]; then
495     # GPM mouse support
496     if zmodload -i zsh/net/socket; then
497
498       zle-update-mouse-driver() {
499         if [[ -n $ZLE_USE_MOUSE ]]; then
500           if (( ! $+ZSH_GPM_FD )); then
501             if zsocket -d 9 /dev/gpmctl; then
502               ZSH_GPM_FD=$REPLY
503               # gpm initialisation:
504               # request single click events with given modifiers
505               local -A modifiers
506               modifiers=(
507                 none        0
508                 shift       1
509                 altgr       2
510                 ctrl        4
511                 alt         8
512                 left-shift  16
513                 right-shift 32
514                 left-ctrl   64
515                 right-ctrl  128
516                 caps-shift  256
517               )
518               local min max
519               # modifiers that need to be on
520               min=$((modifiers[none]))
521               # modifiers that may be on
522               max=$min
523
524               # send 16 bytes:
525               #   1-2: LE short: requested events (btn down = 0x0004)
526               #   3-4: LE short: event passed through (~GPM_HARD=0xFEFF)
527               #   5-6: LE short: min modifiers
528               #   7-8: LE short: max modifiers
529               #  9-12: LE int: pid
530               # 13-16: LE int: virtual console number
531
532               print -u$ZSH_GPM_FD -n "\4\0\377\376\\$(([##8]min&255
533               ))\\$(([##8]min>>8))\\$(([##8]max&255))\\$(([##8]max>>8
534               ))\\$(([##8]$$&255))\\$(([##8]$$>>8&255))\\$((
535               [##8]$$>>16&255))\\$(( [##8]$$>>24))\\$((
536               [##8]${TTY#/dev/tty}))\0\0\0"
537               zle -F $ZSH_GPM_FD handle-gpm-mouse-event
538             else
539               zle-error 'Error: unable to connect to GPM'
540               ZLE_USE_MOUSE=
541             fi
542           fi
543         else
544           # ZLE_USE_MOUSE disabled, close GPM connection
545           if (( $+ZSH_GPM_FD )); then
546             eval "exec $ZSH_GPM_FD>&-"
547             # what if $ZSH_GPM_FD > 9 ?
548             zle -F $ZSH_GPM_FD # remove the handler
549             unset ZSH_GPM_FD
550           fi
551         fi
552       }
553
554       handle-gpm-mouse-event() {
555         local last_status=$?
556         local event i
557         if read -u$1 -k28 event; then
558           local buttons x y
559           (( buttons = ##$event[1] ))
560           (( x = ##$event[9] + ##$event[10] << 8 ))
561           (( y = ##$event[11] + ##$event[12] << 8 ))
562           handle-mouse-event $(( (5 - (buttons & -buttons)) / 2 )) $x $y $last_status
563           zle -R # redraw buffer
564         else
565           zle -M 'Error: connection to GPM lost'
566           ZLE_USE_MOUSE=
567           zle-update-mouse-driver
568         fi
569       }
570     fi 
571   else
572     # xterm-like mouse support
573     zmodload -i zsh/parameter # needed for $functions
574
575     zle-update-mouse-driver() {
576       if [[ -n $WIDGET ]]; then
577         if [[ -n $ZLE_USE_MOUSE ]]; then
578           print -n '\e[?1000h'
579         else
580           print -n '\e[?1000l'
581         fi
582       fi
583     }
584
585     if [[ $functions[precmd] != *ZLE_USE_MOUSE* ]]; then
586       functions[precmd]+='
587       [[ -n $ZLE_USE_MOUSE ]] && print -n '\''\e[?1000h'\'
588     fi
589     if [[ $functions[preexec] != *ZLE_USE_MOUSE* ]]; then
590       functions[preexec]+='
591       [[ -n $ZLE_USE_MOUSE ]] && print -n '\''\e[?1000l'\'
592     fi
593
594     bindkey -M emacs '\e[M' handle-xterm-mouse-event
595     bindkey -M viins '\e[M' handle-xterm-mouse-event
596     bindkey -M vicmd '\e[M' handle-xterm-mouse-event
597
598     if [[ $TERM = *Eterm* ]]; then
599       # Eterm sends \e[5Mxxxxx on drag events, be want to discard them
600       discard-mouse-drag() {
601         local junk
602         read -k5 junk
603       }
604       zle -N discard-mouse-drag
605       bindkey -M emacs '\e[5M' discard-mouse-drag
606       bindkey -M viins '\e[5M' discard-mouse-drag
607       bindkey -M vicmd '\e[5M' discard-mouse-drag
608     fi
609   fi
610
611 fi
612
613 zle-toggle-mouse() {
614   # If no prefix, toggle state.
615   # If positive prefix, turn on.
616   # If zero or negative prefix, turn off.
617
618   # Allow this to be used as a normal function, too.
619   if [[ -n $1 ]]; then
620     local PREFIX=$1
621   fi
622   if (( $+PREFIX )); then
623     if (( PREFIX > 0 )); then
624       ZLE_USE_MOUSE=1
625     else
626       ZLE_USE_MOUSE=
627     fi
628   else
629     if [[ -n $ZLE_USE_MOUSE ]]; then
630       ZLE_USE_MOUSE=
631     else
632       ZLE_USE_MOUSE=1
633     fi
634   fi
635   zle-update-mouse-driver
636 }
637 zle -N zle-toggle-mouse