aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ui/ui.go
Commit message (Collapse)AuthorAgeFilesLines
* ui: fix deadlocks in message channelRobin Jarry2023-05-201-38/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | There are several ways the ui message channel can fill up leading to deadlocks. 1) Invalidate() changes the value of uiState to DIRTY. The following call sequence: QueueRedraw() Invalidate() QueueRedraw() Leads to multiple nil messages being queued in the message channel whereas one could assume that the second QueueRedraw() would do nothing. This is caused by the tri-state nature of uiState. 2) We use the same channel to convey state change, keyboard events and redraw requests. Since a keyboard event almost always triggers a redraw, we end up trying to append a redraw message in the same goroutine that reads from the channel. This triggers a deadlock when there are more than 50 pending messages. Solve the issue by using multiple channels, one per type of message that needs to be sent to the main ui thread. Remove QueueRedraw() and merge its functionality in Invalidate(). Only use a DIRTY/CLEAN state to determine if something needs to be queued in the redraw channel. Use a channel for quitting instead of an atomic. Restructure some code functions to have a cleaner API. Use a for loop in the main thread and select from all channels. Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Koni Marti <koni.marti@gmail.com> Tested-by: Maarten van Gompel <proycon@anaproy.nl>
* ui: avoid races with queue redrawRobin Jarry2023-04-271-15/+11
| | | | | | | | | | | | | | | | | | When calling ui.QueueRedraw() and a redraw is currently in progress, the redraw int value still holds REDRAW_PENDING since it is updated once the redraw is finished. This can lead to incomplete screen redraws on the embedded terminal. Even changing the redraw value before starting to redraw is exposed to races. Use a single atomic int to represent the state of the UI so that there cannot be any confusion. Rename the constants to make them less confusing. Fixes: b148b94cfe1f ("ui: avoid duplicate queued redraws") Reported-by: Jason Cox <dev@jasoncarloscox.com> Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Jason Cox <dev@jasoncarloscox.com>
* ui: avoid duplicate queued redrawsRobin Jarry2023-04-261-1/+8
| | | | | | | | | No need to queue multiple nil messages to force multiple redraws. Only one is required. When the screen is redrawn, clear the queued redraw flag. Signed-off-by: Robin Jarry <robin@jarry.cc> Acked-by: Tim Culverhouse <tim@timculverhouse.com>
* lint: add missing panic handlers in goroutinesRobin Jarry2023-01-061-0/+2
| | | | | | | | These issues were all reported by the new custom analyzer introduced in previous commit. Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
* ui: invalidate ui when queuing redrawTim Culverhouse2022-11-061-2/+3
| | | | | | | | | | | | | | | The QueueRedraw function should always be preceeded by a call to ui.Invalidate in order to make a redraw a occur. In one instance, this was not done and it was possible for the UI to not redraw itself (when a terminal closes, a UI redraw request is made but it is possible for the UI to not be invalidated as a result of the close). Move the call to Invalidate into the QueueRedraw function to ensure that every QueueRedraw call will redraw the screen. Fixes: https://todo.sr.ht/~rjarry/aerc/98 Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Robin Jarry <robin@jarry.cc>
* aercmsg: add AercFuncMsg and QueueFuncTim Culverhouse2022-10-181-0/+10
| | | | | | | | | | Introduce AercFuncMsg and QueueFunc. These are used in combination to queue a function to be run in the main goroutine. This can be used to prevent data races in delayed function calls (ie from debounce functions). Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Robin Jarry <robin@jarry.cc>
* invalidatable: cleanup dead codeTim Culverhouse2022-10-121-1/+1
| | | | | | | | Remove invalidatable type and all associated calls. All items can directly invalidate the UI. Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Robin Jarry <robin@jarry.cc>
* invalidatable: always mark ui as dirty OnInvalidateTim Culverhouse2022-10-071-7/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | The Invalidatable struct is designed so that a widget can have a callback function ran when it is Invalidated. This is used to cascade up the widget tree, marking things as Invalid along the way so that only Invalid widgets are drawn. However, this is only implemented at the grid cell level for checks if the cell is invalidated -- and the grid cells are never set back to a "valid" state. The effect of this is that no matter what is invalidated, the entire UI gets drawn again. The calling through the Invalidate callbacks creates *several* race conditions, as Invalidate is called from several different goroutines, and many widgets call invalidate on their parent or children. Tcell has optimizations to only rerender screen cells that have changed their rune and style. The only performance penalty by redrawing the entire screen for aerc is the operations *within the aerc draw methods*. Most of these are not expensive and have relatively no impact on performance. Skip all of the OnInvalidates, and directly invalidate the UI when DoInvalidate is called by a widget. This reduces data races, and simplifies the widget redraw logic signficantly. Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Robin Jarry <robin@jarry.cc>
* render: clean up render codeTim Culverhouse2022-10-071-11/+1
| | | | | | | | | | | | The render method sets everything as invalid if there was a popover. This is no longer necessary, as everything is redrawn anyways. Remove the check and extra atomic set of dirty and invalidate. Remove unused return value Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Robin Jarry <robin@jarry.cc>
* aerc: use single event loopTim Culverhouse2022-10-071-16/+18
| | | | | | | | | Combine tcell events with WorkerMessages to better synchronize state with IO and UI. Remove Tick loop for rendering. Use events to trigger renders. Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Robin Jarry <robin@jarry.cc>
* events: introduce AercMsg and QueueRedrawTim Culverhouse2022-10-071-0/+8
| | | | | | | | | | Add AercMsg as a main interface for internal communication in aerc in preparation for a main event loop. Add a QueueRedraw function to to trigger a redraw. This will be needed for widgets which should be drawn after some delay (completions, terminal, for example) Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Robin Jarry <robin@jarry.cc>
* term: add bracketed paste supportTim Culverhouse2022-09-141-0/+1
| | | | | | | | Allow forwarding paste events to embedded applications. When a bracketed paste is in progress, do not process any command bindings. Signed-off-by: Robin Jarry <robin@jarry.cc> Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
* ui: cleanup internals and apiRobin Jarry2022-09-141-15/+7
| | | | | | | | | | | | | | | | Now that tcell events are handled in a goroutine, no need for a channel to buffer them. Rename ui.Tick() to ui.Render() and ui.Run() to ui.ProcessEvents() to better reflect what these functions do. Move screen.PollEvent() into ui.ProcessEvents(). Register the panic handler in ui.ProcessEvents(). Remove aerc.ui.Tick() from DecryptKeys(). What the hell was that? Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Tim Culverhouse <tim@timculverhouse.com>
* ui: process tcell events in a separate go routine from renderingTim Culverhouse2022-09-131-17/+16
| | | | | | | | | | | | | The UI runs off a 16 ms ticker. If no render is required, and no event is seen, aerc waits 16 ms before checking for new events or render requests. This severely limits handling of events from tcell, and is particularly noticeable on pasting of large quantities of text. Process tcell events in a separate go routine from the render loop. Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Moritz Poldrack <moritz@poldrack.dev> Acked-by: Robin Jarry <robin@jarry.cc>
* lint: homogenize operations and minor fixes (gocritic)Moritz Poldrack2022-08-041-2/+1
| | | | | | | | | | | | | | | | | | Apply GoDoc comment policy (comments for humans should have a space after the //; machine-readable comments shouldn't) Use strings.ReplaceAll instead of strings.Replace when appropriate Remove if/else chains by replacing them with switches Use short assignment/increment notation Replace single case switches with if statements Combine else and if when appropriate Signed-off-by: Moritz Poldrack <moritz@poldrack.dev> Acked-by: Robin Jarry <robin@jarry.cc>
* lint: apply new formatting rulesMoritz Poldrack2022-08-011-1/+0
| | | | | | | Run `make fmt`. Signed-off-by: Moritz Poldrack <git@moritz.sh> Acked-by: Robin Jarry <robin@jarry.cc>
* logging: added a log on panicMoritz Poldrack2022-03-231-0/+3
| | | | | | | | | | | | | Since panics still regularly "destroy" the terminal, it is hard to get a stack trace for panics you do not anticipate. This commit adds a panic handler that automatically creates a logfile inside the current working directory. It has to be added to every goroutine that is started and will repair the terminal on a panic. Signed-off-by: Moritz Poldrack <git@moritz.sh> Acked-by: Robin Jarry <robin@jarry.cc>
* update tcell to v2 and enable TrueColor supporty0ast2020-12-181-1/+1
| | | | | | | | | Also update to the tcell v2 PaletteColor api, which should keep the chosen theme of the user intact. Note, that if $TRUECOLOR is defined and a truecolor given, aerc will now stop clipping the value to one of the theme colors. Generally this is desired behaviour though.
* libui: don't require beeper for main contentDrew DeVault2020-05-051-2/+4
|
* Initial support for PGP decryption & signaturesDrew DeVault2020-03-031-0/+4
|
* Add popoversBen Burwell2019-12-211-3/+23
| | | | | | | | | | | | A popover is a special UI element which can be layered over the rest of the UI (i.e. it is painted last) and can fall anywhere on the screen, not just with the bounds of its parent's viewport/context. With these special abilities comes the restriction that only one popover may be visible on screen at once. Popovers are requested from the UI context passed to Draw calls and specify the anchor point and the desired dimensions. The popover is then fit to the available space and placed relative to the anchor point.
* Remove aerc specific code from the uiNicolai Dagestad2019-08-031-7/+5
| | | | | Separatiing the ui code from aerc makes it usable as a library in other projects.
* Ring bell when new messages arrive0.2.0Ben Burwell2019-07-291-1/+2
| | | | | | | | Add a "new-message-bell" option to the UI section of aerc.conf. A new hook into the message store allows the msglist widget to detect new messages being added to the displayed list. When new messages are delivered, and the new-message-bell option is enabled (as it is by default), the terminal will beep.
* Add MouseEnabled config settingJeffas2019-07-171-1/+3
| | | | | This patch adds the ability to control whether aerc captures mouseevents or not. By default it will be set to not capture events.
* Add clickable tabsJeffas2019-07-111-0/+1
| | | | | | | | | | | This introduces a new interface `Clickable`. I'd imagine this would be implemented for most widgets eventually and would allow for programs run in the terminal to also have their mouse events forwarded to them. For the tabs it was relatively simple to check that the position of the click is within the boxes for the tabs. For other components I'd imagine that some state representing their currently drawn bounding box would be useful.
* lib/ui/ui: use atomic instead of channelSimon Ser2019-05-191-26/+21
| | | | | | | | | This makes it so an atomic `invalid` value is used instead of an unbuffered channel. When many invalidations kick in, a lot of values were sent to the channel. (Since OnInvalidate's callback can be run in any goroutine, we need to be careful about races here.)
* Update internal state and draw from the same goroutineSimon Ser2019-05-191-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | This commit introduces a new Aerc.Tick function that should be called to refresh the internal state. This in turn makes each AccountView process worker events. The UI goroutine repeatedly refreshes the internal state before drawing a new frame. The reason for this is that many worker messages may need to be processed for a single frame, and drawing the UI is far slower than refreshing the internal state. This has been confirmed in my testing (calling Aerc.Tick only once per frame results in a slower display). Many synchronization code has been removed. We can now write widgets without having to care so much about races. The remaining sync users are: - widgets/spinner: the spinner value is updated from inside an internal goroutine - lib/ui/invalidatable: Invalidate may be called from any goroutine - lib/ui/grid: same - lib/ui/ui: an internal goroutine needs read access to UI.exit - worker/types/worker: Worker.callbacks is used for both worker and UI callbacks The exact goroutine requirements for Drawable have been documented.
* s/aerc2/aerc/gDrew DeVault2019-05-171-1/+1
|
* lib/ui: fix UI.Exit race conditionSimon Ser2019-05-051-2/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | UI.Exit can be accessed from goroutines drawing, goroutines executing commands and goroutines waiting for events. Write at 0x00c0002b2040 by main goroutine: main.main.func1() /home/simon/src/aerc2/aerc.go:76 +0x33d git.sr.ht/~sircmpwn/aerc2/widgets.(*Aerc).BeginExCommand.func1() /home/simon/src/aerc2/widgets/aerc.go:245 +0x89 git.sr.ht/~sircmpwn/aerc2/widgets.(*ExLine).Event() /home/simon/src/aerc2/widgets/exline.go:131 +0x442 git.sr.ht/~sircmpwn/aerc2/widgets.(*Aerc).Event() /home/simon/src/aerc2/widgets/aerc.go:116 +0x83c git.sr.ht/~sircmpwn/aerc2/widgets.(*Aerc).simulate() /home/simon/src/aerc2/widgets/aerc.go:109 +0x12a git.sr.ht/~sircmpwn/aerc2/widgets.(*Aerc).Event() /home/simon/src/aerc2/widgets/aerc.go:142 +0x722 git.sr.ht/~sircmpwn/aerc2/lib/ui.(*UI).Tick() /home/simon/src/aerc2/lib/ui/ui.go:75 +0x33f main.main() /home/simon/src/aerc2/aerc.go:94 +0x497 Previous read at 0x00c0002b2040 by goroutine 19: git.sr.ht/~sircmpwn/aerc2/lib/ui.Initialize.func1() /home/simon/src/aerc2/lib/ui/ui.go:45 +0x97 Goroutine 19 (running) created at: git.sr.ht/~sircmpwn/aerc2/lib/ui.Initialize() /home/simon/src/aerc2/lib/ui/ui.go:44 +0x372 main.main() /home/simon/src/aerc2/aerc.go:87 +0x3a9
* Add basic terminal widgetDrew DeVault2019-03-171-0/+1
|
* Add :quit commandDrew DeVault2019-03-151-5/+0
|
* Make repeated invalidations more efficientDrew DeVault2019-01-141-0/+10
|
* Clean up some old codeDrew DeVault2018-06-111-3/+6
|
* switch to tcell from termboxMarkus Ongyerth2018-06-011-18/+30
| | | | | | | | | | | | | This is a simple mostly straight forward switch to tcell in favor of termbox. It uses the tcell native api (not the compat layer) but does not make use of most features. Further changes should include moving to tcell's views.TextArea and the general built in widget behaviour instead of the current ad hoc implementation. Regression: Cursor isn't shown in ex-line
* Pull main aerc UI into widgetDrew DeVault2018-02-271-13/+6
|
* Split UI library and widgetsDrew DeVault2018-02-261-0/+79