zsh-users
 help / color / mirror / code / Atom feed
* [tip] mouse support
@ 2004-11-11 12:20 Stephane Chazelas
  2004-11-11 14:40 ` Peter Stephenson
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Stephane Chazelas @ 2004-11-11 12:20 UTC (permalink / raw)
  To: Zsh users list

Hi zsh users,

I just posted that to comp.unix.shell, I thought it might be of
some interest for some of you. Basically, it's cursor
positionning with the mouse under xterm like terminals (works
with xterm, gnome-terminal and rxvt AFAICS, probably also with
putty, not if there are tabs or NLs (or multi-byte characters)
in the zle buffer).

I'm not used to writing zle code. Please tell me what you think.

### code begins
if [[ $TERM = *xterm* ]]; then
zle-xterm-mouse() {
 emulate -L zsh
 setopt extendedglob # for (#b)
 local bt mx my cx cy i match mbegin mend

 read -k bt # mouse button, x, y reported after \e[M
 read -k mx
 read -k my
 [[ $bt = "#" ]] || return 0 # only for btn1 release

 print -n '\e[6n' # query cursor position
 while read -k i && [[ $i != R ]]; do cx+=$i; done
 # can't use read -d R in zle

 [[ $cx = (#b)??(*)\;(*) ]] || return
 cy=$match[1]
 cx=$match[2]

 (( CURSOR += #mx - 32 - cx + (#my - 32 - cy) * COLUMNS ))
 return 0
}

precmd() {
  # enable mouse tracking
  print -n '\e[?1000h'
}
preexec() {
  # disable mouse tracking
  print -n '\e[?1000l'
}

zle -N zle-xterm-mouse zle-xterm-mouse
bindkey '\e[M' zle-xterm-mouse
fi
### code ends

-- 
Stéphane


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse support
  2004-11-11 12:20 [tip] mouse support Stephane Chazelas
@ 2004-11-11 14:40 ` Peter Stephenson
  2004-11-11 16:22   ` Stephane Chazelas
  2004-11-11 16:07 ` Andy Spiegl
  2004-11-12 12:02 ` [tip] mouse and mouse-wheel support! Stephane Chazelas
  2 siblings, 1 reply; 13+ messages in thread
From: Peter Stephenson @ 2004-11-11 14:40 UTC (permalink / raw)
  To: Zsh users list

Stephane Chazelas wrote:
> I just posted that to comp.unix.shell, I thought it might be of
> some interest for some of you. Basically, it's cursor
> positionning with the mouse under xterm like terminals (works
> with xterm, gnome-terminal and rxvt AFAICS, probably also with
> putty, not if there are tabs or NLs (or multi-byte characters)
> in the zle buffer).

This is very useful.  (Other people should note it's only useful with
the current zsh editing buffer; there's no way of accessing other
information on the screen.)

I've extended it.  Other people may have suggestions, too.  This
probably requires zsh 4.2 to use the cutbuffer and killring.

Button 1 is as before.

Button 2 pastes the zsh cutbuffer at the mouse position.  This
is identical to yank.  It also emulates normal xterm behaviour
by pasting at the cursor, not the mouse position.  This seemed
less surprising but is easy to change or make optional.

Button 3 copies the region from the cursor to the mouse position
into the zsh cutbuffer.   This is like copy-region-as-kill but
doesn't use the zsh mark. (I could set that too, I suppose.)
Note I'm not 100% confident the limits (i.e. indices into the editing
buffer) used are the best ones.

Note no use is made of the X selection mechanism.

Other additions
- It takes care to change the existing precmd and preexec rather
  than overwriting them.  This won't work if there is a "return"
  earlier in either.  Possibly the new lines should go at the top.
- There's a function/widget zle-toggle-mouse to switch between normal
  xterm mouse and zle mouse.  Positive prefix forces zle mouse,
  negative or zero prefix forces normal xterm mouse.

The version of code below is designed to be used as a (normal,
i.e. non-zle) function, called eg. setup-zle-mouse.  (Naming might need
rationalising, too.)


### code begins
[[ $TERM = *xterm* ]] || return

zmodload -i zsh/parameter

zle-xterm-mouse() {
  emulate -L zsh
  setopt extendedglob # for (#b)
  local bt mx my cx cy i match mbegin mend text
  integer rel

  read -k bt # mouse button, x, y reported after \e[M
  read -k mx
  read -k my
  if [[ $bt != "#" ]]; then
    # Process on release, but record the button on press.
    ZLE_MOUSE_BUTTON=$bt
    return
  fi

  print -n '\e[6n' # query cursor position
  while read -k i && [[ $i != R ]]; do cx+=$i; done
  # can't use read -d R in zle

  [[ $cx = (#b)??(*)\;(*) ]] || return
  cy=$match[1]
  cx=$match[2]

  # Relative position between cursor and mouse.
  (( rel = #mx - 32 - cx + (#my - 32 - cy) * COLUMNS ))

  case $ZLE_MOUSE_BUTTON in
    (' ')
    # Button 1.  Move cursor.
    (( CURSOR += rel ))
    ;;

    ('!')
    # Button 2.  Insert selection at cursor postion.
    LBUFFER+=$CUTBUFFER
    ;;

    ('"')
    # Button 3.  Copy from cursor to mouse to cutbuffer.
    if (( rel > 0 )); then
      text=${RBUFFER[1,rel]}
    elif (( CURSOR + rel > 0 )); then
      text=${LBUFFER[rel,-1]}
    else
      text=$LBUFFER
    fi
    killring=("$CUTBUFFER" "${(@)killring[1,-2]}")
    CUTBUFFER=$text
    ;;
  esac
  return 0
}

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

ZLE_USE_MOUSE=1
### code ends


-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


**********************************************************************
This email and any files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the system manager.

This footnote also confirms that this email message has been swept by
MIMEsweeper for the presence of computer viruses.

www.mimesweeper.com
**********************************************************************


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse support
  2004-11-11 12:20 [tip] mouse support Stephane Chazelas
  2004-11-11 14:40 ` Peter Stephenson
@ 2004-11-11 16:07 ` Andy Spiegl
  2004-11-11 17:26   ` Stephane Chazelas
  2004-11-12 12:02 ` [tip] mouse and mouse-wheel support! Stephane Chazelas
  2 siblings, 1 reply; 13+ messages in thread
From: Andy Spiegl @ 2004-11-11 16:07 UTC (permalink / raw)
  To: Zsh users list

Hi Stephane,

wow great idea!  Not that I use the mouse a lot while typing in the
shell but sometimes it would really be useful, especially with very
long lines or multiline commands.

With multilines it doesn't work however. :-(
And it also breaks the usual copy&paste behaviour.  It is necessary
to hold the SHIFT key to be able to copy&paste.  Hm, maybe it would
be better to just get the new behaviour holding down some modifier key.

Any other opinions?
 Andy.

-- 
                              o      _     _         _
  ------- __o       __o      /\_   _ \\o  (_)\__/o  (_)          -o)
  ----- _`\<,_    _`\<,_    _>(_) (_)/<_    \_| \   _|/' \/       /\\
  ---- (_)/ (_)  (_)/ (_)  (_)        (_)   (_)    (_)'  _\o_    _\_v
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Being adopted means that you grew in your mother's heart
 instead of her tummy.  (Jocelynn, http://adoption.here.de/)


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse support
  2004-11-11 14:40 ` Peter Stephenson
@ 2004-11-11 16:22   ` Stephane Chazelas
  2004-11-11 17:47     ` Bart Schaefer
  0 siblings, 1 reply; 13+ messages in thread
From: Stephane Chazelas @ 2004-11-11 16:22 UTC (permalink / raw)
  To: Zsh users list

On Thu, Nov 11, 2004 at 02:40:32PM +0000, Peter Stephenson wrote:
> Stephane Chazelas wrote:
> > I just posted that to comp.unix.shell, I thought it might be of
> > some interest for some of you. Basically, it's cursor
> > positionning with the mouse under xterm like terminals (works
> > with xterm, gnome-terminal and rxvt AFAICS, probably also with
> > putty, not if there are tabs or NLs (or multi-byte characters)
> > in the zle buffer).
> 
> This is very useful.  (Other people should note it's only useful with
> the current zsh editing buffer; there's no way of accessing other
> information on the screen.)

In that new one, I tried to address the tab/newline issue.
It tries to calculate the length of the last line of the prompt,
it may fail if PS1 contains %%s or %{ %{ ... %} %} or if two
consecutive expansions of PS1 may not generate the same thing,
it would be nice to have access at what zsh thinks is the
cursor horizontal position at begining of buffer.

> I've extended it.  Other people may have suggestions, too.  This
> probably requires zsh 4.2 to use the cutbuffer and killring.
> 
> Button 1 is as before.
> 
> Button 2 pastes the zsh cutbuffer at the mouse position.  This
> is identical to yank.  It also emulates normal xterm behaviour
> by pasting at the cursor, not the mouse position.  This seemed
> less surprising but is easy to change or make optional.

I changed for "pasting at the mouse cursor".

> Button 3 copies the region from the cursor to the mouse position
> into the zsh cutbuffer.   This is like copy-region-as-kill but
> doesn't use the zsh mark. (I could set that too, I suppose.)
> Note I'm not 100% confident the limits (i.e. indices into the editing
> buffer) used are the best ones.

Not sure of mines either ;).

> Other additions
> - It takes care to change the existing precmd and preexec rather
>   than overwriting them.  This won't work if there is a "return"
>   earlier in either.  Possibly the new lines should go at the top.

Thanks for that. I didn't know about the $functions hash.

> - There's a function/widget zle-toggle-mouse to switch between normal
>   xterm mouse and zle mouse.  Positive prefix forces zle mouse,
>   negative or zero prefix forces normal xterm mouse.

You can use hold the <Shift> key to have the normal xterm
behavior.

Here's the code. Be careful when you bullet proof it, it may be
full of bugs...

### code begin
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
  read -k mx
  read -k my
  if [[ $bt != "#" ]]; then
    # Process on release, but record the button on press.
    ZLE_MOUSE_BUTTON=$bt
    return 0
  fi

  (( my = #my - 32 ))
  (( mx = #mx - 32 ))

  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
  if [[ -o promptsubst ]]; then
    cur_prompt=${(e)cur_prompt}
  else
    cur_prompt=$cur_prompt
  fi

  # remove visual effects:
  cur_prompt=${(S)cur_prompt//("%{"*"%}"|%[BbEuUsS])/}

  # restore the exit status in case $PS<n> relies on it
  set-status $last_status
  cur_prompt=${(%)cur_prompt}
  cur_prompt=${cur_prompt##*$'\n'}
    
  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
    (' ')
      # Button 1.  Move cursor.
      CURSOR=$mouse_CURSOR
    ;;

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

    ('"')
      # 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
}
### code ends

-- 
Stéphane


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse support
  2004-11-11 16:07 ` Andy Spiegl
@ 2004-11-11 17:26   ` Stephane Chazelas
  0 siblings, 0 replies; 13+ messages in thread
From: Stephane Chazelas @ 2004-11-11 17:26 UTC (permalink / raw)
  To: Zsh users list

On Thu, Nov 11, 2004 at 05:07:36PM +0100, Andy Spiegl wrote:
[...] 
> wow great idea!  Not that I use the mouse a lot while typing in the
> shell but sometimes it would really be useful, especially with very
> long lines or multiline commands.
> 
> With multilines it doesn't work however. :-(

The new one I provided fixes that.

> And it also breaks the usual copy&paste behaviour.  It is necessary
> to hold the SHIFT key to be able to copy&paste.  Hm, maybe it would
> be better to just get the new behaviour holding down some modifier key.
[...]

I don't think it is possible. We'd have to tell xterm to send
the mouse tracking escape sequences on other events than the
default ones, and I don't think it is possible.

We could map any action on <Ctrl-btn<n>> but not the sending of
the cursor position. We may be able to do something with
dired-button(), though.

That would involve install a new translation table, you could
map:

someModifiers <BtnDown1>: string(0x1b) string("[M1") dired-button()\n\
someModifiers <BtnDown2>: string(0x1b) string("[M2") dired-button()\n\
someModifiers <BtnDown3>: string(0x1b) string("[M3") dired-button()\n\

and modify the code of zle-xterm-mouse so that it recognizes
those new sequences.

-- 
Stéphane


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse support
  2004-11-11 16:22   ` Stephane Chazelas
@ 2004-11-11 17:47     ` Bart Schaefer
  2004-11-11 18:05       ` Stephane Chazelas
  2004-11-11 18:22       ` Stephane Chazelas
  0 siblings, 2 replies; 13+ messages in thread
From: Bart Schaefer @ 2004-11-11 17:47 UTC (permalink / raw)
  To: Stephane Chazelas; +Cc: Zsh users list

On Thu, 11 Nov 2004, Stephane Chazelas wrote:

> In that new one, I tried to address the tab/newline issue.
> It tries to calculate the length of the last line of the prompt,
> it may fail if PS1 contains %%s or %{ %{ ... %} %} or if two
> consecutive expansions of PS1 may not generate the same thing

There's some code in Functions/Prompts/prompt_bart_setup in the function
prompt_bart_precmd that computes the width of prompt strings.  It's been
specialized to expect to compute only the width of the first line of a
two-line PS1, but it shouldn't be hard to adapt.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse support
  2004-11-11 17:47     ` Bart Schaefer
@ 2004-11-11 18:05       ` Stephane Chazelas
  2004-11-11 18:22       ` Stephane Chazelas
  1 sibling, 0 replies; 13+ messages in thread
From: Stephane Chazelas @ 2004-11-11 18:05 UTC (permalink / raw)
  To: Zsh users list

On Thu, Nov 11, 2004 at 09:47:02AM -0800, Bart Schaefer wrote:
> On Thu, 11 Nov 2004, Stephane Chazelas wrote:
> 
> > In that new one, I tried to address the tab/newline issue.
> > It tries to calculate the length of the last line of the prompt,
> > it may fail if PS1 contains %%s or %{ %{ ... %} %} or if two
> > consecutive expansions of PS1 may not generate the same thing
> 
> There's some code in Functions/Prompts/prompt_bart_setup in the function
> prompt_bart_precmd that computes the width of prompt strings.  It's been
> specialized to expect to compute only the width of the first line of a
> two-line PS1, but it shouldn't be hard to adapt.

Hi Bart,

we actually chose the same approach, it suffers from the same
problems:

zero='%([BSUbsu]|{*%})'

It replaces '%%some %{%{some%}%}' with '%ome %}' instead of
'%%some '

Thanks for the (%%) flag, though.

-- 
Stéphane


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse support
  2004-11-11 17:47     ` Bart Schaefer
  2004-11-11 18:05       ` Stephane Chazelas
@ 2004-11-11 18:22       ` Stephane Chazelas
  2004-11-12  1:02         ` Bart Schaefer
  1 sibling, 1 reply; 13+ messages in thread
From: Stephane Chazelas @ 2004-11-11 18:22 UTC (permalink / raw)
  To: Zsh users list

On Thu, Nov 11, 2004 at 09:47:02AM -0800, Bart Schaefer wrote:
> On Thu, 11 Nov 2004, Stephane Chazelas wrote:
> 
> > In that new one, I tried to address the tab/newline issue.
> > It tries to calculate the length of the last line of the prompt,
> > it may fail if PS1 contains %%s or %{ %{ ... %} %} or if two
> > consecutive expansions of PS1 may not generate the same thing
> 
> There's some code in Functions/Prompts/prompt_bart_setup in the function
> prompt_bart_precmd that computes the width of prompt strings.  It's been
> specialized to expect to compute only the width of the first line of a
> two-line PS1, but it shouldn't be hard to adapt.

Well the (%%) expansion flag has another problem:

var='%Sfoo%s'
PS1='$var'

In that case, you need to expand first the variables in PS1 or
you'll miss the %S %s visual sequences.

-- 
Stéphane


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse support
  2004-11-11 18:22       ` Stephane Chazelas
@ 2004-11-12  1:02         ` Bart Schaefer
  2004-11-12  9:22           ` Stephane Chazelas
  0 siblings, 1 reply; 13+ messages in thread
From: Bart Schaefer @ 2004-11-12  1:02 UTC (permalink / raw)
  To: Stephane Chazelas; +Cc: Zsh users list

On Thu, 11 Nov 2004, Stephane Chazelas wrote:

> zero='%([BSUbsu]|{*%})'
> 
> It replaces '%%some %{%{some%}%}' with '%ome %}' instead of
> '%%some '

Hmm.  To fix that, you need extended globbing:

setopt extendedglob
zero='(#b)([^%]|(#s))%([BSUbsu]|{*%})'
print ${(%%)PS1//$~zero/$match[1]}

On Thu, 11 Nov 2004, Stephane Chazelas wrote:

> Well the (%%) expansion flag has another problem:
> 
> var='%Sfoo%s'
> PS1='$var'
> 
> In that case, you need to expand first the variables in PS1 or
> you'll miss the %S %s visual sequences.

That's easy enough:

if [[ -o promptsubst ]]
then print ${(%%)${(e)PS1}//$~zero/$match[1]}
else print ${(%%)PS1//$~zero/$match[1]}
fi


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse support
  2004-11-12  1:02         ` Bart Schaefer
@ 2004-11-12  9:22           ` Stephane Chazelas
  0 siblings, 0 replies; 13+ messages in thread
From: Stephane Chazelas @ 2004-11-12  9:22 UTC (permalink / raw)
  To: Zsh users list

On Thu, Nov 11, 2004 at 05:02:47PM -0800, Bart Schaefer wrote:
> On Thu, 11 Nov 2004, Stephane Chazelas wrote:
> 
> > zero='%([BSUbsu]|{*%})'
> > 
> > It replaces '%%some %{%{some%}%}' with '%ome %}' instead of
> > '%%some '
> 
> Hmm.  To fix that, you need extended globbing:
> 
> setopt extendedglob
> zero='(#b)([^%]|(#s))%([BSUbsu]|{*%})'
> print ${(%%)PS1//$~zero/$match[1]}
[...]

That moves the problem (the first one)

ow, there's a problem with '%%%S'

A better fix:

zero='(#b)(%([BSUbsu]|{*%})|(%[^BSUbsu{}]))'
print ${(S%%)PS1//$~zero/$match[3]}

But, it still doesn't fix the problem of nested %{ (maybe not
too big an issue, as I can't think of any case where nesting
%{'s can be useful).

> On Thu, 11 Nov 2004, Stephane Chazelas wrote:
> 
> > Well the (%%) expansion flag has another problem:
> > 
> > var='%Sfoo%s'
> > PS1='$var'
> > 
> > In that case, you need to expand first the variables in PS1 or
> > you'll miss the %S %s visual sequences.
> 
> That's easy enough:
> 
> if [[ -o promptsubst ]]
> then print ${(%%)${(e)PS1}//$~zero/$match[1]}
> else print ${(%%)PS1//$~zero/$match[1]}
> fi

Yes, that was what I did. Except that I used (%) instead of
(%%). But with (%%) PS1 may be expanded again (PS1='$(echo "\$foo")')

So a refined solution could be:

    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]}


what do you think?

-- 
Stéphane


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [tip] mouse and mouse-wheel support!
  2004-11-11 12:20 [tip] mouse support Stephane Chazelas
  2004-11-11 14:40 ` Peter Stephenson
  2004-11-11 16:07 ` Andy Spiegl
@ 2004-11-12 12:02 ` Stephane Chazelas
  2004-11-15 12:21   ` Stephane Chazelas
  2 siblings, 1 reply; 13+ messages in thread
From: Stephane Chazelas @ 2004-11-12 12:02 UTC (permalink / raw)
  To: Zsh users list

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


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse and mouse-wheel support!
  2004-11-12 12:02 ` [tip] mouse and mouse-wheel support! Stephane Chazelas
@ 2004-11-15 12:21   ` Stephane Chazelas
  2004-11-15 13:14     ` Stephane Chazelas
  0 siblings, 1 reply; 13+ messages in thread
From: Stephane Chazelas @ 2004-11-15 12:21 UTC (permalink / raw)
  To: Zsh users list

On Fri, Nov 12, 2004 at 12:02:48PM +0000, Stephane Chazelas wrote:
> Ok, thanks to the help of Peter, Bart and Andy, here is a new
> version.
[...]

Yet another one, please test it:

- fix problem when a ^M like char is spanned on two lines
- works within "select" and zed and zcalc
- fixed typo bug ($'\0200' should be $'\200')
- now works within vared as long as there is no prompt (-p vared
  option) because I didn't know how to get vared prompt.

(again, either set ZLE_USE_MOUSE to 1 or add 
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)
to your resource file to have <Super-btnClick> do the stuff).

### code begins
if [[ $TERM = *xterm* || $TERM = *rxvt* || $TERM = *screen* ]]; 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

    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

    [[ -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 n x=0 y=1 cursor=$((${#cur_prompt}+$CURSOR+1))
    local Y

    set -x
    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 $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 ends

-- 
Stéphane


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [tip] mouse and mouse-wheel support!
  2004-11-15 12:21   ` Stephane Chazelas
@ 2004-11-15 13:14     ` Stephane Chazelas
  0 siblings, 0 replies; 13+ messages in thread
From: Stephane Chazelas @ 2004-11-15 13:14 UTC (permalink / raw)
  To: Zsh users list

On Mon, Nov 15, 2004 at 12:21:34PM +0000, Stephane Chazelas wrote:
[...]
> 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)
[...]

I've experienced weird xterm behaviors with the above, this one
works better:

XTerm.VT100.translations:             #override\
        Mod4 <Btn1Down>: ignore()\n\
        Mod4 <Btn2Down>: ignore()\n\
        Mod4 <Btn3Down>: ignore()\n\
        Mod4 <Btn1Up>: string(0x1b) string("[M ") dired-button()\n\
        Mod4 <Btn2Up>: string(0x1b) string("[M!") dired-button()\n\
        Mod4 <Btn3Up>: string(0x1b) string("[M") string(0x22) dired-button()\n\
        Mod4 <Btn5Down>,<Btn5Up>: string(0xe)\n\
        Mod4 <Btn4Down>,<Btn4Up>: string(0x10)

-- 
Stéphane


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2004-11-15 13:17 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-11-11 12:20 [tip] mouse support Stephane Chazelas
2004-11-11 14:40 ` Peter Stephenson
2004-11-11 16:22   ` Stephane Chazelas
2004-11-11 17:47     ` Bart Schaefer
2004-11-11 18:05       ` Stephane Chazelas
2004-11-11 18:22       ` Stephane Chazelas
2004-11-12  1:02         ` Bart Schaefer
2004-11-12  9:22           ` Stephane Chazelas
2004-11-11 16:07 ` Andy Spiegl
2004-11-11 17:26   ` Stephane Chazelas
2004-11-12 12:02 ` [tip] mouse and mouse-wheel support! Stephane Chazelas
2004-11-15 12:21   ` Stephane Chazelas
2004-11-15 13:14     ` Stephane Chazelas

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).