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",
|
"Comment": "null-213",
|
||||||
"Rev": "aa2644fe4aa50e3b38d75187b4799b1f0c9ddcef"
|
"Rev": "aa2644fe4aa50e3b38d75187b4799b1f0c9ddcef"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/creack/termios/raw",
|
||||||
|
"Rev": "d60649a6c40aa68303e3d69e0423a1e7aedf4bbd"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docopt/docopt-go",
|
"ImportPath": "github.com/docopt/docopt-go",
|
||||||
"Comment": "0.6.1-1-gc5dac53",
|
"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"
|
"time"
|
||||||
|
|
||||||
"github.com/asciinema/asciinema/Godeps/_workspace/src/code.google.com/p/go.crypto/ssh/terminal"
|
"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/Godeps/_workspace/src/github.com/kr/pty"
|
||||||
"github.com/asciinema/asciinema/ptyx"
|
"github.com/asciinema/asciinema/ptyx"
|
||||||
"github.com/asciinema/asciinema/util"
|
"github.com/asciinema/asciinema/util"
|
||||||
@@ -55,13 +56,13 @@ func (p *Pty) Record(command string, stdoutCopy io.Writer) error {
|
|||||||
defer close(signals)
|
defer close(signals)
|
||||||
|
|
||||||
// put stdin in raw mode (if it's a tty)
|
// put stdin in raw mode (if it's a tty)
|
||||||
fd := int(p.Stdin.Fd())
|
fd := p.Stdin.Fd()
|
||||||
if terminal.IsTerminal(fd) {
|
if terminal.IsTerminal(int(fd)) {
|
||||||
oldState, err := terminal.MakeRaw(fd)
|
oldState, err := raw.MakeRaw(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer terminal.Restore(fd, oldState)
|
defer raw.TcSetAttr(fd, oldState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// do initial resize
|
// do initial resize
|
||||||
|
|||||||
Reference in New Issue
Block a user