neovim-gtk icon indicating copy to clipboard operation
neovim-gtk copied to clipboard

Listen to VimResized event and resize the window when needed

Open jacobmischka opened this issue 3 years ago • 3 comments

Fixes #156

jacobmischka avatar Oct 17 '21 12:10 jacobmischka

Hm, so trying this out on Fedora in Wayland this seems to give neovim-gtk a case of the "jitters" animation I didn't go far back enough in the git history of my fork to see if this was because of my resizing changes in 2ec2eb19e14fe08a10928f152e91403edc25cc8f, because I made the mistake back then of committing an apparently out of date Cargo.lock (a mistake I have been more cautious to avoid since) which makes it annoying to go back that far in repo history and attempt applying this patch there.

Regardless though, I don't think this patch is doing things the right way. The thing is we already know what the new requested size of the window is going to be before we even send it to nvim, so we should try to enforce snapping instead by always setting the size of the GTK+ window on resize events to the requested size (see the ResizeStatus I added in that commit) of neovim, even if that size hasn't changed. This might help with the jittering problem.

Lyude avatar Oct 24 '21 01:10 Lyude

Good point. My patch from the initial repo did used the cached size value, but I got a bit confused by your changes in that area I guess. I'll take another look soon and update this PR.

jacobmischka avatar Oct 24 '21 10:10 jacobmischka

Good point. My patch from the initial repo did used the cached size value, but I got a bit confused by your changes in that area I guess. I'll take another look soon and update this PR.

No problem, let me know if you have any questions! Since I'm here I might as well try to explain the resizing logic a big more thoroughly since it definitely may seem very strange at first. Some necessary precursor though: most neovim RPC requests, including nvim_ui_try_resize(), are handled asynchronously by nvim. This means they'll return immediately, even if nvim hasn't handled them yet. This has exceptions though. For instance, if nvim is displaying a message that requires user confirmation, then nvim_ui_try_request() will block until that message isn't being displayed any longer.

This caused a few problems: first off, remember that neovim-gtk "times out" if it sends an RPC request that doesn't return in a given amount of time. This meant that if we tried to resize while a prompt was up, the request would block the UI and then neovim-gtk would eventually abort. Even if we just got rid of the timeout, we'd still be blocking the UI infinitely. The other problem this caused was that neovim used a timeout with a fixed interval to "throttle" resize requests, which I assume was an attempt to prevent unneeded resize requests from being sent to neovim-gtk. This may seem silly, but the reason you'd want to do that is because even though nvim handles resize requests asynchronously, this just means each individual request is put up in a queue to be processed later. So if you were to send resize requests to nvim like so:

  • Resize to (80, 80)
  • Resize to (80, 81)
  • Resize to (80, 82)
  • Resize to (80, 83)

The user would see nvim resize and redraw itself a minimum of 4 times. This isn't an unlikely scenario either, since when dragging a window the movement from the cursor will be gradual and not immediate. In an ideal world, nvim would handle one resize request at a time and simply squash any subsequent requests into another update. So if it handled the first resize request, but did not finish handling it until after receiving the 3 subsequent resize requests, the user would see nvim resize and redraw twice: once to (80, 80) and then to (80, 83). This would still potentially mean up to 4 resize/redraws if nvim was able to handle each event immediately, which would likely happen locally. But in scenarios where we might be connected to a remote nvim instance, a feature I'd like to add support for in the future, such optimizations might be quite helpful to reduce how many redraws have to be sent back over the network.

That's basically what my resizing code is trying to do - optimize this sort of thing as much as we can from the client side by ensuring we never have more then one resize request in nvim pending at a time. Because our resize requests are async return immediately however, this means we technically don't have any way of knowing when they're handled and taken out of the event queue by default. This is where VimResized comes in, because we can use that event to make nvim notify us once it's actually handled the resize request. With all this, we can ensure we only send nvim resize requests as fast as it's handling them.

I should note that I would like to figure out a better synchronization mechanism then VimResized in the future. I would have simply used receiving a grid_resize event from a UI redraw event as the sync mechanism here, but it doesn't seem like there's much of a way we can guarantee that a grid_resize event is actually associated with the resize request we sent ourselves. With an autocommand and atomic RPC calls we can however, since it lets us hook up the autocommand and then resize with a guarantee that no other client resize requests can occur between those two steps. TBH though, I have a feeling the only way to have a better solution here would be to just improve the way nvim itself handles resizing.

Anyway-hopefully that helps explain the changes! Let me know if you have any more questions or feedback on that, I'm happy to answer.

Lyude avatar Oct 24 '21 17:10 Lyude