forked from mirror/readline
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
|
||||
CharCtrlU = 21
|
||||
CharCtrlW = 23
|
||||
CharCtrlZ = 26
|
||||
CharEsc = 27
|
||||
CharEscapeEx = 91
|
||||
CharBackspace = 127
|
||||
|
|
|
@ -186,6 +186,10 @@ func (o *Operation) ioloop() {
|
|||
if o.IsInCompleteMode() {
|
||||
o.OnComplete()
|
||||
}
|
||||
case CharCtrlZ:
|
||||
o.buf.Clean()
|
||||
o.t.SleepToResume()
|
||||
o.Refresh()
|
||||
case MetaBackspace, CharCtrlW:
|
||||
o.buf.BackEscapeWord()
|
||||
case CharEnter, CharCtrlJ:
|
||||
|
|
20
terminal.go
20
terminal.go
|
@ -3,6 +3,7 @@ package readline
|
|||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
@ -15,6 +16,7 @@ type Terminal struct {
|
|||
kickChan chan struct{}
|
||||
wg sync.WaitGroup
|
||||
isReading int32
|
||||
sleeping int32
|
||||
}
|
||||
|
||||
func NewTerminal(cfg *Config) (*Terminal, error) {
|
||||
|
@ -32,6 +34,20 @@ func NewTerminal(cfg *Config) (*Terminal, error) {
|
|||
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) {
|
||||
return t.cfg.FuncMakeRaw()
|
||||
}
|
||||
|
@ -99,6 +115,10 @@ func (t *Terminal) ioloop() {
|
|||
expectNextChar = false
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "interrupted system call") {
|
||||
expectNextChar = true
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
|
|
27
utils.go
27
utils.go
|
@ -4,6 +4,8 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
@ -12,6 +14,31 @@ var (
|
|||
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.
|
||||
func IsTerminal(fd int) bool {
|
||||
return terminal.IsTerminal(fd)
|
||||
|
|
|
@ -17,6 +17,16 @@ type winsize struct {
|
|||
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
|
||||
func getWidth(stdoutFd int) int {
|
||||
ws := &winsize{}
|
||||
|
|
|
@ -4,6 +4,9 @@ package readline
|
|||
|
||||
import "syscall"
|
||||
|
||||
func SuspendMe() {
|
||||
}
|
||||
|
||||
func GetStdin() int {
|
||||
return int(syscall.Stdin)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue