diff --git a/src/terminal.go b/src/terminal.go index 197ab6b..2802edd 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -59,6 +59,7 @@ type Terminal struct { inlineInfo bool prompt string reverse bool + fullscreen bool hscroll bool hscrollOff int wordRubout string @@ -141,6 +142,7 @@ const ( reqList reqJump reqRefresh + reqReinit reqRedraw reqClose reqPrintQuery @@ -210,6 +212,7 @@ const ( actExecute actExecuteSilent actExecuteMulti // Deprecated + actSigStop ) func toActions(types ...actionType) []action { @@ -246,6 +249,9 @@ func defaultKeymap() map[int][]action { keymap[tui.CtrlU] = toActions(actUnixLineDiscard) keymap[tui.CtrlW] = toActions(actUnixWordRubout) keymap[tui.CtrlY] = toActions(actYank) + if !util.IsWindows() { + keymap[tui.CtrlZ] = toActions(actSigStop) + } keymap[tui.AltB] = toActions(actBackwardWord) keymap[tui.SLeft] = toActions(actBackwardWord) @@ -295,7 +301,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { strongAttr = tui.AttrRegular } 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() { renderer = tui.NewFullscreenRenderer(opts.Theme, opts.Black, opts.Mouse) } else { @@ -337,6 +344,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { inlineInfo: opts.InlineInfo, prompt: opts.Prompt, reverse: opts.Reverse, + fullscreen: fullscreen, hscroll: opts.Hscroll, hscrollOff: opts.HscrollOff, 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) { valid, list := t.buildPlusList(template, forcePlus) if !valid { @@ -1181,12 +1195,10 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - t.tui.Pause() + t.tui.Pause(true) cmd.Run() - if t.tui.Resume() { - t.tui.Clear() - t.printAll() - } + t.tui.Resume(true) + t.redraw() t.refresh() } else { cmd.Run() @@ -1244,6 +1256,15 @@ func (t *Terminal) Loop() { 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) notifyOnResize(resizeChan) // Non-portable go func() { @@ -1352,10 +1373,11 @@ func (t *Terminal) Loop() { t.printHeader() case reqRefresh: t.suppress = false + case reqReinit: + t.tui.Resume(t.fullscreen) + t.redraw() case reqRedraw: - t.tui.Clear() - t.tui.Refresh() - t.printAll() + t.redraw() case reqClose: t.tui.Close() if t.output() { @@ -1654,6 +1676,15 @@ func (t *Terminal) Loop() { t.input = []rune(t.history.next()) 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: me := event.MouseEvent mx, my := me.X, me.Y diff --git a/src/terminal_unix.go b/src/terminal_unix.go index 6284c22..2ae8175 100644 --- a/src/terminal_unix.go +++ b/src/terminal_unix.go @@ -11,3 +11,11 @@ import ( func notifyOnResize(resizeChan chan<- os.Signal) { 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) +} diff --git a/src/terminal_windows.go b/src/terminal_windows.go index 5512bba..9de7ae4 100644 --- a/src/terminal_windows.go +++ b/src/terminal_windows.go @@ -9,3 +9,11 @@ import ( func notifyOnResize(resizeChan chan<- os.Signal) { // TODO } + +func notifyStop(p *os.Process) { + // NOOP +} + +func notifyOnCont(resizeChan chan<- os.Signal) { + // NOOP +} diff --git a/src/tui/dummy.go b/src/tui/dummy.go index 60a23fb..c96ce8e 100644 --- a/src/tui/dummy.go +++ b/src/tui/dummy.go @@ -25,13 +25,13 @@ const ( Reverse = Attr(1 << 6) ) -func (r *FullscreenRenderer) Init() {} -func (r *FullscreenRenderer) Pause() {} -func (r *FullscreenRenderer) Clear() {} -func (r *FullscreenRenderer) Refresh() {} -func (r *FullscreenRenderer) Close() {} +func (r *FullscreenRenderer) Init() {} +func (r *FullscreenRenderer) Pause(bool) {} +func (r *FullscreenRenderer) Resume(bool) {} +func (r *FullscreenRenderer) Clear() {} +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) IsOptimized() bool { return false } func (r *FullscreenRenderer) GetChar() Event { return Event{} } diff --git a/src/tui/light.go b/src/tui/light.go index 7819050..be6950c 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -522,27 +522,35 @@ func (r *LightRenderer) rmcup() { r.csi("?1049l") } -func (r *LightRenderer) Pause() { +func (r *LightRenderer) Pause(clear bool) { terminal.Restore(r.fd(), r.origState) - if r.fullscreen { - r.rmcup() - } else { - r.smcup() - r.csi("H") + if clear { + if r.fullscreen { + r.rmcup() + } else { + r.smcup() + r.csi("H") + } + r.flush() } - r.flush() } -func (r *LightRenderer) Resume() bool { +func (r *LightRenderer) Resume(clear bool) { terminal.MakeRaw(r.fd()) - if r.fullscreen { - r.smcup() - } else { - r.rmcup() + if clear { + if r.fullscreen { + r.smcup() + } 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() { diff --git a/src/tui/ncurses.go b/src/tui/ncurses.go index ba3a1cf..3263c39 100644 --- a/src/tui/ncurses.go +++ b/src/tui/ncurses.go @@ -176,12 +176,11 @@ func initPairs(theme *ColorTheme) { } } -func (r *FullscreenRenderer) Pause() { +func (r *FullscreenRenderer) Pause(bool) { C.endwin() } -func (r *FullscreenRenderer) Resume() bool { - return false +func (r *FullscreenRenderer) Resume(bool) { } func (r *FullscreenRenderer) Close() { diff --git a/src/tui/tcell.go b/src/tui/tcell.go index bda5d08..3a8c765 100644 --- a/src/tui/tcell.go +++ b/src/tui/tcell.go @@ -282,7 +282,7 @@ func (r *FullscreenRenderer) GetChar() Event { return Event{keyfn('z'), 0, nil} case tcell.KeyCtrlSpace: return Event{CtrlSpace, 0, nil} - case tcell.KeyBackspace, tcell.KeyBackspace2: + case tcell.KeyBackspace2: if alt { return Event{AltBS, 0, nil} } @@ -308,8 +308,6 @@ func (r *FullscreenRenderer) GetChar() Event { case tcell.KeyPgDn: return Event{PgDn, 0, nil} - case tcell.KeyTab: - return Event{Tab, 0, nil} case tcell.KeyBacktab: return Event{BTab, 0, nil} @@ -366,13 +364,12 @@ func (r *FullscreenRenderer) GetChar() Event { return Event{Invalid, 0, nil} } -func (r *FullscreenRenderer) Pause() { +func (r *FullscreenRenderer) Pause(bool) { _screen.Fini() } -func (r *FullscreenRenderer) Resume() bool { +func (r *FullscreenRenderer) Resume(bool) { r.initScreen() - return true } func (r *FullscreenRenderer) Close() { diff --git a/src/tui/tui.go b/src/tui/tui.go index d360c4e..c638b35 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -206,8 +206,8 @@ const ( type Renderer interface { Init() - Pause() - Resume() bool + Pause(clear bool) + Resume(clear bool) Clear() RefreshWindows(windows []Window) Refresh()