mirror of https://github.com/chzyer/readline.git
support suspend process (#48)
* support suspend process * fix suspend in windows * add comments
This commit is contained in:
parent
52d8a65723
commit
3ea5940c39
1
char.go
1
char.go
|
@ -20,6 +20,7 @@ const (
|
||||||
CharTranspose = 20
|
CharTranspose = 20
|
||||||
CharCtrlU = 21
|
CharCtrlU = 21
|
||||||
CharCtrlW = 23
|
CharCtrlW = 23
|
||||||
|
CharCtrlZ = 26
|
||||||
CharEsc = 27
|
CharEsc = 27
|
||||||
CharEscapeEx = 91
|
CharEscapeEx = 91
|
||||||
CharBackspace = 127
|
CharBackspace = 127
|
||||||
|
|
|
@ -186,6 +186,10 @@ func (o *Operation) ioloop() {
|
||||||
if o.IsInCompleteMode() {
|
if o.IsInCompleteMode() {
|
||||||
o.OnComplete()
|
o.OnComplete()
|
||||||
}
|
}
|
||||||
|
case CharCtrlZ:
|
||||||
|
o.buf.Clean()
|
||||||
|
o.t.SleepToResume()
|
||||||
|
o.Refresh()
|
||||||
case MetaBackspace, CharCtrlW:
|
case MetaBackspace, CharCtrlW:
|
||||||
o.buf.BackEscapeWord()
|
o.buf.BackEscapeWord()
|
||||||
case CharEnter, CharCtrlJ:
|
case CharEnter, CharCtrlJ:
|
||||||
|
|
20
terminal.go
20
terminal.go
|
@ -3,6 +3,7 @@ package readline
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
@ -15,6 +16,7 @@ type Terminal struct {
|
||||||
kickChan chan struct{}
|
kickChan chan struct{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
isReading int32
|
isReading int32
|
||||||
|
sleeping int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTerminal(cfg *Config) (*Terminal, error) {
|
func NewTerminal(cfg *Config) (*Terminal, error) {
|
||||||
|
@ -32,6 +34,20 @@ func NewTerminal(cfg *Config) (*Terminal, error) {
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SleepToResume will sleep myself, and return only if I'm resumed.
|
||||||
|
func (t *Terminal) SleepToResume() {
|
||||||
|
if !atomic.CompareAndSwapInt32(&t.sleeping, 0, 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer atomic.StoreInt32(&t.sleeping, 0)
|
||||||
|
|
||||||
|
t.ExitRawMode()
|
||||||
|
ch := WaitForResume()
|
||||||
|
SuspendMe()
|
||||||
|
<-ch
|
||||||
|
t.EnterRawMode()
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Terminal) EnterRawMode() (err error) {
|
func (t *Terminal) EnterRawMode() (err error) {
|
||||||
return t.cfg.FuncMakeRaw()
|
return t.cfg.FuncMakeRaw()
|
||||||
}
|
}
|
||||||
|
@ -99,6 +115,10 @@ func (t *Terminal) ioloop() {
|
||||||
expectNextChar = false
|
expectNextChar = false
|
||||||
r, _, err := buf.ReadRune()
|
r, _, err := buf.ReadRune()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "interrupted system call") {
|
||||||
|
expectNextChar = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
utils.go
27
utils.go
|
@ -4,6 +4,8 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
@ -12,6 +14,31 @@ var (
|
||||||
isWindows = false
|
isWindows = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// WaitForResume need to call before current process got suspend.
|
||||||
|
// It will run a ticker until a long duration is occurs,
|
||||||
|
// which means this process is resumed.
|
||||||
|
func WaitForResume() chan struct{} {
|
||||||
|
ch := make(chan struct{})
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(10 * time.Millisecond)
|
||||||
|
t := time.Now()
|
||||||
|
wg.Done()
|
||||||
|
for {
|
||||||
|
now := <-ticker.C
|
||||||
|
if now.Sub(t) > 100*time.Millisecond {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t = now
|
||||||
|
}
|
||||||
|
ticker.Stop()
|
||||||
|
ch <- struct{}{}
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
func IsTerminal(fd int) bool {
|
func IsTerminal(fd int) bool {
|
||||||
return terminal.IsTerminal(fd)
|
return terminal.IsTerminal(fd)
|
||||||
|
|
|
@ -17,6 +17,16 @@ type winsize struct {
|
||||||
Ypixel uint16
|
Ypixel uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SuspendMe use to send suspend signal to myself, when we in the raw mode.
|
||||||
|
// For OSX it need to send to parent's pid
|
||||||
|
// For Linux it need to send to myself
|
||||||
|
func SuspendMe() {
|
||||||
|
p, _ := os.FindProcess(os.Getppid())
|
||||||
|
p.Signal(syscall.SIGTSTP)
|
||||||
|
p, _ = os.FindProcess(os.Getpid())
|
||||||
|
p.Signal(syscall.SIGTSTP)
|
||||||
|
}
|
||||||
|
|
||||||
// get width of the terminal
|
// get width of the terminal
|
||||||
func getWidth(stdoutFd int) int {
|
func getWidth(stdoutFd int) int {
|
||||||
ws := &winsize{}
|
ws := &winsize{}
|
||||||
|
|
|
@ -4,6 +4,9 @@ package readline
|
||||||
|
|
||||||
import "syscall"
|
import "syscall"
|
||||||
|
|
||||||
|
func SuspendMe() {
|
||||||
|
}
|
||||||
|
|
||||||
func GetStdin() int {
|
func GetStdin() int {
|
||||||
return int(syscall.Stdin)
|
return int(syscall.Stdin)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue