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
In https://github.com/wez/wezterm/issues/2932 the user desired to have
brightened text without the boldness, as they were accustomed to that
behavior in a couple of other terminal emulators.
This commit changes the `bold_brightens_ansi_colors` from a simple
boolean to a tristate that allows for not changing the brightness,
changing the brightness, and changing the brightness while adjusting
the boldness down to normal levels.
boolean values are accepted for backwards compatibility.
This avoids a jarring situation where the width rubber-bands to fit
a run of narrower text when scrolling down through the list of options.
refs: https://github.com/wez/wezterm/issues/1485
```
thread 'main' panicked at 'Error in Surface::configure: Both `Surface` width and height must be non-zero. Wait to recreate the `Surface` until the window has non-zero area.', C:\Users\runneradmin\.cargo\registry\src\github.com-1ecc6299db9ec823\wgpu-0.14.0\src\backend\direct.rs:274:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
refs: https://github.com/wez/wezterm/issues/2881
This is moderately painful to do, because of some objc/cocoa lifetime
concern that causes a crash when attempting to simply replace the
entire menubar, so we try to find/update items instead.
refs: https://github.com/wez/wezterm/issues/1485
Fixup CopyMode key assignments; they were all incorrectly
labelled as activating copy mode, which isn't correct.
We're just using Debug labelling for them at the moment,
need to get that fixed properly.
refs: https://github.com/wez/wezterm/issues/1485
This commit safely registers key equivalents with the menubar. Safe in
this context means "doesn't override a key assignment from a key table".
For example, it would suck to define an application-wide key assignment
for a key combination that has a different assignment in a key table
that may be activated conditionally by some user-defined state/mode.
refs: https://github.com/wez/wezterm/issues/1485
Similar to the equivalent functionality in the launcher menu,
syntheisze some default commands based on the current state
of the launcher menu (config) and mux (domains, workspaces).
This does mean that the launcher menu may show duplicates for these
if it has COMMANDS enabled.
refs: https://github.com/wez/wezterm/issues/1485
This commit introduces a rough first pass at a command palette modal.
It is an adaptation of the emoji character selector and needs
refinement.
Importantly, the default pane selector key assignment now calls
into this new command palette instead.
refs: https://github.com/wez/wezterm/issues/1485
re-structure it so that we have a list of default action values,
and a function that can compute the command description from them.
This allows describing user-specified actions in the future,
as well as reducing some boilerplate: we can now generate eg:
ActivateTab(n) description text without hardcoding similar
alternatives.
refs: https://github.com/wez/wezterm/issues/1485
This allows defining those help actions that open URLs in the main
commands list, and not just for the macOS Help menu.
refs: https://github.com/wez/wezterm/issues/1485
This took a decent amount of effort to thread through with context;
wrappers around NSMenu and NSMenuItem are added to reduce some of
the objc usability warts, and an additional NSObject wrapper is
added to help copy the KeyAssignment from the existing list
of command palette commands and associate it with the menu item.
When a menu item is selected, macOS will walk through the responder
chain and look for a responder that responds to the selector associated
with the menu item. In practice that means that our window/view class
will be tried first, and then later, our app delegate will be tried.
This commit implements routing from both of these: the window case
routes to the associated TermWindow and drops into the existing
perform_key_assignment method.
In case there is no window (not currently possible, but will be
in the future), the app delegate also has a placeholder for dispatching
key assignments, although it will only be able to perform a subset
of the possible actions.
A couple of things to note:
* Items aren't smart enough to disable themselves or adjust their
caption based on the context. To make that work, we either need
to recreate the entire menubar when any possible context changes
(doable, but feels heavy), or we need to assign a target to each
menu item and implement a validation handler on that target.
That seemed to mess with the responder chain when I briefly
experimented with it.
* There's some disabled code to add a Services menu. It is disabled
because when it is enabled, accessing either Services or Help
from the menu bar sends the process into a busy loop somewhere
in macOS's internals. It's unclear what it is unhappy with.
* No keyboard accelerators are associated with the menubar yet.
That needs some thought, as they would essentially become global
keyboard shortcuts and take precedence over the shortcuts defined
for other keys in the config. This feels like it should be something
that the user has control over, so there needs to be something to
allow that before we go ahead and wire those up.
refs: https://github.com/wez/wezterm/issues/162
refs: https://github.com/wez/wezterm/issues/1485
This allows removing a bunch of unwrap/expect calls.
However, my primary motive was to replace the cases where we used
Mux::get() == None to indicate that we were not on the main thread.
A separate API has been added to test for that explicitly rather than
implicitly.
Implement an app delegate to receive a callback when the system
requests that we open `.command` files, and then ask the mux
layer to spawn a window for it and execute it via the shell.
Also allow handling `.sh`, `.zsh`, `.bash`, `.fish` and `.tool`,
per kitty.
refs: https://github.com/wez/wezterm/issues/2741
refs: https://github.com/wez/wezterm/issues/2871
Using the new abstractions, we can call into webgpu code now.
It doesn't do anything useful, and in fact crashes because
the mapping of the quads is doing the wrong thing.
Will fix in the next commit.
The recent changes to clustering make this more likely to happen.
This doesn't handle overflow resulting from changing styles in the tab
title, but those are less common.
refs: https://github.com/wez/wezterm/issues/2560
Since we're no longer tied to contiguous Vertex slices, we can
record the Quad data used for HeapQuadAllocator in a smaller structure,
so that's what this does.
It reduces the per-Quad storage from 272 bytes down to 84 bytes,
which helps with overall memory usage: the default cache size is 1024
lines worth of quads, so this can add up to multiple MB of RAM.
refs: https://github.com/wez/wezterm/issues/2626
This reduces peak heap usage by several MB by making better use
of allocated memory--no wasted overhead in the Vec capacity.
refs: https://github.com/wez/wezterm/issues/2626
When a line is rapidly updated with only some of the cells being
actually changed (eg: progress counter or other status being frequently
updated), it is desirable to avoid paying the cost of shaping the entire
line.
When bidi is not enabled we can assume that it is safe to break clusters
on whitespace boundaries. Doing so allows each of those whitespace
separated words to be shaped and potentially cached independently,
which reduces the amount of CPU time spent for the whole line.
This commit just adjusts the clustering, which reduces the CPU
utilization a bit.
refs: https://github.com/wez/wezterm/issues/2701
Do it "more properly": use intrusive list linkage to manage three
indices:
* hash lookup
* recency
* frequency
eviction is frequency based, but in order to avoid things that were
super hot in the past and that are no longer hot clogging up the cache,
eviction will incrementally age out least recently used entries that
haven't been active in the span of some number of get/put operations.
Aging scales down the frequency value to reduce its strength, so an aged
item isn't necessarily immediately a candidate for removal, it just
makes it more likely to be picked up by the frequency based removal as
it goes unused for an extended period.
Right now wezterm already allows to pass a cmdline to execute (instead
of the shell) by calling "wezterm start" with trailing arguments, e.g.
wezterm start -- bash
However, most other terminals implement this via a "-e" option. This
seems to be adopted widely in the wild, such that some third-party
frameworks just blindly expect the user's terminal to use the "-e"
option for this.
One such notable framework is kio from KDE Plasma, that calls the user's
terminal in that way when launching a desktop file that uses
Terminal=true. [1]
To solve this problem, we add a compatibility layer by adding a dummy
"-e" option. This will then consume the "-e" leaving the remaining
arguments as trailing arguments, which will later be consumed by our
existing implementation in the "prog" option.
Given that clap does not really support multiple arguments writing to
the same destination [2], this seems like the most sane implementation,
even if it is a hack.
It seems to work reliable, even for edge cases where we pass wezterm
options as trailing arguments, e.g. the following will just work and do
the expected outcome (and will **not** parse "--position" as a wezterm
argument):
wezterm start -e echo --position 10,10
Fixes#2622
[1] https://bugs.kde.org/show_bug.cgi?id=459616
[2] https://github.com/clap-rs/clap/issues/3146
I can't think of a good reason for this being here. I think I
may have been lazy about resolving the lifetime annotations
and just stuck in an extra buffer while building the original
version of this logic, and then forgot about it.
This commit resolves the lifetime annotations and directly
references the passed in buffer.
refs: https://github.com/wez/wezterm/issues/2626
I spent a few hours in heap profilers. What I found was:
* Inefficient use of heap when building up runs of
`Action::Print(char)`.
-> Solve by adding `Action::PrintString(String)`
and accumulating utf8 bytes rather than u32 codepoints.
* Inefficient use of heap when building Quad buffers: the default
exponential growth of `Vec` tended to waste 40%-75% of the allocated
capacity, and since we could keep ~1024 of these in cache, there's
a lot of potential for waste.
-> Solve by bounding the growth to 64 at a time. This has similar
characteristics to exponential growth at the default 80x24 terminal
size. May need to add a config option for this step size for users
with very large terminals.
* Lazy eviction from the LFU caches. The underlying cache advisor is
somewhat probabilistic and has a minimum cache size of 256, making
it difficult to maintain low heap utilization.
-> Solve by replacing it with a very simple LFU algorithm. It doesn't
seem to hurt much at the default terminal size with the default
cache sizes. If we make the cache sizes smaller, its overhead is
reduced.
Some further experimentation is needed to adjust defaults, but this
should help reduce heap usage.
refs: https://github.com/wez/wezterm/issues/2626
It can be helpful for debugging purposes.
The socket can be discovered by setting:
WEZTERM_LOG=wezterm_mux_server_impl::local=trace,info
to get it to log a line like:
```
setting up /Users/wez/.local/share/wezterm/gui-sock-38183
```
Then it can be helpful to do:
```
WEZTERM_UNIX_SOCKET=/Users/wez/.local/share/wezterm/gui-sock-38183 wezterm cli list
```
to see what is in the gui's mux model.
refs: https://github.com/wez/wezterm/issues/2616
When using `key_map_preference="Mapped"`, `ctrl-shift-1` is actually
`ctrl-shift-!` in a US layout.
This commit adds the us-layout mapping for shifted number keys to
allow that to work, but it is worth calling out that this will only
be meaningful in layouts that have the same US mapping for the number
keys.
refs: https://github.com/wez/wezterm/issues/2623
I've expanded the number of bits from 16->32 without impacting
the overall struct sizes and reserved 2 bits for super/subscript.
I refer to these as vertical alignment properties for conceptual
consistency with css.
SGR 73, 74, 75 are used to set super, sub and normal vertical alignment.
These are compatible with mintty.
However, mintty just added support for setting both attributes to render in
small caps in 06ac446049
(https://github.com/mintty/mintty/issues/1171)
I was going to upgrade to the unicode 15 font, but in testing this I
decided that the logic is slightly complex and the glyphs are often
difficult to see at most terminal font sizes, which generates questions
from users, so just fall back to notdef.
The clustered line storage impl of get_cell() is O(column-index)
as cells have to be iterated in order.
The render pass was calling it 3 times to resolve information
about the cursor; reduce that to just once.
It was also calling it once per cell to determine whether the
cell needed to be replaced with a custom glyph if custom block
glyphs were enabled, making it accidentally quadratic!
While it wasn't especially expensive, it did show up in profiling,
so this commit removes that call: we can cache the block glyph
key in the shaper info which is a better place for it anyway,
so that's what we do.
Similarly, there was some extraneous work to call get_cell
when computing some shaper info; remove that too!
That one might be slightly contentious: the is-followed-by-space
logic used to check the successor cell by index to see if it
was a space, but now looks at the successor shaped glyph to see
if it was a space. That might actually be a better choice, but
it may have some ripple effects.
According to its benchmarks, it's almost 2x faster than
unicode_segmentation. It doesn't appear to make a visible
difference to `time cat bigfile`, but I'll take anything
that gives more headroom for such little effort of switching.
This is a weird attribute TBH.
xterm seems to replace the cells with spaces: copying and pasting
results in spaces.
Kitty ignores it.
VTE doesn't render it but allows copying and pasting.
The latter is now also the behavior in wezterm.
This provides a means for more easily extending the default key
tables without forcing the user to recreate the entire config
for themselves.
wezterm.gui.default_keys is also added by this, but it is likely
not as useful.
There were two problems:
* We weren't correctly invalidating when the hover state changed
(a recent regression caused by recent caching changes)
* We'd underline every link with the same destination on hover,
not just the one under the mouse (longstanding wart)
Recent changes allow the application layer to reference the underlying
Lines directly, so we can restore the original and expected
only-highlight-under-the-mouse by switching to those newer APIs.
Adjust the cache values so that we know to also verify the current
highlight and invalidate.
I was a little surprised to see that this also works with mux client
panes: I was expecting to need to do some follow up on those because
they return copies of Line rather than references to them. That happens
to work because the mux client updates the hyperlinks at the time where
it inserts into its cache. The effect of that is that lines in mux
client panes won't update to new hyperlink rules if they were received
prior to a change in the config.
refs: https://github.com/wez/wezterm/issues/2496
This makes the search feel more responsive.
We search from bottom to top so that we show the more recent results
first, but for the sake of efficiency when accumulating result chunks
we need to reverse the order of the results vec from how it was
previously.
Each result chunk is loosely ordered from top to bottom, so we sort
it and reverse it: results[0] is the bottom-most result.
New rows are accumulated on the end of the result array; this is
not only more efficient, but it preverses the match result number
ordering.
The next/prior functions need to be swapped to account for this change
in result order.
refs: https://github.com/wez/wezterm/issues/1209
It was set to the first non-tab bar pixel y coordinate rather than
the line y pixel coordinate.
Move the calculation up!
refs: https://github.com/wez/wezterm/issues/2483
There are caveats to determining this, but when we think
password entry is enabled, switch the cursor to the font-awesome
lock glyph instead of the normal cursor sprite.
fa_lock is used because it is monochrome and can thus be tinted
to the configured cursor color, and it respects blinking/easing.
refs: https://github.com/wez/wezterm/issues/2460
The idea here is that different kinds of panes may want to expose
additional metadata to lua scripts. It would be a bit weird to add
a Pane method for each of those and plumb it all the way through
the various APIs, so just allowing a pane impl to return a dynamic
value (likely an Object) allows a bunch of flexibility.
This commit exposes the clientpane is_tardy boolean and the time
since the last data was recevied (since_last_response_ms) from
the mux client pane implementation: these are used to show the
tardiness indicator in the client pane.
Exposing this data enables the user to add that info to their
status bar if they wish.
The default behavior for charselect is to show the recent category
if you have previously used it, otherwise, show the default emotion
category.
refs: https://github.com/wez/wezterm/issues/2163
CTRL-SHIFT-U is a new default key assignment for this new modal.
It opens up a fuzzy searchable browser that defaults to showing
emoji/emoticons. The category can by cycled through the suggested
emoji categories using CTRL-r. Unlike the system emoji palette,
wezterm includes a category for nerdfont symbols, and another
that is a list of all unicode codepoint names, so you should be
able to browse for pretty much any codepoint you can think of.
The modal also allows fuzzy searching based on:
* The official unicode name
* The github shortcode
* codepoint value in hex
so if you know the codepoint value but not the name, you can
still find a way to input what you're looking for.
Pressing Enter will copy the selected item to the clipboard
send it to the active pane, and cancel the modal. You can therefore
repeat the insert by simply pasting.
I plan to add frecency to this in a later commit: that way the
frequently/recently used selections will show in a category of
their own and make it easier to re-input them.
refs: https://github.com/wez/wezterm/issues/2163
Tidy some things up to avoid some dead code and redundant impls.
Make it easier to select whether you want to implement the new
methods in terms of the old, or the old methods in terms of
the new in a given pane impl.
Remove the faulty cache key stuff and hang references to the cached
data directly off the underlying Line. That makes the association
between the the Line and the data O(1) plus some basic cache
invalidation checks.
Adjust the shape cache portion of this to use the same ID for the
shape, so that things that invalidate just the quads (such as cursor
movement when not composing, and selection) only need to recompute
the quads without re-shaping.
Adds Pane::for_each_logical_line_in_stable_range_mut and
Pane::with_lines_mut which allow iterating mutably over lines.
The idea is that this will allow the renderer to directly cache
data in the Line via its appdata without having to build cumbersome
external caching logic and managing cache keys.
This commit just swaps the implementation around for localpane
and sanity checks that the renderer functions.
Various overlays and the mux client don't properly implement these
yet and current warn at compile time and panic at runtime.
To follow is the logic to cache the data and make sure that it
works the way that I think before converting the other Pane
implementations.
The cache key isn't quite right, leading to some artifacts
in some cases.
I have a cunning plan, but it will take me a bit to wrap
it up, so in the meantime, disable these caches.
refs: https://github.com/wez/wezterm/issues/2455
It didn't really belong there; it was added as a bit of a hack
to propagate screen reverse video mode.
Move that to the RenderableDims struct and remove the related
bits from Line
Similar to selection_fg, setting the cursor_fg to transparent ("none")
will use the foreground color of the text behind the cursor.
refs: https://github.com/wez/wezterm/issues/1835
I broke this with the performance changes I pushed earlier today.
Poke a hole in caching for it as well, as it doesn't use the seqno
to track changes like the rest of the Line based data.
cc: @DeadlySquad13
This doesn't mean that it renders at 1fps, just that it goes through
the simpler path of scheduling on the boundaries rather than at
animation_fps through the intermediate stages, where it would be
useless.
Introduces a heap-based quad allocator that we cache on a per-line
basis, so if a line is unchanged we simply need to copy the previously
computed set of quads for it into the gpu quad buffer.
The results are encouraging wrt. constructing those quads; the
`quad_buffer_apply` is the cost of the copy operation, compare with
`render_screen_line_opengl` which is the cost of computing the quads;
it's 300x better at the p50 and >100x better at p95 for a full-screen
updating program:
full 2880x1800 screen top:
```
STAT p50 p75 p95
Key(quad_buffer_apply) 2.26µs 5.22µs 9.60µs
Key(render_screen_line_opengl) 610.30µs 905.22µs 1.33ms
Key(gui.paint.opengl) 35.39ms 37.75ms 45.88ms
```
However, the extra buffering does increase the latency of
`gui.paint.opengl` (the overall cost of painting a frame); contrast the
above with the latency in the same scenario with the current `main`
(rather than this branch):
```
Key(gui.paint.opengl) 19.14ms 21.10ms 28.18ms
```
Note that for an idle screen this latency is ~1.5ms but that is also true
of `main`.
While the overall latency in the histogram isn't a slam dunk,
running `time cat bigfile` is ~10% faster on my mac.
I'm sure there's something that can be shaved off to get a more
convincing win.