github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/edit/tty/reader_unix.go (about)

     1  // +build !windows,!plan9
     2  
     3  package tty
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"time"
     9  
    10  	"github.com/u-root/u-root/cmds/core/elvish/edit/ui"
    11  )
    12  
    13  // DefaultSeqTimeout is the amount of time within which runes that make up an
    14  // escape sequence are supposed to follow each other. Modern terminal emulators
    15  // send escape sequences very fast, so 10ms is more than sufficient. SSH
    16  // connections on a slow link might be problematic though.
    17  const DefaultSeqTimeout = 10 * time.Millisecond
    18  
    19  // reader reads terminal escape sequences and decodes them into events.
    20  type reader struct {
    21  	ar         *runeReader
    22  	seqTimeout time.Duration
    23  	raw        bool
    24  
    25  	eventChan   chan Event
    26  	stopChan    chan struct{}
    27  	stopAckChan chan struct{}
    28  }
    29  
    30  func newReader(f *os.File) *reader {
    31  	rd := &reader{
    32  		newRuneReader(f),
    33  		DefaultSeqTimeout,
    34  		false,
    35  		make(chan Event),
    36  		nil,
    37  		nil,
    38  	}
    39  	return rd
    40  }
    41  
    42  // SetRaw turns the raw option on or off. If the reader is in the middle of
    43  // reading one event, it takes effect after this event is fully read.
    44  func (rd *reader) SetRaw(raw bool) {
    45  	rd.raw = raw
    46  }
    47  
    48  // EventChan returns the channel onto which the Reader writes what it has read.
    49  func (rd *reader) EventChan() <-chan Event {
    50  	return rd.eventChan
    51  }
    52  
    53  // Start starts the Reader.
    54  func (rd *reader) Start() {
    55  	rd.stopChan = make(chan struct{})
    56  	rd.stopAckChan = make(chan struct{})
    57  	rd.ar.Start()
    58  	go rd.run()
    59  }
    60  
    61  func (rd *reader) run() {
    62  	// NOTE: Stop may be called at any time. All channel reads and sends should
    63  	// be wrapped in a select and have a "case <-rd.stopChan" clause.
    64  	for {
    65  		select {
    66  		case r := <-rd.ar.Chan():
    67  			if rd.raw {
    68  				rd.send(RawRune(r))
    69  			} else {
    70  				event, seqError, ioError := rd.readOne(r)
    71  				if event != nil {
    72  					rd.send(event)
    73  				}
    74  				if seqError != nil {
    75  					rd.send(NonfatalErrorEvent{seqError})
    76  				}
    77  				if ioError != nil {
    78  					rd.send(FatalErrorEvent{ioError})
    79  					<-rd.stopChan
    80  				}
    81  			}
    82  		case err := <-rd.ar.ErrorChan():
    83  			rd.send(FatalErrorEvent{err})
    84  			<-rd.stopChan
    85  		case <-rd.stopChan:
    86  		}
    87  
    88  		select {
    89  		case <-rd.stopChan:
    90  			close(rd.stopAckChan)
    91  			return
    92  		default:
    93  		}
    94  	}
    95  }
    96  
    97  // send tries to send an event, unless stop was requested. If stop was requested
    98  // before, it does nothing; hence it is safe to use after stop.
    99  func (rd *reader) send(event Event) {
   100  	select {
   101  	case rd.eventChan <- event:
   102  	case <-rd.stopChan:
   103  	}
   104  }
   105  
   106  // Stop stops the Reader.
   107  func (rd *reader) Stop() {
   108  	rd.ar.Stop()
   109  	close(rd.stopChan)
   110  	<-rd.stopAckChan
   111  }
   112  
   113  // Close releases files associated with the Reader. It does not close the file
   114  // used to create it.
   115  func (rd *reader) Close() {
   116  	rd.ar.Close()
   117  }
   118  
   119  // Used by readRune in readOne to signal end of current sequence.
   120  const runeEndOfSeq rune = -1
   121  
   122  // readOne attempts to read one key or CPR, led by a rune already read.
   123  func (rd *reader) readOne(r rune) (event Event, seqError, ioError error) {
   124  	currentSeq := string(r)
   125  
   126  	badSeq := func(msg string) {
   127  		seqError = fmt.Errorf("%s: %q", msg, currentSeq)
   128  	}
   129  
   130  	// readRune attempts to read a rune within a timeout of EscSequenceTimeout.
   131  	// It may return runeEndOfSeq when the read timed out, an error was
   132  	// encountered (in which case it sets ioError) or when stopped. In all three
   133  	// cases, the reader should terminate the current sequence. If the current
   134  	// sequence is valid, it should set event. If not, it should set seqError by
   135  	// calling badSeq.
   136  	readRune :=
   137  		func() rune {
   138  			select {
   139  			case r := <-rd.ar.Chan():
   140  				currentSeq += string(r)
   141  				return r
   142  			case ioError = <-rd.ar.ErrorChan():
   143  				return runeEndOfSeq
   144  			case <-time.After(rd.seqTimeout):
   145  				return runeEndOfSeq
   146  			case <-rd.stopChan:
   147  				return runeEndOfSeq
   148  			}
   149  		}
   150  
   151  	switch r {
   152  	case 0x1b: // ^[ Escape
   153  		r2 := readRune()
   154  		// According to https://unix.stackexchange.com/a/73697, rxvt and derivatives
   155  		// prepend another ESC to a CSI-style or G3-style sequence to signal Alt.
   156  		// If that happens, remember this now; it will be later picked up when parsing
   157  		// those two kinds of sequences.
   158  		//
   159  		// issue #181
   160  		hasTwoLeadingESC := false
   161  		if r2 == 0x1b {
   162  			hasTwoLeadingESC = true
   163  			r2 = readRune()
   164  		}
   165  		if r2 == runeEndOfSeq {
   166  			// XXX Error is swallowed
   167  			// Nothing follows. Taken as a lone Escape.
   168  			event = KeyEvent{'[', ui.Ctrl}
   169  			break
   170  		}
   171  		switch r2 {
   172  		case '[':
   173  			// A '[' follows. CSI style function key sequence.
   174  			r = readRune()
   175  			if r == runeEndOfSeq {
   176  				event = KeyEvent{'[', ui.Alt}
   177  				return
   178  			}
   179  
   180  			nums := make([]int, 0, 2)
   181  			var starter rune
   182  
   183  			// Read an optional starter.
   184  			switch r {
   185  			case '<':
   186  				starter = r
   187  				r = readRune()
   188  			case 'M':
   189  				// Mouse event.
   190  				cb := readRune()
   191  				if cb == runeEndOfSeq {
   192  					badSeq("Incomplete mouse event")
   193  					return
   194  				}
   195  				cx := readRune()
   196  				if cx == runeEndOfSeq {
   197  					badSeq("Incomplete mouse event")
   198  					return
   199  				}
   200  				cy := readRune()
   201  				if cy == runeEndOfSeq {
   202  					badSeq("Incomplete mouse event")
   203  					return
   204  				}
   205  				down := true
   206  				button := int(cb & 3)
   207  				if button == 3 {
   208  					down = false
   209  					button = -1
   210  				}
   211  				mod := mouseModify(int(cb))
   212  				event = MouseEvent{
   213  					Pos{int(cy) - 32, int(cx) - 32}, down, button, mod}
   214  				return
   215  			}
   216  		CSISeq:
   217  			for {
   218  				switch {
   219  				case r == ';':
   220  					nums = append(nums, 0)
   221  				case '0' <= r && r <= '9':
   222  					if len(nums) == 0 {
   223  						nums = append(nums, 0)
   224  					}
   225  					cur := len(nums) - 1
   226  					nums[cur] = nums[cur]*10 + int(r-'0')
   227  				case r == runeEndOfSeq:
   228  					// Incomplete CSI.
   229  					badSeq("Incomplete CSI")
   230  					return
   231  				default: // Treat as a terminator.
   232  					break CSISeq
   233  				}
   234  
   235  				r = readRune()
   236  			}
   237  			if starter == 0 && r == 'R' {
   238  				// Cursor position report.
   239  				if len(nums) != 2 {
   240  					badSeq("bad CPR")
   241  					return
   242  				}
   243  				event = CursorPosition{nums[0], nums[1]}
   244  			} else if starter == '<' && (r == 'm' || r == 'M') {
   245  				// SGR-style mouse event.
   246  				if len(nums) != 3 {
   247  					badSeq("bad SGR mouse event")
   248  					return
   249  				}
   250  				down := r == 'M'
   251  				button := nums[0] & 3
   252  				mod := mouseModify(nums[0])
   253  				event = MouseEvent{Pos{nums[2], nums[1]}, down, button, mod}
   254  			} else if r == '~' && len(nums) == 1 && (nums[0] == 200 || nums[0] == 201) {
   255  				b := nums[0] == 200
   256  				event = PasteSetting(b)
   257  			} else {
   258  				k := parseCSI(nums, r, currentSeq)
   259  				if k == (ui.Key{}) {
   260  					badSeq("bad CSI")
   261  				} else {
   262  					if hasTwoLeadingESC {
   263  						k.Mod |= ui.Alt
   264  					}
   265  					event = KeyEvent(k)
   266  				}
   267  			}
   268  		case 'O':
   269  			// An 'O' follows. G3 style function key sequence: read one rune.
   270  			r = readRune()
   271  			if r == runeEndOfSeq {
   272  				// Nothing follows after 'O'. Taken as Alt-o.
   273  				event = KeyEvent{'o', ui.Alt}
   274  				return
   275  			}
   276  			k, ok := g3Seq[r]
   277  			if ok {
   278  				if hasTwoLeadingESC {
   279  					k.Mod |= ui.Alt
   280  				}
   281  				event = KeyEvent(k)
   282  			} else {
   283  				badSeq("bad G3")
   284  			}
   285  		default:
   286  			// Something other than '[' or 'O' follows. Taken as an
   287  			// Alt-modified key, possibly also modified by Ctrl.
   288  			k := ctrlModify(r2)
   289  			k.Mod |= ui.Alt
   290  			event = KeyEvent(k)
   291  		}
   292  	default:
   293  		event = KeyEvent(ctrlModify(r))
   294  	}
   295  	return
   296  }
   297  
   298  // ctrlModify determines whether a rune corresponds to a Ctrl-modified key and
   299  // returns the ui.Key the rune represents.
   300  func ctrlModify(r rune) ui.Key {
   301  	switch r {
   302  	case 0x0:
   303  		return ui.Key{'`', ui.Ctrl} // ^@
   304  	case 0x1e:
   305  		return ui.Key{'6', ui.Ctrl} // ^^
   306  	case 0x1f:
   307  		return ui.Key{'/', ui.Ctrl} // ^_
   308  	case ui.Tab, ui.Enter, ui.Backspace: // ^I ^J ^?
   309  		return ui.Key{r, 0}
   310  	default:
   311  		// Regular ui.Ctrl sequences.
   312  		if 0x1 <= r && r <= 0x1d {
   313  			return ui.Key{r + 0x40, ui.Ctrl}
   314  		}
   315  	}
   316  	return ui.Key{r, 0}
   317  }
   318  
   319  // Tables for key sequences. Comments document which terminal emulators are
   320  // known to generate which sequences. The terminal emulators tested are
   321  // categorized into xterm (including actual xterm, libvte-based terminals,
   322  // Konsole and Terminal.app unless otherwise noted), urxvt, tmux.
   323  
   324  // G3-style key sequences: \eO followed by exactly one character. For instance,
   325  // \eOP is F1. These are pretty limited in that they cannot be extended to
   326  // support modifier keys, other than a leading \e for Alt (e.g. \e\eOP is
   327  // Alt-F1). Terminals that send G3-style key sequences typically switch to
   328  // sending a CSI-style key sequence when a non-Alt modifier key is pressed.
   329  var g3Seq = map[rune]ui.Key{
   330  	// xterm, tmux -- only in Vim, depends on termios setting?
   331  	// NOTE(xiaq): According to urxvt's manpage, \eO[ABCD] sequences are used for
   332  	// Ctrl-Shift-modified arrow keys; however, this doesn't seem to be true for
   333  	// urxvt 9.22 packaged by Debian; those keys simply send the same sequence
   334  	// as Ctrl-modified keys (\eO[abcd]).
   335  	'A': {ui.Up, 0}, 'B': {ui.Down, 0}, 'C': {ui.Right, 0}, 'D': {ui.Left, 0},
   336  	'H': {ui.Home, 0}, 'F': {ui.End, 0}, 'M': {ui.Insert, 0},
   337  	// urxvt
   338  	'a': {ui.Up, ui.Ctrl}, 'b': {ui.Down, ui.Ctrl},
   339  	'c': {ui.Right, ui.Ctrl}, 'd': {ui.Left, ui.Ctrl},
   340  	// xterm, urxvt, tmux
   341  	'P': {ui.F1, 0}, 'Q': {ui.F2, 0}, 'R': {ui.F3, 0}, 'S': {ui.F4, 0},
   342  }
   343  
   344  // Tables for CSI-style key sequences. A CSI sequence is \e[ followed by zero or
   345  // more numerical arguments (separated by semicolons), ending in a non-numeric,
   346  // non-semicolon rune. They are used for many purposes, and CSI-style key
   347  // sequences are a subset of them.
   348  //
   349  // There are several variants of CSI-style key sequences; see comments above the
   350  // respective tables. In all variants, modifier keys are encoded in numerical
   351  // arguments; see xtermModify. Note that although the set of possible sequences
   352  // make it possible to express a very complete set of key combinations, they are
   353  // not always sent by terminals. For instance, many (if not most) terminals will
   354  // send the same sequence for Up when Shift-Up is pressed, even if Shift-Up is
   355  // expressible using the escape sequences described below.
   356  
   357  // CSI-style key sequences identified by the last rune. For instance, \e[A is
   358  // Up. When modified, two numerical arguments are added, the first always beging
   359  // 1 and the second identifying the modifier. For instance, \e1;5A is Ctrl-Up.
   360  var csiSeqByLast = map[rune]ui.Key{
   361  	// xterm, urxvt, tmux
   362  	'A': {ui.Up, 0}, 'B': {ui.Down, 0}, 'C': {ui.Right, 0}, 'D': {ui.Left, 0},
   363  	// urxvt
   364  	'a': {ui.Up, ui.Shift}, 'b': {ui.Down, ui.Shift},
   365  	'c': {ui.Right, ui.Shift}, 'd': {ui.Left, ui.Shift},
   366  	// xterm (Terminal.app only sends those in alternate screen)
   367  	'H': {ui.Home, 0}, 'F': {ui.End, 0},
   368  	// xterm, urxvt, tmux
   369  	'Z': {ui.Tab, ui.Shift},
   370  }
   371  
   372  // CSI-style key sequences ending with '~' and identified by one numerical
   373  // argument. For instance, \e[3~ is Delete. When modified, an additional
   374  // argument identifies the modifier; for instance, \e[3;5~ is Ctrl-Delete.
   375  //
   376  // An alternative encoding of the modifier key, only known to be used by urxvt
   377  // (or for that matter, likely also rxvt) is to change the last rune: '$' for
   378  // Shift, '^' for Ctrl, and '@' for Ctrl+Shift. The numeric argument is kept
   379  // unchanged. For instance, \e[3^ is Ctrl-Delete.
   380  var csiSeqTilde = map[int]rune{
   381  	// tmux (NOTE: urxvt uses the pair for Find/Select)
   382  	1: ui.Home, 4: ui.End,
   383  	// xterm (Terminal.app sends ^M for Fn+Enter), urxvt, tmux
   384  	2: ui.Insert,
   385  	// xterm, urxvt, tmux
   386  	3: ui.Delete,
   387  	// xterm (Terminal.app only sends those in alternate screen), urxvt, tmux
   388  	// NOTE: called Prior/Next in urxvt manpage
   389  	5: ui.PageUp, 6: ui.PageDown,
   390  	// urxvt
   391  	7: ui.Home, 8: ui.End,
   392  	// urxvt
   393  	11: ui.F1, 12: ui.F2, 13: ui.F3, 14: ui.F4,
   394  	// xterm, urxvt, tmux
   395  	// NOTE: 16 and 22 are unused
   396  	15: ui.F5, 17: ui.F6, 18: ui.F7, 19: ui.F8,
   397  	20: ui.F9, 21: ui.F10, 23: ui.F11, 24: ui.F12,
   398  }
   399  
   400  // CSI-style key sequences ending with '~', with the first argument always 27,
   401  // the second argument identifying the modifier, and the third argument
   402  // identifying the key. For instance, \e[27;5;9~ is Ctrl-Tab.
   403  //
   404  // NOTE(xiaq): The list is taken blindly from xterm-keys.c in the tmux source
   405  // tree. I do not have a keyboard-terminal combination that generate such
   406  // sequences, but assumably they are generated by some terminals for numpad
   407  // inputs.
   408  var csiSeqTilde27 = map[int]rune{
   409  	9: '\t', 13: '\r',
   410  	33: '!', 35: '#', 39: '\'', 40: '(', 41: ')', 43: '+', 44: ',', 45: '-',
   411  	46: '.',
   412  	48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7',
   413  	56: '8', 57: '9',
   414  	58: ':', 59: ';', 60: '<', 61: '=', 62: '>', 63: ';',
   415  }
   416  
   417  // parseCSI parses a CSI-style key sequence. See comments above for all the 3
   418  // variants this function handles.
   419  func parseCSI(nums []int, last rune, seq string) ui.Key {
   420  	if k, ok := csiSeqByLast[last]; ok {
   421  		if len(nums) == 0 {
   422  			// Unmodified: \e[A (Up)
   423  			return k
   424  		} else if len(nums) == 2 && nums[0] == 1 {
   425  			// Modified: \e[1;5A (Ctrl-Up)
   426  			return xtermModify(k, nums[1], seq)
   427  		} else {
   428  			return ui.Key{}
   429  		}
   430  	}
   431  
   432  	switch last {
   433  	case '~':
   434  		if len(nums) == 1 || len(nums) == 2 {
   435  			if r, ok := csiSeqTilde[nums[0]]; ok {
   436  				k := ui.Key{r, 0}
   437  				if len(nums) == 1 {
   438  					// Unmodified: \e[5~ (e.g. PageUp)
   439  					return k
   440  				}
   441  				// Modified: \e[5;5~ (e.g. Ctrl-PageUp)
   442  				return xtermModify(k, nums[1], seq)
   443  			}
   444  		} else if len(nums) == 3 && nums[0] == 27 {
   445  			if r, ok := csiSeqTilde27[nums[2]]; ok {
   446  				k := ui.Key{r, 0}
   447  				return xtermModify(k, nums[1], seq)
   448  			}
   449  		}
   450  	case '$', '^', '@':
   451  		// Modified by urxvt; see comment above csiSeqTilde.
   452  		if len(nums) == 1 {
   453  			if r, ok := csiSeqTilde[nums[0]]; ok {
   454  				var mod ui.Mod
   455  				switch last {
   456  				case '$':
   457  					mod = ui.Shift
   458  				case '^':
   459  					mod = ui.Ctrl
   460  				case '@':
   461  					mod = ui.Shift | ui.Ctrl
   462  				}
   463  				return ui.Key{r, mod}
   464  			}
   465  		}
   466  	}
   467  
   468  	return ui.Key{}
   469  }
   470  
   471  func xtermModify(k ui.Key, mod int, seq string) ui.Key {
   472  	switch mod {
   473  	case 0:
   474  		// do nothing
   475  	case 2:
   476  		k.Mod |= ui.Shift
   477  	case 3:
   478  		k.Mod |= ui.Alt
   479  	case 4:
   480  		k.Mod |= ui.Shift | ui.Alt
   481  	case 5:
   482  		k.Mod |= ui.Ctrl
   483  	case 6:
   484  		k.Mod |= ui.Shift | ui.Ctrl
   485  	case 7:
   486  		k.Mod |= ui.Alt | ui.Ctrl
   487  	case 8:
   488  		k.Mod |= ui.Shift | ui.Alt | ui.Ctrl
   489  	default:
   490  		return ui.Key{}
   491  	}
   492  	return k
   493  }
   494  
   495  func mouseModify(n int) ui.Mod {
   496  	var mod ui.Mod
   497  	if n&4 != 0 {
   498  		mod |= ui.Shift
   499  	}
   500  	if n&8 != 0 {
   501  		mod |= ui.Alt
   502  	}
   503  	if n&16 != 0 {
   504  		mod |= ui.Ctrl
   505  	}
   506  	return mod
   507  }