9 "github.com/zyedidia/clipboard"
10 "github.com/zyedidia/tcell"
11 "github.com/zyedidia/terminal"
15 VTIdle = iota // Waiting for a new command
16 VTRunning // Currently running a command
17 VTDone // Finished running a command
20 // A Terminal holds information for the terminal emulator
21 type Terminal struct {
32 // HasSelection returns whether this terminal has a valid selection
33 func (t *Terminal) HasSelection() bool {
34 return t.selection[0] != t.selection[1]
37 // GetSelection returns the selected text
38 func (t *Terminal) GetSelection(width int) string {
39 start := t.selection[0]
41 if start.GreaterThan(end) {
42 start, end = end, start
46 for y := start.Y; y <= end.Y; y++ {
47 for x := 0; x < width; x++ {
49 if l.GreaterEqual(start) && l.LessThan(end) {
50 c, _, _ := t.state.Cell(x, y)
58 // Start begins a new command in this terminal with a given view
59 func (t *Terminal) Start(execCmd []string, view *View) error {
60 if len(execCmd) <= 0 {
64 cmd := exec.Command(execCmd[0], execCmd[1:]...)
65 term, _, err := terminal.Start(&t.state, cmd)
73 t.title = execCmd[0] + ":" + strconv.Itoa(cmd.Process.Pid)
79 fmt.Fprintln(os.Stderr, "[Press enter to close]")
90 // Resize informs the terminal of a resize event
91 func (t *Terminal) Resize(width, height int) {
92 t.term.Resize(width, height)
95 // HandleEvent handles a tcell event by forwarding it to the terminal emulator
96 // If the event is a mouse event and the program running in the emulator
97 // does not have mouse support, the emulator will support selections and
99 func (t *Terminal) HandleEvent(event tcell.Event) {
100 if e, ok := event.(*tcell.EventKey); ok {
101 if t.status == VTDone {
103 case tcell.KeyEscape, tcell.KeyCtrlQ, tcell.KeyEnter:
105 t.view.Type = vtDefault
109 if e.Key() == tcell.KeyCtrlC && t.HasSelection() {
110 clipboard.WriteAll(t.GetSelection(t.view.Width), "clipboard")
111 messenger.Message("Copied selection to clipboard")
112 } else if t.status != VTDone {
113 t.WriteString(event.EscSeq())
115 } else if e, ok := event.(*tcell.EventMouse); !ok || t.state.Mode(terminal.ModeMouseMask) {
116 t.WriteString(event.EscSeq())
122 if e.Buttons() == tcell.Button1 {
123 if !t.view.mouseReleased {
134 t.view.mouseReleased = false
135 } else if e.Buttons() == tcell.ButtonNone {
136 if !t.view.mouseReleased {
140 t.view.mouseReleased = true
145 // Stop stops execution of the terminal and sets the status
147 func (t *Terminal) Stop() {
148 t.term.File().Close()
154 t.view.Type = t.vtOld
158 // Close sets the status to VTIdle indicating that the terminal
159 // is ready for a new command to execute
160 func (t *Terminal) Close() {
164 // WriteString writes a given string to this terminal's pty
165 func (t *Terminal) WriteString(str string) {
166 t.term.File().WriteString(str)
169 // Display displays this terminal in a view
170 func (t *Terminal) Display() {
174 dividerStyle := defStyle
175 if style, ok := colorscheme["divider"]; ok {
178 for i := 0; i < t.view.Height; i++ {
179 screen.SetContent(t.view.x, t.view.y+i, '|', nil, dividerStyle.Reverse(true))
183 defer t.state.Unlock()
186 for y := 0; y < t.view.Height; y++ {
187 for x := 0; x < t.view.Width; x++ {
189 c, f, b := t.state.Cell(x, y)
191 fg, bg := int(f), int(b)
192 if f == terminal.DefaultFG {
193 fg = int(tcell.ColorDefault)
195 if b == terminal.DefaultBG {
196 bg = int(tcell.ColorDefault)
198 st := tcell.StyleDefault.Foreground(GetColor256(int(fg))).Background(GetColor256(int(bg)))
200 if l.LessThan(t.selection[1]) && l.GreaterEqual(t.selection[0]) || l.LessThan(t.selection[0]) && l.GreaterEqual(t.selection[1]) {
201 st = st.Reverse(true)
204 screen.SetContent(t.view.x+x+divider, t.view.y+y, c, nil, st)
207 if t.state.CursorVisible() && tabs[curTab].CurView == t.view.Num {
208 curx, cury := t.state.Cursor()
209 screen.ShowCursor(curx+t.view.x+divider, cury+t.view.y)