mirror of https://github.com/mum4k/termdash.git
214 lines
7.9 KiB
Markdown
214 lines
7.9 KiB
Markdown
# Terminal Dashboard High-Level Design
|
|
|
|
## Objective
|
|
|
|
Develop infrastructure of dashboard widgets. The widgets should support both
|
|
input (mouse and keyboard) and output (display of information to the user).
|
|
|
|
Fulfil the requirements outlined in the main
|
|
[README](http://github.com/mum4k/termdash).
|
|
|
|
## Background
|
|
|
|
The terminal dashboard allows placement of configurable widgets onto the terminal.
|
|
|
|
A widget displays some information to the user, e.g. A graph, a chart, a
|
|
progress bar. A widget can receive information from the user in the form of
|
|
events, e.g. Mouse or keyboard input.
|
|
|
|
The widgets aren't placed onto the terminal directly, instead the terminal is
|
|
organized into containers. Each container can contain either a widget or
|
|
other containers.
|
|
|
|
## Overview
|
|
|
|
The terminal dashboard consists of the following layers:
|
|
|
|
- Terminal.
|
|
- Infrastructure.
|
|
- Widgets.
|
|
|
|
The **terminal layer** abstracts the terminal implementation. A real terminal
|
|
implementation is used in production when displaying data to the user. A fake
|
|
terminal implementation is used in widget unit tests and system tests. Other
|
|
implementations are possible, e.g. Image export. The terminal layer is private,
|
|
neither the users of this library nor the widgets interact with the terminal
|
|
directly.
|
|
|
|
The **infrastructure layer** is responsible for container management, tracking
|
|
of keyboard and mouse focus and distribution and handling of external events
|
|
like resizing of the terminal. The infrastructure layer also decides when to
|
|
flush the buffer and refresh the screen. I.e. The widgets update content of a
|
|
back buffer and the infrastructure decides when it is synchronized to the
|
|
terminal.
|
|
|
|
The **widgets layer** contains the implementations of individual widgets. Each
|
|
widget receives a canvas from the container on which it presents its content to
|
|
the user. Widgets indicate to the infrastructure layer if they support input
|
|
events, which are then forwarded from the infrastructure layer.
|
|
|
|
The user interacts with the widget API when constructing individual widgets and
|
|
with the container API when placing the widgets onto the dashboard.
|
|
|
|
<p align="center">
|
|
<img src="images/hld.png" width="50%">
|
|
</p>
|
|
|
|
## Detailed design
|
|
|
|
### Terminal
|
|
|
|
The terminal provides access to the input and output.
|
|
|
|
It allows to:
|
|
|
|
- Set values and attributes of cells on a back buffer representing a 2-D
|
|
canvas.
|
|
- Flush the content of the back buffer to the output.
|
|
- Manipulate the cursor position and visibility.
|
|
- Allow the infrastructure to read input events (keyboard, mouse, terminal
|
|
resize, etc...).
|
|
|
|
### Infrastructure
|
|
|
|
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 fulfils the following
|
|
functions:
|
|
|
|
- Allow subscribers to specify the type of events they want to receive.
|
|
- 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
|
|
towards the subscriber and eventually implements a tail-drop strategy.
|
|
|
|
The infrastructure itself is an input event subscriber and processes resize and
|
|
error events. The infrastructure panics on error events by default, unless an
|
|
error handler is provided by the user. Each widget that registers for keyboard
|
|
or mouse events is also an event subscriber. Any errors that happen while
|
|
processing an input event are send back to the EDS in the form of an Error
|
|
event and are processed by the infrastructure.
|
|
|
|
|
|
#### Input keyboard focus
|
|
|
|
The infrastructure tracks focus. Only the focused widget receives keyboard
|
|
events. Focus can be changed using mouse or keyboard shortcuts. The focused
|
|
widget is highlighted on the dashboard.
|
|
|
|
#### Containers
|
|
|
|
The container provides a way of splitting the dashboard down to smaller
|
|
elements where individual widgets can be placed. Each container can be split to
|
|
multiple sub containers. A container contains either a sub container or a
|
|
widget.
|
|
|
|
Container is responsible for coordinate translation. Each widget receives a
|
|
virtual canvas it can draw on. Each of these canvases starts at coordinates
|
|
image.Point{0, 0}. The parent container translates coordinates from the virtual
|
|
canvas to the real terminal. The container therefore enforces limits for widgets.
|
|
|
|
Containers can be styled with borders and other options.
|
|
|
|
#### Flushing the terminal
|
|
|
|
All widgets indirectly write to the back buffer of the terminal implementation.
|
|
The changes to the back buffer only become visible when the infrastructure
|
|
flushes its content.
|
|
|
|
#### Terminal resizing
|
|
|
|
The terminal resize events are processed by the infrastructure. Each widget
|
|
indicates its desired and minimum size for its canvas when registering with its
|
|
parent container.
|
|
|
|
The parent container in turn informs the widget what is the actual size of its
|
|
canvas. The infrastructure guarantees that the actual size won't ever be
|
|
smaller than the advertised minimum and guarantees that the size will keep the
|
|
aspect ration requested by the widget.
|
|
|
|
When the size of terminal changes, the infrastructure resizes all containers
|
|
according to the rules outlined above, asks all widgets to redraw their
|
|
canvases and flushes to the back buffer to the terminal.
|
|
|
|
### Widgets
|
|
|
|
Users of the terminal dashboard construct the widgets directly. Therefore each
|
|
widget can define its own options and API for setting values (e.g. The
|
|
displayed percentage on a progress bar). The users then create the desired
|
|
container splits and place each widget into a dedicated container.
|
|
|
|
Each widget receives a canvas from the parent container, the widget can draw
|
|
anything on the canvas as long as it stays within the limits. Helper libraries
|
|
are developed that allow placement and drawing of common elements like lines or
|
|
geometric shapes.
|
|
|
|
## APIs
|
|
|
|
### Terminal API
|
|
|
|
The Terminal API is an interface private to the terminal dashboard library. Its
|
|
primary purpose is to act as a shim layer over different terminal
|
|
implementations.
|
|
|
|
The Terminal API is defined in the
|
|
[terminalapi](http://github.com/mum4k/termdash/terminalapi/terminalapi.go)
|
|
package.
|
|
|
|
The **Event()** method returns the next input event. Different input event
|
|
types are defined in the
|
|
[event.go](http://github.com/mum4k/termdash/terminalapi/event.go)
|
|
file.
|
|
|
|
### Container API
|
|
|
|
The Container API is used to split the terminal and place the widgets. Each
|
|
container can be split to two sub containers or have a widget placed into it.
|
|
A container can be split either horizontally or vertically.
|
|
|
|
The containers further accept styling options and alignment options. The
|
|
following indicates how the Container API will be used.
|
|
|
|
The Container API is defined in the
|
|
[container](http://github.com/mum4k/termdash/container/container.go)
|
|
package.
|
|
|
|
A demonstration how this is used from the client perspective is in the
|
|
[container_test.go](http://github.com/mum4k/termdash/container/container_test.go)
|
|
file.
|
|
|
|
### Widget API
|
|
|
|
Each widget must implement the Widget API. All widget implementations must
|
|
be thread-safe since the calls that update the displayed values come in
|
|
concurrently with requests and events from the infrastructure.
|
|
|
|
The Widget API is defined in the
|
|
[widget](http://github.com/mum4k/termdash/widget/widget.go)
|
|
package.
|
|
|
|
Each widget gets a Canvas to draw on. The Canvas API is defined in the
|
|
[canvas](http://github.com/mum4k/termdash/canvas/canvas.go)
|
|
package.
|
|
|
|
## Testing plan
|
|
|
|
Unit test helpers are provided with the terminal dashboard library, these
|
|
include:
|
|
|
|
- A fake implementation of the terminal API.
|
|
- Unit test comparison helpers to verify the content of the fake terminal.
|
|
- Visualization tools to display differences between the expected and the
|
|
actual.
|
|
|
|
## Document history
|
|
|
|
Date | Author | Description
|
|
------------|--------|-----------------------------------
|
|
24-Mar-2018 | mum4k | Initial draft.
|
|
20-Feb-2019 | mum4k | Added notes on event distribution.
|