forked from gdamore/tcell
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tscreen_unix.go
133 lines (112 loc) · 3.36 KB
/
tscreen_unix.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2021 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package tcell
import (
"errors"
"os"
"os/signal"
"syscall"
"golang.org/x/term"
)
// engage is used to place the terminal in raw mode and establish screen size, etc.
// Thing of this is as tcell "engaging" the clutch, as it's going to be driving the
// terminal interface.
func (t *tScreen) engage() error {
t.Lock()
defer t.Unlock()
if t.stopQ != nil {
return errors.New("already engaged")
}
if _, err := term.MakeRaw(int(t.in.Fd())); err != nil {
return err
}
if w, h, err := term.GetSize(int(t.in.Fd())); err == nil && w != 0 && h != 0 {
t.cells.Resize(w, h)
}
stopQ := make(chan struct{})
t.stopQ = stopQ
t.nonBlocking(false)
t.enableMouse(t.mouseFlags)
t.enablePasting(t.pasteEnabled)
signal.Notify(t.sigwinch, syscall.SIGWINCH)
ti := t.ti
t.TPuts(ti.EnterCA)
t.TPuts(ti.EnterKeypad)
t.TPuts(ti.HideCursor)
t.TPuts(ti.EnableAcs)
t.TPuts(ti.Clear)
t.wg.Add(2)
go t.inputLoop(stopQ)
go t.mainLoop(stopQ)
return nil
}
// disengage is used to release the terminal back to support from the caller.
// Think of this as tcell disengaging the clutch, so that another application
// can take over the terminal interface. This restores the TTY mode that was
// present when the application was first started.
func (t *tScreen) disengage() {
t.Lock()
t.nonBlocking(true)
stopQ := t.stopQ
t.stopQ = nil
close(stopQ)
t.Unlock()
// wait for everything to shut down
t.wg.Wait()
signal.Stop(t.sigwinch)
// put back normal blocking mode
t.nonBlocking(false)
// shutdown the screen and disable special modes (e.g. mouse and bracketed paste)
ti := t.ti
t.cells.Resize(0, 0)
t.TPuts(ti.ShowCursor)
t.TPuts(ti.AttrOff)
t.TPuts(ti.Clear)
t.TPuts(ti.ExitCA)
t.TPuts(ti.ExitKeypad)
t.enableMouse(0)
t.enablePasting(false)
// restore the termios that we were started with
_ = term.Restore(int(t.in.Fd()), t.saved)
}
// initialize is used at application startup, and sets up the initial values
// including file descriptors used for terminals and saving the initial state
// so that it can be restored when the application terminates.
func (t *tScreen) initialize() error {
var err error
t.out = os.Stdout
if t.in, err = os.Open("/dev/tty"); err != nil {
return err
}
t.saved, err = term.GetState(int(t.in.Fd()))
if err == nil {
return nil
}
return nil
}
// finalize is used to at application shutdown, and restores the terminal
// to it's initial state. It should not be called more than once.
func (t *tScreen) finalize() {
t.disengage()
}
// getWinSize is called to obtain the terminal dimensions.
func (t *tScreen) getWinSize() (int, int, error) {
return term.GetSize(int(t.in.Fd()))
}
// Beep emits a beep to the terminal.
func (t *tScreen) Beep() error {
t.writeString(string(byte(7)))
return nil
}