ajalt/mordant

React to terminal size changes on JVM

mikehearn opened this issue · 10 comments

Currently, changing the size of the terminal window whilst a progress tracker runs will cause corrupt display, as the new size isn't taken into account. There's a sun.misc.Signal API I think, that would allow for reacting to the SIGWINCH on UNIX. On Windows it's harder, I don't think the JVM exposes anything for that, not even in private APIs. Perhaps jline has some code for it.

I think this would require some JNA code.

In the meantime, you could potentially poll terminal.info.updateTerminalSize(). The reason I don't do that automatically is because, due to the JNA bug mentioned in #86, we still have to shell out to stty on macs, which is potentially slow. If that ever gets resolved, we could check for a new size before each frame.

updateTerminalSize correctly detects the size change, but doesn't address the garbling of the output when the screen shrinks.
What seems to happen when using full width progress bars is that any reduction in the screen width causes the progress bar lines to wrap, so when the next animation frame comes the number of lines to clear is incorrect, and some of the lines become permanent above the animation.

Oh, good point. That's a tougher problem. It seems like the solution would be to keep track of the terminal size between frames and calculate where we think the cursor is after wrapping so that we can move back to the original position.

Should be something like previousHeight * ceil(previousWitdh/newWidth), for the case where the animation width covers the entire terminal width, and the newWidth is smaller than the previous.

Would printing the right number of backspace characters be an alternate way to clear the animation?

Backspace is a good idea, but unfortunately terminals handle it very differently. Some will ignore it entirely, some will only delete back to the start of the line etc.

Ok, so I implemented this in #151.

  • The Terminal will now check for size updates before it draws each frame, so you don't need to poll yourself, and we don't need SIGWINCH (so we can support Windows). Unfortunately, due the the JNI bug I mentioned, we don't do this on JVM macOS, since it's potentially a little too slow.
  • I try to calculate the hard wrap and compensate for it, but it's inconsistent across terminals. Even whether or not the cursor is visible seems to affect the results. So it may leave part of a frame on screen after the resize, but it will at least work on future frames.

If you're able to try out that branch and let me know what you think, I'd be appreciate it.

Thanks! I'll try and make time for it in the next few weeks.

We have our own multi-task progress bar implementation that uses Mordants rendering alongside its own, but it uses the animation APIs so it should work. Do you have any pictures of what the new multi-task tracker looks like? Perhaps we can migrate, although I do like our current look.

The JNA bug could be worked around by using Panama on modern JVMs. JNA isn't necessary anymore on those versions.

The multi bar looks pretty much the same, but

  • Animations are synchronized to the same framerate and start time, so you get less flickering with fewer redraws
  • Columns are (optionally) aligned

image

Panama looks promising, but it's still in preview, so it will be quite a while before we can drop JNA.

It's stable in JDK 22 releasing in two months. A multi-release jar would allow its use only when running on 22+.