package terminal import "github.com/gdamore/tcell/v2" // HandleKey sends a key press to the shell. // // Shift with Page Up or Page Down reads back through the history instead, // which is where every terminal puts that. Everything else goes to the shell, // and brings the view back to the live screen on the way — a terminal that // stayed in its history while you typed would hide your own command. // // Once the program has gone, only the scrolling keys are still taken. A dead // terminal that went on swallowing keys could never be closed with Ctrl-W: the // write would fail silently and the key would be consumed anyway, leaving the // mouse as the only way out of a window whose command has finished. func (v *View) HandleKey(ev *tcell.EventKey) bool { if !v.Focused() { return false } if v.handleScrollKey(ev) { return true } if v.Exited() { return false } sequence := Encode(ev, v.applicationCursor()) if len(sequence) == 0 { return false } v.ScrollToBottom() // A write that fails means the shell has gone, which the reading goroutine // is about to report; there is nothing useful to do with the error here. _, _ = v.session.Write(sequence) return true } // handleScrollKey deals with the keys that read back through the history. func (v *View) handleScrollKey(ev *tcell.EventKey) bool { if ev.Modifiers()&tcell.ModShift == 0 { return false } page := max(v.Bounds().H-1, 1) switch ev.Key() { case tcell.KeyPgUp: v.ScrollBy(page) case tcell.KeyPgDn: v.ScrollBy(-page) default: return false } return true } // applicationCursor reports the mode the program has asked for, under the lock. func (v *View) applicationCursor() bool { v.mu.Lock() defer v.mu.Unlock() return v.parser.Screen().ApplicationCursor() } // HandleMouse scrolls back through the history with the wheel. // // Clicks are not forwarded: mouse reporting is a mode this emulator does not // implement, so a program has no reason to expect them. func (v *View) HandleMouse(ev *tcell.EventMouse) bool { if !v.hitTest(ev) { return false } switch ev.Buttons() { case tcell.WheelUp: v.ScrollBy(wheelStep) case tcell.WheelDown: v.ScrollBy(-wheelStep) case tcell.Button1: // A click inside the window is claimed so that it counts as focusing // the window rather than falling through to the desktop. default: return false } return true } // hitTest reports whether a mouse event landed on the view. func (v *View) hitTest(ev *tcell.EventMouse) bool { x, y := ev.Position() return v.Bounds().Contains(x, y) }