Support CTRL-Z (SIGSTOP)

This commit is contained in:
Junegunn Choi 2017-04-28 22:58:08 +09:00
parent 6b592137b9
commit d34e4cf698
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
8 changed files with 92 additions and 41 deletions

View File

@ -59,6 +59,7 @@ type Terminal struct {
inlineInfo bool inlineInfo bool
prompt string prompt string
reverse bool reverse bool
fullscreen bool
hscroll bool hscroll bool
hscrollOff int hscrollOff int
wordRubout string wordRubout string
@ -141,6 +142,7 @@ const (
reqList reqList
reqJump reqJump
reqRefresh reqRefresh
reqReinit
reqRedraw reqRedraw
reqClose reqClose
reqPrintQuery reqPrintQuery
@ -210,6 +212,7 @@ const (
actExecute actExecute
actExecuteSilent actExecuteSilent
actExecuteMulti // Deprecated actExecuteMulti // Deprecated
actSigStop
) )
func toActions(types ...actionType) []action { func toActions(types ...actionType) []action {
@ -246,6 +249,9 @@ func defaultKeymap() map[int][]action {
keymap[tui.CtrlU] = toActions(actUnixLineDiscard) keymap[tui.CtrlU] = toActions(actUnixLineDiscard)
keymap[tui.CtrlW] = toActions(actUnixWordRubout) keymap[tui.CtrlW] = toActions(actUnixWordRubout)
keymap[tui.CtrlY] = toActions(actYank) keymap[tui.CtrlY] = toActions(actYank)
if !util.IsWindows() {
keymap[tui.CtrlZ] = toActions(actSigStop)
}
keymap[tui.AltB] = toActions(actBackwardWord) keymap[tui.AltB] = toActions(actBackwardWord)
keymap[tui.SLeft] = toActions(actBackwardWord) keymap[tui.SLeft] = toActions(actBackwardWord)
@ -295,7 +301,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
strongAttr = tui.AttrRegular strongAttr = tui.AttrRegular
} }
var renderer tui.Renderer var renderer tui.Renderer
if opts.Height.size == 0 || opts.Height.percent && opts.Height.size == 100 { fullscreen := opts.Height.size == 0 || opts.Height.percent && opts.Height.size == 100
if fullscreen {
if tui.HasFullscreenRenderer() { if tui.HasFullscreenRenderer() {
renderer = tui.NewFullscreenRenderer(opts.Theme, opts.Black, opts.Mouse) renderer = tui.NewFullscreenRenderer(opts.Theme, opts.Black, opts.Mouse)
} else { } else {
@ -337,6 +344,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
inlineInfo: opts.InlineInfo, inlineInfo: opts.InlineInfo,
prompt: opts.Prompt, prompt: opts.Prompt,
reverse: opts.Reverse, reverse: opts.Reverse,
fullscreen: fullscreen,
hscroll: opts.Hscroll, hscroll: opts.Hscroll,
hscrollOff: opts.HscrollOff, hscrollOff: opts.HscrollOff,
wordRubout: wordRubout, wordRubout: wordRubout,
@ -1170,6 +1178,12 @@ func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, fo
}) })
} }
func (t *Terminal) redraw() {
t.tui.Clear()
t.tui.Refresh()
t.printAll()
}
func (t *Terminal) executeCommand(template string, forcePlus bool, background bool) { func (t *Terminal) executeCommand(template string, forcePlus bool, background bool) {
valid, list := t.buildPlusList(template, forcePlus) valid, list := t.buildPlusList(template, forcePlus)
if !valid { if !valid {
@ -1181,12 +1195,10 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
t.tui.Pause() t.tui.Pause(true)
cmd.Run() cmd.Run()
if t.tui.Resume() { t.tui.Resume(true)
t.tui.Clear() t.redraw()
t.printAll()
}
t.refresh() t.refresh()
} else { } else {
cmd.Run() cmd.Run()
@ -1244,6 +1256,15 @@ func (t *Terminal) Loop() {
t.reqBox.Set(reqQuit, nil) t.reqBox.Set(reqQuit, nil)
}() }()
contChan := make(chan os.Signal, 1)
notifyOnCont(contChan)
go func() {
for {
<-contChan
t.reqBox.Set(reqReinit, nil)
}
}()
resizeChan := make(chan os.Signal, 1) resizeChan := make(chan os.Signal, 1)
notifyOnResize(resizeChan) // Non-portable notifyOnResize(resizeChan) // Non-portable
go func() { go func() {
@ -1352,10 +1373,11 @@ func (t *Terminal) Loop() {
t.printHeader() t.printHeader()
case reqRefresh: case reqRefresh:
t.suppress = false t.suppress = false
case reqReinit:
t.tui.Resume(t.fullscreen)
t.redraw()
case reqRedraw: case reqRedraw:
t.tui.Clear() t.redraw()
t.tui.Refresh()
t.printAll()
case reqClose: case reqClose:
t.tui.Close() t.tui.Close()
if t.output() { if t.output() {
@ -1654,6 +1676,15 @@ func (t *Terminal) Loop() {
t.input = []rune(t.history.next()) t.input = []rune(t.history.next())
t.cx = len(t.input) t.cx = len(t.input)
} }
case actSigStop:
p, err := os.FindProcess(os.Getpid())
if err == nil {
t.tui.Clear()
t.tui.Pause(t.fullscreen)
notifyStop(p)
t.mutex.Unlock()
return false
}
case actMouse: case actMouse:
me := event.MouseEvent me := event.MouseEvent
mx, my := me.X, me.Y mx, my := me.X, me.Y

View File

@ -11,3 +11,11 @@ import (
func notifyOnResize(resizeChan chan<- os.Signal) { func notifyOnResize(resizeChan chan<- os.Signal) {
signal.Notify(resizeChan, syscall.SIGWINCH) signal.Notify(resizeChan, syscall.SIGWINCH)
} }
func notifyStop(p *os.Process) {
p.Signal(syscall.SIGSTOP)
}
func notifyOnCont(resizeChan chan<- os.Signal) {
signal.Notify(resizeChan, syscall.SIGCONT)
}

View File

@ -9,3 +9,11 @@ import (
func notifyOnResize(resizeChan chan<- os.Signal) { func notifyOnResize(resizeChan chan<- os.Signal) {
// TODO // TODO
} }
func notifyStop(p *os.Process) {
// NOOP
}
func notifyOnCont(resizeChan chan<- os.Signal) {
// NOOP
}

View File

@ -25,13 +25,13 @@ const (
Reverse = Attr(1 << 6) Reverse = Attr(1 << 6)
) )
func (r *FullscreenRenderer) Init() {} func (r *FullscreenRenderer) Init() {}
func (r *FullscreenRenderer) Pause() {} func (r *FullscreenRenderer) Pause(bool) {}
func (r *FullscreenRenderer) Clear() {} func (r *FullscreenRenderer) Resume(bool) {}
func (r *FullscreenRenderer) Refresh() {} func (r *FullscreenRenderer) Clear() {}
func (r *FullscreenRenderer) Close() {} func (r *FullscreenRenderer) Refresh() {}
func (r *FullscreenRenderer) Close() {}
func (r *FullscreenRenderer) Resume() bool { return false }
func (r *FullscreenRenderer) DoesAutoWrap() bool { return false } func (r *FullscreenRenderer) DoesAutoWrap() bool { return false }
func (r *FullscreenRenderer) IsOptimized() bool { return false } func (r *FullscreenRenderer) IsOptimized() bool { return false }
func (r *FullscreenRenderer) GetChar() Event { return Event{} } func (r *FullscreenRenderer) GetChar() Event { return Event{} }

View File

@ -522,27 +522,35 @@ func (r *LightRenderer) rmcup() {
r.csi("?1049l") r.csi("?1049l")
} }
func (r *LightRenderer) Pause() { func (r *LightRenderer) Pause(clear bool) {
terminal.Restore(r.fd(), r.origState) terminal.Restore(r.fd(), r.origState)
if r.fullscreen { if clear {
r.rmcup() if r.fullscreen {
} else { r.rmcup()
r.smcup() } else {
r.csi("H") r.smcup()
r.csi("H")
}
r.flush()
} }
r.flush()
} }
func (r *LightRenderer) Resume() bool { func (r *LightRenderer) Resume(clear bool) {
terminal.MakeRaw(r.fd()) terminal.MakeRaw(r.fd())
if r.fullscreen { if clear {
r.smcup() if r.fullscreen {
} else { r.smcup()
r.rmcup() } else {
r.rmcup()
}
r.flush()
} else if !r.fullscreen && r.mouse {
// NOTE: Resume(false) is only called on SIGCONT after SIGSTOP.
// And It's highly likely that the offset we obtained at the beginning will
// no longer be correct, so we simply disable mouse input.
r.csi("?1000l")
r.mouse = false
} }
r.flush()
// Should redraw
return true
} }
func (r *LightRenderer) Clear() { func (r *LightRenderer) Clear() {

View File

@ -176,12 +176,11 @@ func initPairs(theme *ColorTheme) {
} }
} }
func (r *FullscreenRenderer) Pause() { func (r *FullscreenRenderer) Pause(bool) {
C.endwin() C.endwin()
} }
func (r *FullscreenRenderer) Resume() bool { func (r *FullscreenRenderer) Resume(bool) {
return false
} }
func (r *FullscreenRenderer) Close() { func (r *FullscreenRenderer) Close() {

View File

@ -282,7 +282,7 @@ func (r *FullscreenRenderer) GetChar() Event {
return Event{keyfn('z'), 0, nil} return Event{keyfn('z'), 0, nil}
case tcell.KeyCtrlSpace: case tcell.KeyCtrlSpace:
return Event{CtrlSpace, 0, nil} return Event{CtrlSpace, 0, nil}
case tcell.KeyBackspace, tcell.KeyBackspace2: case tcell.KeyBackspace2:
if alt { if alt {
return Event{AltBS, 0, nil} return Event{AltBS, 0, nil}
} }
@ -308,8 +308,6 @@ func (r *FullscreenRenderer) GetChar() Event {
case tcell.KeyPgDn: case tcell.KeyPgDn:
return Event{PgDn, 0, nil} return Event{PgDn, 0, nil}
case tcell.KeyTab:
return Event{Tab, 0, nil}
case tcell.KeyBacktab: case tcell.KeyBacktab:
return Event{BTab, 0, nil} return Event{BTab, 0, nil}
@ -366,13 +364,12 @@ func (r *FullscreenRenderer) GetChar() Event {
return Event{Invalid, 0, nil} return Event{Invalid, 0, nil}
} }
func (r *FullscreenRenderer) Pause() { func (r *FullscreenRenderer) Pause(bool) {
_screen.Fini() _screen.Fini()
} }
func (r *FullscreenRenderer) Resume() bool { func (r *FullscreenRenderer) Resume(bool) {
r.initScreen() r.initScreen()
return true
} }
func (r *FullscreenRenderer) Close() { func (r *FullscreenRenderer) Close() {

View File

@ -206,8 +206,8 @@ const (
type Renderer interface { type Renderer interface {
Init() Init()
Pause() Pause(clear bool)
Resume() bool Resume(clear bool)
Clear() Clear()
RefreshWindows(windows []Window) RefreshWindows(windows []Window)
Refresh() Refresh()