kill the process but the mouse code still output in the console
4everSivan opened this issue · comments
Describe the bug
Linux use -9 kill the process but the mouse code still output in the console until run process again and use normal exit.
Setup
- OS [centos 7.9]
- Shell [system console]
To Reproduce
Steps to reproduce the behavior:
- Run the process.
- Kill the process.
- See error
Source Code
p := tea.NewProgram(ui, tea.WithAltScreen(), tea.WithMouseAllMotion())
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
Expected behavior
nothing output when kill or close the process.
I also use bubblezone for my process, i dont know what made it happen🙏
It's expected behavior that the mouse code still output in the console. Use tea.WithMouseAllMotion()
will change the terminal state, when you kill -9 <pid>
, your process can do nothing but stop immediately. Try use kill -SIGINT <pid>
or kill -SIGTERM <pid>
which have registered handler to correctly tear down.
Below code show what happen when you use tea.WithMouseAllMotion()
. r.out.EnableMouseExtendedMode()
changes the behavior of terminal.
https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L506
} else if p.startupOptions&withMouseAllMotion != 0 {
p.renderer.enableMouseAllMotion()
p.renderer.enableMouseSGRMode()
}
https://github.com/charmbracelet/bubbletea/blob/master/standard_renderer.go#L383-L388
func (r *standardRenderer) enableMouseSGRMode() {
r.mtx.Lock()
defer r.mtx.Unlock()
r.out.EnableMouseExtendedMode()
}
Below code show why you can stop your program properly use SIGINT and SIGTERM. tea will shutdown when receiving SIGINT or SIGTERM. During shutdown restoreTerminalState
is called which calls disableMouse
, therefore your terminal works as usual.
https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L212-L246
func (p *Program) handleSignals() chan struct{} {
ch := make(chan struct{})
// Listen for SIGINT and SIGTERM.
//
// In most cases ^C will not send an interrupt because the terminal will be
// in raw mode and ^C will be captured as a keystroke and sent along to
// Program.Update as a KeyMsg. When input is not a TTY, however, ^C will be
// caught here.
//
// SIGTERM is sent by unix utilities (like kill) to terminate a process.
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
defer func() {
signal.Stop(sig)
close(ch)
}()
for {
select {
case <-p.ctx.Done():
return
case <-sig:
if atomic.LoadUint32(&p.ignoreSignals) == 0 {
p.msgs <- QuitMsg{}
return
}
}
}
}()
return ch
}
https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L316-L328
case msg := <-p.msgs:
// Filter messages.
if p.filter != nil {
msg = p.filter(model, msg)
}
if msg == nil {
continue
}
// Handle special internal messages.
switch msg := msg.(type) {
case QuitMsg:
return model, nil
https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L572
p.shutdown(killed)
https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L635-649
func (p *Program) shutdown(kill bool) {
if p.renderer != nil {
if kill {
p.renderer.kill()
} else {
p.renderer.stop()
}
}
_ = p.restoreTerminalState()
if p.restoreOutput != nil {
_ = p.restoreOutput()
}
p.finished <- struct{}{}
}
https://github.com/charmbracelet/bubbletea/blob/master/tty.go#L25-L40
func (p *Program) restoreTerminalState() error {
if p.renderer != nil {
p.renderer.disableBracketedPaste()
p.renderer.showCursor()
p.disableMouse()
if p.renderer.altScreen() {
p.renderer.exitAltScreen()
// give the terminal a moment to catch up
time.Sleep(time.Millisecond * 10) //nolint:gomnd
}
}
return p.restoreInput()
}
https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L299-L303
func (p *Program) disableMouse() {
p.renderer.disableMouseCellMotion()
p.renderer.disableMouseAllMotion()
p.renderer.disableMouseSGRMode()
}
It's expected behavior that the mouse code still output in the console. Use
tea.WithMouseAllMotion()
will change the terminal state, when youkill -9 <pid>
, your process can do nothing but stop immediately. Try usekill -SIGINT <pid>
orkill -SIGTERM <pid>
which have registered handler to correctly tear down.鼠标代码仍然在控制台中输出是预期的行为。使用tea.WithMouseAllMotion()
将更改终端状态,当您kill -9 <pid>
时,您的进程只能立即停止,而不能执行任何操作。尝试使用kill -SIGINT <pid>
或kill -SIGTERM <pid>
,它们已注册处理程序以正确终止。 Below code show what happen when you usetea.WithMouseAllMotion()
.r.out.EnableMouseExtendedMode()
changes the behavior of terminal.以下代码显示了使用tea.WithMouseAllMotion()
时会发生什么。r.out.EnableMouseExtendedMode()
更改了终端的行为。 https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L506} else if p.startupOptions&withMouseAllMotion != 0 { p.renderer.enableMouseAllMotion() p.renderer.enableMouseSGRMode() }https://github.com/charmbracelet/bubbletea/blob/master/standard_renderer.go#L383-L388
func (r *standardRenderer) enableMouseSGRMode() { r.mtx.Lock() defer r.mtx.Unlock() r.out.EnableMouseExtendedMode() }Below code show why you can stop your program properly use SIGINT and SIGTERM. tea will shutdown when receiving SIGINT or SIGTERM. During shutdown
restoreTerminalState
is called which callsdisableMouse
, therefore your terminal works as usual.以下代码显示了为什么您可以使用 SIGINT 和 SIGTERM 正确停止程序。tea 在收到 SIGINT 或 SIGTERM 时将关闭。在关闭期间调用restoreTerminalState
,它调用disableMouse
,因此您的终端像往常一样工作。 https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L212-L246func (p *Program) handleSignals() chan struct{} { ch := make(chan struct{}) // Listen for SIGINT and SIGTERM. // // In most cases ^C will not send an interrupt because the terminal will be // in raw mode and ^C will be captured as a keystroke and sent along to // Program.Update as a KeyMsg. When input is not a TTY, however, ^C will be // caught here. // // SIGTERM is sent by unix utilities (like kill) to terminate a process. go func() { sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) defer func() { signal.Stop(sig) close(ch) }() for { select { case <-p.ctx.Done(): return case <-sig: if atomic.LoadUint32(&p.ignoreSignals) == 0 { p.msgs <- QuitMsg{} return } } } }() return ch }https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L316-L328
case msg := <-p.msgs: // Filter messages. if p.filter != nil { msg = p.filter(model, msg) } if msg == nil { continue } // Handle special internal messages. switch msg := msg.(type) { case QuitMsg: return model, nilhttps://github.com/charmbracelet/bubbletea/blob/master/tea.go#L572
p.shutdown(killed)https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L635-649
func (p *Program) shutdown(kill bool) { if p.renderer != nil { if kill { p.renderer.kill() } else { p.renderer.stop() } } _ = p.restoreTerminalState() if p.restoreOutput != nil { _ = p.restoreOutput() } p.finished <- struct{}{} }https://github.com/charmbracelet/bubbletea/blob/master/tty.go#L25-L40
func (p *Program) restoreTerminalState() error { if p.renderer != nil { p.renderer.disableBracketedPaste() p.renderer.showCursor() p.disableMouse() if p.renderer.altScreen() { p.renderer.exitAltScreen() // give the terminal a moment to catch up time.Sleep(time.Millisecond * 10) //nolint:gomnd } } return p.restoreInput() }https://github.com/charmbracelet/bubbletea/blob/master/tea.go#L299-L303
func (p *Program) disableMouse() { p.renderer.disableMouseCellMotion() p.renderer.disableMouseAllMotion() p.renderer.disableMouseSGRMode() }
OK, thanks bro.