mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-16 19:58:03 +01:00
Fix putting terminal into raw mode (fixes #92)
This commit is contained in:
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@@ -11,6 +11,10 @@
|
||||
"Comment": "null-213",
|
||||
"Rev": "aa2644fe4aa50e3b38d75187b4799b1f0c9ddcef"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/creack/termios/raw",
|
||||
"Rev": "d60649a6c40aa68303e3d69e0423a1e7aedf4bbd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docopt/docopt-go",
|
||||
"Comment": "0.6.1-1-gc5dac53",
|
||||
|
||||
62
Godeps/_workspace/src/github.com/creack/termios/raw/raw.go
generated
vendored
Normal file
62
Godeps/_workspace/src/github.com/creack/termios/raw/raw.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
package raw
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// TcSetAttr restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func TcSetAttr(fd uintptr, termios *Termios) error {
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(termios))); err != 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TcGetAttr retrieves the current terminal settings and returns it.
|
||||
func TcGetAttr(fd uintptr) (*Termios, error) {
|
||||
var termios = &Termios{}
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(termios))); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
return termios, nil
|
||||
}
|
||||
|
||||
// CfMakeRaw sets the flags stored in the termios structure to a state disabling
|
||||
// all input and output processing, giving a ``raw I/O path''.
|
||||
//
|
||||
// From man cfmakeraw(3) on linux:
|
||||
// termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
// termios_p->c_oflag &= ~OPOST;
|
||||
// termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
// termios_p->c_cflag &= ~(CSIZE | PARENB);
|
||||
// termios_p->c_cflag |= CS8;
|
||||
//
|
||||
func CfMakeRaw(termios *Termios) {
|
||||
termios.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON)
|
||||
termios.Oflag &^= syscall.OPOST
|
||||
termios.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
|
||||
termios.Cflag &^= (syscall.CSIZE | syscall.PARENB)
|
||||
termios.Cflag |= syscall.CS8
|
||||
termios.Cc[syscall.VMIN] = 1
|
||||
termios.Cc[syscall.VTIME] = 0
|
||||
}
|
||||
|
||||
// MakeRaw sets the flags stored in the termios structure for the given terminal fd
|
||||
// to a state disabling all input and output processing, giving a ``raw I/O path''.
|
||||
// It returns the current terminal's termios struct to allow to revert with TcSetAttr
|
||||
func MakeRaw(fd uintptr) (*Termios, error) {
|
||||
old, err := TcGetAttr(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
new := *old
|
||||
CfMakeRaw(&new)
|
||||
|
||||
if err := TcSetAttr(fd, &new); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return old, nil
|
||||
}
|
||||
115
Godeps/_workspace/src/github.com/creack/termios/raw/raw_test.go
generated
vendored
Normal file
115
Godeps/_workspace/src/github.com/creack/termios/raw/raw_test.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
package raw
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/asciinema/asciinema/Godeps/_workspace/src/github.com/kr/pty"
|
||||
)
|
||||
|
||||
func TestCfMakeRaw(t *testing.T) {
|
||||
termios := &Termios{
|
||||
Iflag: 0x2b02,
|
||||
Oflag: 0x1,
|
||||
Lflag: 0x5c3,
|
||||
Cflag: 0x4b00,
|
||||
Cc: [20]byte{
|
||||
0x4, 0xff, 0xff, 0xff,
|
||||
0x17, 0xff, 0x12, 0xff,
|
||||
0x3, 0x1c, 0x1a, 0x19,
|
||||
0x11, 0x13, 0x16, 0xf,
|
||||
0xff, 0xff, 0x14, 0xff,
|
||||
},
|
||||
Ispeed: 0x2580, // (9600)
|
||||
Ospeed: 0x2580, // (9600)
|
||||
}
|
||||
CfMakeRaw(termios)
|
||||
want := Termios{
|
||||
Iflag: 0x2800,
|
||||
Oflag: 0x0,
|
||||
Lflag: 0x43,
|
||||
Cflag: 0x4b00,
|
||||
Cc: [20]byte{
|
||||
0x4, 0xff, 0xff, 0xff,
|
||||
0x17, 0xff, 0x12, 0xff,
|
||||
0x3, 0x1c, 0x1a, 0x19,
|
||||
0x11, 0x13, 0x16, 0xf,
|
||||
0x1, 0x0, 0x14, 0xff,
|
||||
},
|
||||
Ispeed: 0x2580,
|
||||
Ospeed: 0x2580,
|
||||
}
|
||||
if got := *termios; want != got {
|
||||
t.Fatalf("Unexpected Raw termios.\nGot:\t %#v\nWant:\t %#v\n", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeRaw(t *testing.T) {
|
||||
// Create a PTY pair to play with
|
||||
master, slave, err := pty.Open()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer master.Close()
|
||||
defer slave.Close()
|
||||
|
||||
// Apply the raw mode on the slave
|
||||
slaveTermios, err := MakeRaw(slave.Fd())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Make a copy of the original termios and manually apply cfmakeraw
|
||||
slaveTermiosOrig := *slaveTermios
|
||||
CfMakeRaw(&slaveTermiosOrig)
|
||||
|
||||
// Retrieve the new termios on the slave after NakeRaw
|
||||
slaveTermiosRaw, err := TcGetAttr(slave.Fd())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Make sure the new termios are the one we want
|
||||
if slaveTermiosOrig != *slaveTermiosRaw {
|
||||
t.Fatalf("Unepexpected termios.\nGot:\t %#v\nWant:\t %#v\n", *slaveTermiosRaw, slaveTermiosOrig)
|
||||
}
|
||||
|
||||
// Simple read/write test on the master/slave pair
|
||||
want := "hello world!"
|
||||
go master.WriteString(want)
|
||||
|
||||
var (
|
||||
buf = make([]byte, 64)
|
||||
c = make(chan struct{})
|
||||
)
|
||||
|
||||
// Without raw mode, as there is no \n, read will block forever
|
||||
go func() {
|
||||
defer close(c)
|
||||
n, err := slave.Read(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buf = buf[:n]
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second)
|
||||
buf = []byte("timeout")
|
||||
close(c)
|
||||
}()
|
||||
<-c
|
||||
if got := string(buf); got != want {
|
||||
t.Fatalf("Unexpected result.\nGot: %s\nWant: %s\n", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCfMakeRaw(b *testing.B) {
|
||||
t := &Termios{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
CfMakeRaw(t)
|
||||
if t.Cc[syscall.VMIN] != 1 {
|
||||
b.Fatalf("err")
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Godeps/_workspace/src/github.com/creack/termios/raw/termios_32.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/creack/termios/raw/termios_32.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// +build linux freebsd
|
||||
|
||||
package raw
|
||||
|
||||
// Termios holds the TTY attributes. See man termios(4).
|
||||
// Tested on linux386, linux/arm, linux/amd64,
|
||||
// freebsd/386, freebsd/arm, freebsd/amd64.
|
||||
// See tremios_64.go for darwin.
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]byte
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
||||
15
Godeps/_workspace/src/github.com/creack/termios/raw/termios_64.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/creack/termios/raw/termios_64.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// +build darwin
|
||||
|
||||
package raw
|
||||
|
||||
// Termios holds the TTY attributes. See man termios(4).
|
||||
// Tested on darwin/386, darwin/amd64. See termios_32.go for others.
|
||||
type Termios struct {
|
||||
Iflag uint64
|
||||
Oflag uint64
|
||||
Cflag uint64
|
||||
Lflag uint64
|
||||
Cc [20]byte
|
||||
Ispeed uint64
|
||||
Ospeed uint64
|
||||
}
|
||||
10
Godeps/_workspace/src/github.com/creack/termios/raw/tioc_bsd.go
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/creack/termios/raw/tioc_bsd.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// +build darwin freebsd
|
||||
|
||||
package raw
|
||||
|
||||
import "syscall"
|
||||
|
||||
const (
|
||||
getTermios = syscall.TIOCGETA
|
||||
setTermios = syscall.TIOCSETA
|
||||
)
|
||||
10
Godeps/_workspace/src/github.com/creack/termios/raw/tioc_linux.go
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/creack/termios/raw/tioc_linux.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// +build linux
|
||||
|
||||
package raw
|
||||
|
||||
import "syscall"
|
||||
|
||||
const (
|
||||
getTermios = syscall.TCGETS
|
||||
setTermios = syscall.TCSETS
|
||||
)
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/asciinema/asciinema/Godeps/_workspace/src/code.google.com/p/go.crypto/ssh/terminal"
|
||||
"github.com/asciinema/asciinema/Godeps/_workspace/src/github.com/creack/termios/raw"
|
||||
"github.com/asciinema/asciinema/Godeps/_workspace/src/github.com/kr/pty"
|
||||
"github.com/asciinema/asciinema/ptyx"
|
||||
"github.com/asciinema/asciinema/util"
|
||||
@@ -55,13 +56,13 @@ func (p *Pty) Record(command string, stdoutCopy io.Writer) error {
|
||||
defer close(signals)
|
||||
|
||||
// put stdin in raw mode (if it's a tty)
|
||||
fd := int(p.Stdin.Fd())
|
||||
if terminal.IsTerminal(fd) {
|
||||
oldState, err := terminal.MakeRaw(fd)
|
||||
fd := p.Stdin.Fd()
|
||||
if terminal.IsTerminal(int(fd)) {
|
||||
oldState, err := raw.MakeRaw(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer terminal.Restore(fd, oldState)
|
||||
defer raw.TcSetAttr(fd, oldState)
|
||||
}
|
||||
|
||||
// do initial resize
|
||||
|
||||
Reference in New Issue
Block a user