diff --git a/CHANGELOG.md b/CHANGELOG.md index 585197b..4d4048e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Lint issues found on the Go report card. +## [0.9.1] - 15-May-2019 + +### Fixed + +- Termdash could deadlock when a `Button` or a `TextInput` was configured to + call the `Container.Update` method. + ## [0.9.0] - 28-Apr-2019 ### Added @@ -273,7 +280,8 @@ identifiers shouldn't be used externally. - The Gauge widget. - The Text widget. -[unreleased]: https://github.com/mum4k/termdash/compare/v0.9.0...devel +[unreleased]: https://github.com/mum4k/termdash/compare/v0.9.1...devel +[0.9.1]: https://github.com/mum4k/termdash/compare/v0.9.0...v0.9.1 [0.9.0]: https://github.com/mum4k/termdash/compare/v0.8.0...v0.9.0 [0.8.0]: https://github.com/mum4k/termdash/compare/v0.7.2...v0.8.0 [0.7.2]: https://github.com/mum4k/termdash/compare/v0.7.1...v0.7.2 diff --git a/README.md b/README.md index 7e61dd7..e34072f 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,6 @@ go get -u github.com/mum4k/termdash The usage of most of these elements is demonstrated in [termdashdemo.go](termdashdemo/termdashdemo.go). To execute the demo: - ```go go run github.com/mum4k/termdash/termdashdemo/termdashdemo.go ``` @@ -194,6 +193,20 @@ before contributing. If you're developing a new widget, please see the [widget development](doc/widget_development.md) section. +Termdash uses [this branching model](https://nvie.com/posts/a-successful-git-branching-model/). When you fork the repository, base your changes off the [devel](https://github.com/mum4k/termdash/tree/devel) branch and the pull request should merge it back to the devel branch. Commits to the master branch are limited to releases, major bug fixes and documentation updates. + +# Similar projects in Go + +- [termui](https://github.com/gizak/termui) +- [tview](https://github.com/rivo/tview) +- [gocui](https://github.com/jroimartin/gocui) +- [gowid](https://github.com/gcla/gowid) +- [clui](https://github.com/VladimirMarkelov/clui) +- [tui-go](https://github.com/marcusolsson/tui-go) + +# Projects using Termdash + +- [grafterm](https://github.com/slok/grafterm): Metrics dashboards visualization on the terminal. # Disclaimer diff --git a/doc/hld.md b/doc/hld.md index 5e663e0..a38eb8a 100644 --- a/doc/hld.md +++ b/doc/hld.md @@ -5,7 +5,7 @@ Develop infrastructure of dashboard widgets. The widgets should support both input (mouse and keyboard) and output (display of information to the user). -Fulfill the requirements outlined in the main +Fulfil the requirements outlined in the main [README](http://github.com/mum4k/termdash). ## Background @@ -76,11 +76,11 @@ The infrastructure handles terminal setup, input events and manages containers. #### Input events The infrastructure regularly polls events from the terminal layer and feeds -them into the event distribution system (EDS). The EDS fulfills the following +them into the event distribution system (EDS). The EDS fulfils the following functions: - Allow subscribers to specify the type of events they want to receive. -- Distributeis events in a non-blocking manner, i.e. a single slow subscriber +- Distributes events in a non-blocking manner, i.e. a single slow subscriber cannot slow down other subscribers. - Events to each subscriber are throttled, if a subscriber builds a long tail of unprocessed input events, the EDS selectively drops repetitive events diff --git a/termdashdemo/termdashdemo.go b/termdashdemo/termdashdemo.go index a37dd75..f2b6ab2 100644 --- a/termdashdemo/termdashdemo.go +++ b/termdashdemo/termdashdemo.go @@ -13,7 +13,7 @@ // limitations under the License. // Binary termdashdemo demonstrates the functionality of termdash and its various widgets. -// Exist when 'q' is pressed. +// Exits when 'q' is pressed. package main import ( diff --git a/widgets/button/button.go b/widgets/button/button.go index 0a7544e..beaf5ca 100644 --- a/widgets/button/button.go +++ b/widgets/button/button.go @@ -154,11 +154,8 @@ func (b *Button) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error { ) } -// Keyboard processes keyboard events, acts as a button press on the configured -// Key. -// -// Implements widgetapi.Widget.Keyboard. -func (b *Button) Keyboard(k *terminalapi.Keyboard) error { +// activated asserts whether the keyboard event activated the button. +func (b *Button) keyActivated(k *terminalapi.Keyboard) bool { b.mu.Lock() defer b.mu.Unlock() @@ -166,16 +163,27 @@ func (b *Button) Keyboard(k *terminalapi.Keyboard) error { b.state = button.Down now := time.Now().UTC() b.keyTriggerTime = &now + return true + } + return false +} + +// Keyboard processes keyboard events, acts as a button press on the configured +// Key. +// +// Implements widgetapi.Widget.Keyboard. +func (b *Button) Keyboard(k *terminalapi.Keyboard) error { + if b.keyActivated(k) { + // Mutex must be released when calling the callback. + // Users might call container methods from the callback like the + // Container.Update, see #205. return b.callback() } return nil } -// Mouse processes mouse events, acts as a button press if both the press and -// the release happen inside the button. -// -// Implements widgetapi.Widget.Mouse. -func (b *Button) Mouse(m *terminalapi.Mouse) error { +// mouseActivated asserts whether the mouse event activated the button. +func (b *Button) mouseActivated(m *terminalapi.Mouse) bool { b.mu.Lock() defer b.mu.Unlock() @@ -183,7 +191,18 @@ func (b *Button) Mouse(m *terminalapi.Mouse) error { b.state = state b.keyTriggerTime = nil - if clicked { + return clicked +} + +// Mouse processes mouse events, acts as a button press if both the press and +// the release happen inside the button. +// +// Implements widgetapi.Widget.Mouse. +func (b *Button) Mouse(m *terminalapi.Mouse) error { + if b.mouseActivated(m) { + // Mutex must be released when calling the callback. + // Users might call container methods from the callback like the + // Container.Update, see #205. return b.callback() } return nil diff --git a/widgets/textinput/textinput.go b/widgets/textinput/textinput.go index 0e7455f..2f769d7 100644 --- a/widgets/textinput/textinput.go +++ b/widgets/textinput/textinput.go @@ -214,9 +214,11 @@ func (ti *TextInput) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error { return nil } -// Keyboard processes keyboard events. +// keyboard processes keyboard events. +// Returns a bool indicating if the content was submitted and the text in the +// field at submission time. // Implements widgetapi.Widget.Keyboard. -func (ti *TextInput) Keyboard(k *terminalapi.Keyboard) error { +func (ti *TextInput) keyboard(k *terminalapi.Keyboard) (bool, string) { ti.mu.Lock() defer ti.mu.Unlock() @@ -245,21 +247,33 @@ func (ti *TextInput) Keyboard(k *terminalapi.Keyboard) error { ti.editor.reset() } if ti.opts.onSubmit != nil { - return ti.opts.onSubmit(text) + return true, text } default: if err := wrap.ValidText(string(k.Key)); err != nil { // Ignore unsupported runes. - return nil + return false, "" } if ti.opts.filter != nil && !ti.opts.filter(rune(k.Key)) { // Ignore filtered runes. - return nil + return false, "" } ti.editor.insert(rune(k.Key)) } + return false, "" +} + +// Keyboard processes keyboard events. +// Implements widgetapi.Widget.Keyboard. +func (ti *TextInput) Keyboard(k *terminalapi.Keyboard) error { + if submitted, text := ti.keyboard(k); submitted { + // Mutex must be released when calling the callback. + // Users might call container methods from the callback like the + // Container.Update, see #205. + return ti.opts.onSubmit(text) + } return nil }