The upstream open crate keeps making stuff async/blocking/not-working on
windows, so this is a step towards removing this dependency.
refs: https://github.com/wez/wezterm/issues/3288
The static OnceCell didn't run destructors, so shift to something
where we can explicitly trigger them to run and clean up the
temporary directory area.
This allows the decoder thread to live only long enough
to decode the full set of frames; we can then store the
leases in memory and pull them in only when there is
a cache miss.
refs: https://github.com/wez/wezterm/issues/3263
Adopt the new blob lease layer to storing and referencing
image files.
This reduces the number of open files needed when
images are being displayed in the terminal.
refs: https://github.com/wez/wezterm/issues/3263
Continuing from the previous commit, this shifts:
* In-memory data -> temporary file
* Image decoding -> background thread
The background thread asynchronously decodes frames and
sends them to the render thread via a bounded channel.
While decoding frames, it writes them, uncompressed, to
a scratch file so that when the animation loops, it is
a very cheap operation to rewind and pull that data
from the file, without having to burn CPU to re-decode
the data from the start.
Memory usage is bounded to 4 uncompressed frames while
decoding, then 3 uncompressed frames (triple buffered)
while looping over the rest.
However, disk usage is N uncompressed frames.
refs: https://github.com/wez/wezterm/issues/3263
This makes decoding animation frames a lazy operation, but it
comes at the cost of needing to re-decode the image from scratch
when it loops, because the image crate doesn't provide a way
to rewind its frame iterator.
That initial decode can have a significant time cost; a small
webp file consistently takes 150ms to decode, which is too
much to do inline in the render thread.
Next steps will be to move that cost off the render thread.
In #3260, the candidate animation had a frame delay of 1ms. That is
problematic because there is no hope for wezterm running at 60fps (a
frame delay of ~16ms) to render that without dropping frames.
A consequence of such a short frame delay, coupled with the way that
images are carved up into cells, is that individual cells can easily
become visibled de-synchronized from their neighbors and results in what
looks like mpeg artifacts but are really regions where the cells are a
few frames ahead/behind their neighbors.
The solution is to clamp the frame duration up to match the minimum
displayable frame duration as governed by max_fps.
refs: https://github.com/wez/wezterm/issues/3260
I don't recall changing it to copy to the primary selection only, and
it doesn't feel like it is something I would want anyway, and I think
it generally makes things annoying for all but power users
https://fosstodon.org/@trentskunk@mstdn.social/109808345817367266
Threads through a GuiPosition from mux window creation to allow it to be
used when the corresponding gui window is created.
SpawnCommand now has an optional position field to use for that purpose.
```lua
wezterm.mux.spawn_window {
position = {
x = 10,
y = 300,
-- Optional origin to use for x and y.
-- Possible values:
-- * "ScreenCoordinateSystem" (this is the default)
-- * "MainScreen" (the primary or main screen)
-- * "ActiveScreen" (whichever screen hosts the active/focused window)
-- * {Named="HDMI-1"} - uses a screen by name. See wezterm.gui.screens()
-- origin = "ScreenCoordinateSystem"
},
}
```
refs: https://github.com/wez/wezterm/issues/2976
The motivation here was to remove some similar but not quite the same
logic that existed for starting up when using `wezterm connect`.
Now `wezterm connect DOMAIN` is implemented as `wezterm start --domain
DOMAIN --attach --always-new-process` and a little extra hand-wave to
ensure that the default domain is set correctly.
The startup events have been refactored a bit; a new gui-attached
event is emitted after attaching and starting any default programs
in the selected domain at startup.
That event can be used to maximize windows and so on, if desired.
The gui-attached event is independent of the startup command and fires
for `wezterm connect`, which `gui-startup` did not (and could not) do.
The tcgetpgrp call appears to have high variance in latency, ranging
from 200-700us on my system.
If you have 10 tabs and mouse over the tab bar, that's around 7ms
spent per frame just figuring out the foreground process; that doesn't
include actually extracting the process executable or current working
directory paths.
This was exacerbated by the mouse move events triggering a tab bar
recompute on every pixel of mouse movement.
This commit takes the following steps to resolve this:
* We now only re-compute the tab bar when the UI item is changed by
a mouse movement
* A simple single-item cache is now used on unix that allows the caller
to proceed quickly with stale-but-probably-still-mostly-accurate data
while queuing up an update to a background thread which can absorb
the latency.
The result of this is that hovering over several tabs in quick
succession no longer takes a noticeable length of time to render the
hover, but the consequence is that the contents of a given tab may be
stale by 300-400ms.
I think that trade-off is worth while.
We already have a similar trade-off on Windows, although we don't
yet do the updates in a different thread on Windows. Perhaps in
a follow up commit?
refs: https://github.com/wez/wezterm/issues/2991
On my mac, depending the config, I noticed that modals didn't always
render immediately; force a window invalidation after assigning to
encourage them to do so.
The recently added app delegate was telling cocoa that we'd decide
to quit later in response to termination requests, blocking
shutdown/logout/restart.
This commit introduces a macos native modal alert to let the user
decide whether to quit or not.
While testing this, I noticed that in some cases, our internal choice
to quit had no effect. Reading the fine print of NSApp::stop, it sounds
like calling it from a modal context will only stop a modal rather then
exit out of NSApp::run, so we explicitly bounce through an event
callback to try to make it exit from the right place.
I'm not 100% convinced by this. I've left some debug prints in for
now to see if those give some insight in the future.
refs: https://github.com/wez/wezterm/issues/2944