-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathattr.go
160 lines (144 loc) · 3.83 KB
/
attr.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package anansi
import (
"errors"
"fmt"
"image"
"os"
"syscall"
)
var errAttrNoFile = errors.New("anansi.Attr.ioctl: no File set")
// Attr implements Context-ual manipulation and interrogation of terminal
// state, using the termios IOCTLs and ANSI control sequences where possible.
type Attr struct {
file *os.File // XXX re-export
ownFile bool
orig syscall.Termios
cur syscall.Termios
raw bool
echo bool
}
// IsTerminal returns true only if the given file is attached to an interactive
// terminal.
func IsTerminal(f *os.File) bool {
return Attr{file: f}.IsTerminal()
}
// IsTerminal returns true only if both terminal input and output file handles
// are both connected to a valid terminal.
func (term *Term) IsTerminal() bool {
return IsTerminal(term.Input.File) &&
IsTerminal(term.Output.File)
}
// IsTerminal returns true only if the underlying file is attached to an
// interactive terminal.
func (at Attr) IsTerminal() bool {
_, err := at.getAttr()
return err == nil
}
// Size reads and returns the current terminal size.
func (at Attr) Size() (size image.Point, err error) {
return at.getSize()
}
// SetRaw controls whether the terminal should be in raw mode.
//
// Raw mode is suitable for full-screen terminal user interfaces, eliminating
// keyboard shortcuts for job control, echo, line buffering, and escape key
// debouncing.
func (at *Attr) SetRaw(raw bool) error {
if raw == at.raw {
return nil
}
at.raw = raw
if at.file == nil {
return nil
}
at.cur = at.modifyTermios(at.orig)
if err := at.setAttr(at.cur); err != nil {
return fmt.Errorf("failed to set termios attr: %v", err)
}
return nil
}
// SetEcho toggles input echoing mode, which is off by default in raw mode, and
// on in normal mode.
func (at *Attr) SetEcho(echo bool) error {
if echo == at.echo {
return nil
}
at.echo = echo
if at.file == nil {
return nil
}
if echo {
at.cur.Lflag |= syscall.ECHO
} else {
at.cur.Lflag &^= syscall.ECHO
}
if err := at.setAttr(at.cur); err != nil {
return fmt.Errorf("failed to set termios attr: %v", err)
}
return nil
}
func (at Attr) modifyTermios(attr syscall.Termios) syscall.Termios {
if at.raw {
// TODO read things like antirez's kilo notes again
// TODO naturalize / decompose
attr.Iflag &^= syscall.BRKINT | syscall.ICRNL | syscall.INPCK | syscall.ISTRIP | syscall.IXON
attr.Oflag &^= syscall.OPOST
attr.Cflag &^= syscall.CSIZE | syscall.PARENB
attr.Cflag |= syscall.CS8
attr.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG
attr.Cc[syscall.VMIN] = 1
attr.Cc[syscall.VTIME] = 0
}
if at.echo {
attr.Lflag |= syscall.ECHO
} else {
attr.Lflag &^= syscall.ECHO
}
return attr
}
// Enter default the Attr's file to the term's Output File, records its
// original termios attributes, and then applies termios attributes.
func (at *Attr) Enter(term *Term) (err error) {
if at.file == nil {
at.file = term.Output.File
at.ownFile = false
} else {
at.ownFile = true
}
if at.file == nil {
return nil
}
at.orig, err = at.getAttr()
if err != nil {
return fmt.Errorf("failed to get termios attrs: %v", err)
}
at.cur = at.modifyTermios(at.orig)
if err = at.setAttr(at.cur); err != nil {
return fmt.Errorf("failed to set termios attr: %v", err)
}
return nil
}
// Exit restores termios attributes, and clears the File pointer if it was set
// by Enter
func (at *Attr) Exit(term *Term) error {
if at.file == nil {
return nil
}
if err := at.setAttr(at.orig); err != nil {
return fmt.Errorf("failed to set termios attr: %v", err)
}
if !at.ownFile {
at.file = nil
at.ownFile = false
}
return nil
}
func (at Attr) ioctl(request, arg1, arg2, arg3, arg4 uintptr) error {
if at.file == nil {
return errAttrNoFile
}
if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, at.file.Fd(), request, arg1, arg2, arg3, arg4); e != 0 {
return e
}
return nil
}