WezTerm is a GPU-accelerated cross-platform terminal emulator written in Rust
wezfurlong.org
wezfurlong.org
With a tiling window manager, the built-in notebook/tiling functionality is not really useful (the window manager is more flexible and has universal keybindings) so when looking at the time required to pop a full new window in either single or shared instance they were actually behind regular xterm. Resource usage wasn't stellar either (xterm was still better than most lightweight libvt-based terminals). Couldn't feel much of a latency improvement (again, X without compositor).
I'm sure at full throughput the difference is there, but who is looking at pages of output you can't read? I do keep terminals open for days, but my most common usage case is mostly open window -> run a small session -> close and I got annoyed fast.
For the second group, startup perf is everything, because users hit that multiple times a day. For the first group, not so much.
Some of the other tiling functionality is also more helpful for folks that aren't on platforms with as powerful of window managers (macOS, Windows)
Also i'm using xterm and i always found it very fast, i never thought that i'd like a faster terminal.
many modern wm's and terminals have multitab and multiwindow features but i invested time only into learning tmux and i can use it anywhere. and of course nohup functionality is builtin by definition.
i have said it before and i can say it again: terminals come and go, tmux stays.
Like, why? Over 20 years of using terminal emulators not even single time I was like "Man, I wish my terminal was faster".
Is this just a fun project to do, like, "yay, I wrote a GPU-accelerated terminal emulator!"?
even on an integrated GPU, text rendering is far faster when you use the GPU to render glyphs to a texture then display the texture instead of just displaying the glyphs individually with the CPU.
1. The naive way is to render each change as it occurs. This is fine when the unbottlenecked output changes less than once a frame. This is the normal case for terminals and why people rarely care. It falls apart when you e.g. accidentally cat a huge file to the terminal.
Some numbers with the terminals I have on my system (ignored a whole bunch of xterm/rxvt aliases; e.g. aterm, lxterm etc.): cat of a file of 10MB on my system on a terminal filling half of a 1920x1024 screen on a Linux box running X takes (assume an error margin of at least 10% on these; I saw a lot of variability on repeat runs):
* rxvt-unicode: 0.140s
* kterm: 0.2s
* kitty: 0.28s (GPU accelerated)
* xterm: 0.51s
* wezterm: 0.71s (GPU accelerated)
* gnome-terminal: 0.86s
* mlterm: 0.97s
* pterm: 1.11s
* st (suckless): 3.4s
Take this with a big grain of salt - they're a handful of runs on my laptop with other things running, but as a rough indicator of relative speed they're ok.Sorted in ascending order. These basically fall in two groups in terms of the raw "push glyphs to the display" bit, namely using DrawText or CompositeGlyphs calls or similar, or using GPU libs directly.
Put another way: Everything can be as fast as rxvt(-unicode); everything else is inefficiencies or additional features. That's fine - throughput is very rarely the issue people make it out to be (rendering latency might matter, and I haven't tried measuring that)
Note that calling the rest other than kitty and wezterm not GPU-accelerated is not necessarily entirely true, which confuses the issue further. Some of these likely would be slower if run with an X backend with no acceleration support. I've not tried to verify what gets accelerated on mine. But this is more of a comparison between "written to depend on GL or similar" vs "written to use only the specific OS/display servers native primitives which may or may not use a GPU if available".
2. The first obvious fix is to decouple the reading of the app output from the rendering to screen. Rendering to screen more than once per frame achieves nothing since the content will be overwritten before it is displayed. As such you want one thread processing the app output, and one thread putting what actually changed within a frame to screen (EDIT: you don't have to multi-thread this; in fact it can be simpler to multiplex "manually" as it saves you locking whatever buffer you use as an intermediary; the important part is the temporal decoupling - reading from the application should happen as fast as possible while rendering faster than once per frame is pointless). That involves one big blit to scroll the buffer unless the old content has scrolled entirely out of view (with the "cat" example it typically will if the rest of the processing is fast), and one loop over a buffer of what should be visible right now on lines that have changed. The decoupling will achieve more for throughput than any optimisation of the actual rendering, because it means that when you try to maximise throughput most glyphs never make it onto screen. It's valid to not want this, but if you want every character to be visible for at least one frame, then that is a design choice that will inherently bottleneck the terminal far more than CPU rendering. Note that guaranteeing that is also not achieved just through the naive option of rendering as fast as possible, so most of the slow terminals do not achieve this reliably.
Note that this also tends to "fix" one of the big reasons why people take issue with terminal performance anyway: It's rarely that people expect to able to see it all, because there's no way they could read it. The issue tends to be when the terminal fails to pass on ctrl-c fast enough and stop output fast enough once the program terminates because of buffering. Decouple these loops and skip rendering that can't be seen and this tends to go away.
3. Second obvious fix is to ensure you cache glyphs. Server side if letting the display server render; on the GPU if you let the GPU render. Terminals are usually monospaced; at most you will need to deal with ligatures if you're being fancy. Some OS/display server provided primitives will always be server-side cached (e.g. DrawText/DrawText16 on X renders server-side fonts). Almost all terminals do this properly on X at least because it's the easiest alternative (DrawText/DrawText16) and when people "upgrade" to fancier rendering they rarely neglect ensuring the glyphs are cached.
4. Third fix is you want to batch operations. E.g. the faster X terminals all render whole strips of glyphs in one go. There are several ways of doing that, but on X11 the most "modern" (which may be GPU accelerated on the server side) is to use XRender and CreateGlyphSet etc. followed by one of the CompositeGlyphs, but there are other ways (e.g. DrawText/DrawText16) which can also be accelerated (CompositeGlyphs is more flexible for the client in that the client can pre-render the glyphs as it pleases instead of relying on the server side font support). Pretty much every OS will have abstractions to let you draw a sequence of glyphs that may or may not correspond to fonts.
There is a valid reason why using e.g. OpenGL directly might be preferable here, and that is that if used conservatively enough it's potentially more portable. That's a perfectly fine reason to use it, albeit at the cost of network transparency for those of us still using X.
So to be clear, I don't object by people using GPUs to render text. I only object to this rationale that it will result in so much faster terminals, because as you can see from the span of throughput numbers, while Kitty and Wezterm doesn't do too badly they're also nowhere near fastest. But that's fine - it doesn't matter, because almost nobody cares about the maximum throughput of a terminal emulator anyway.
That said, to add another reason why doing the GPU ones may well be worthwhile on modern systems anyway, whether or not one addresses the other performance bits: Being able to use shaders to add effects is fun. E.g. I hacked in an (embarassingly bad) shader into Kitty at one point to add a crude glow effect around characters to make it usable with more translucent backgrounds. Doing that with a CPU based renderer on a modern resolution would definitely be too slow. I wish these terminals would focus more on exploring what new things doing GPU based rendering would allow.
Depends on how the glyph rendering is done. Modern GPU glyph/vector renderers like Pathfinder [1] or Slug [2] keep all the data on the GPU side (although I must admit that I haven't looked too deeply into their implementation details).
The litmus test I use is how smooth can the terminal emulator run `cmatrix` at fullscreen
GPUs aren't really meant for bit blitting sprite graphics. (Which is what a terminal really does.)
Think of the compositing of layers of translucent windows used in modern 2d window managers, while dragging them around. Or even scrolling in a browser. Those rely on the GPU for fast compositing.
Even for 3d, think of the screen-space techniques used in games, where it's necessary to draw a scene in layers combined with each other in various interesting logical ways (for shadows, lighting, surface texture, etc), with the pixels of each layer matching up in a reliable way.
Nowadays, everything is done with the same pipeline as the 3D graphics, there's no need for two pipelines.
GPUs are meant for this. Modern ones. Your knowledge is incomplete, outdated, or both.
[1] https://news.ycombinator.com/item?id=28743687 (It takes a PhD to develop that) [2] https://github.com/cmuratori/refterm [3] https://www.youtube.com/watch?v=hxM8QmyZXtg, https://www.youtube.com/watch?v=pgoetgxecw8
With high refresh rate displays it really helps avoid a blurry mess too
I can read the stream almost like The Matrix
I have started to see "terminal" apps that won't run on a real terminal, though. Using UTF-8 regardless of your locale, using 256-color xterm escapes regardless of your TERM setting, being unreadable without 256 colors, etc, and in general not using termcap/terminfo.
Even worse: although many terminal emulators claim to emulate some "ANSI" terminal or be "VT100 compatible" and so on, most of them aren't at all. Simply run vttest in your terminal of choice and be surprised, especially by how many of them fail at very basic cursor movement tests. One of the few terminal emulators which gets most things right is xterm. It's also one of the very few terminal emulators which even supports exotic graphics capabilities like Sixel/ReGIS/Tek4014. Nobody should underestimate xterm …
I am not. It makes next to no sense to me. Maybe if you have a highres screen and dedicated VRAM. Otherwise going through the GPU interfacing ceremony just adds overhead.
- Keyboard cut-and-paste. CtlShiftSpace to highlight likely selections, matching letter to cut, shift letter to cut AND paste.
- Built in mosh+tmux-like functionality, but at the GUI level. I do a lot of terminal work at home from my Mac connected to my Linux laptop WezTerm instance, with multiple panes/tabs.In the case of wezterm, a quick glance shows that not only is this correct (lots of unsafe rust, even beyond the normal use of wrapped functions), there is also not a lot of documentation about what the safety conditions of the various unsafe code is.
(which is the usual good practice in handling this sort of thing).
So to your point, saying it's "written in rust" doesn't matter here.
If the use of GPUs is via CUDA, there are my wrappers [1] which are RAII/CADRe, and therefore less unsafe. And on the Rust side - don't you need a bunch of unsafe code in the library enabling GPU support?
There is lots of "bad" unsafe code that is much worse, and over time, they will have just as much trouble trying to handle this overall as C++ does.
(and no the ability to say you don't want unsafe transitive crates doesn't fix anything)
Why does the title say "written in rust" then, if not because the author believes it's inherently worthy of merit? It's largely irrelevant to users of the software.
I follow HN mostly through RSS with various filters. Rust is one of the keywords that I follow.
It is not an automatic badge of quality for the program itself, but it is an indicator that someone, somewhere is using $LANGUAGE to make a program like that.
It isn't so much promotion for the program itself, but for $LANGUAGE both to attract like-minded programmers who like it as well as show other programmers that $LANGUAGE is using for making things - which perhaps will give them an incentive to check out the language too, if for no other reason than perhaps answering the curiosity some may have about how a program like that is written in that language.
Hacker News is a place full of programmers after all and programmers do tend to care about programming languages and their uses.
Would you say the same if something was written in FORTRAN, COBOL or assembly?
Why? Because a project written in Rust will certainly be using Cargo for package management, which is absolutely delightful relative to just about any other language’s package manager.
I can git clone and start hacking on just about any Rust project almost immediately. If I’m using 100 tools throughout the day (that’s a conservative estimate) I’d much prefer each of those were written in Rust than C or C++, for instance. Otherwise that’s 100 opportunities for me to find a missing feature or bug and end up futzing with autotools and cmake and ninja and distro-packaged but-not-quite-the-right-version dynamic libraries and broken pkg-config files and and and…
As an example, I recently needed to add support to probe-rs for debugging embedded devices via JTAG with a CMSIS-DAP probe. One git clone and I immediately have all of my dependencies resolved, my editor immediately has go to definition and autocomplete working, and I have a host of debug target definitions for debugging probe-rs itself.
OpenOCD doesn’t support the chip I’m targeting, so if I wanted to use that instead of probe-rs I’d have to first set up a dev env for it and add that support myself. That consists of using a tool like bear to trace the build execution so it can spit out a compile_commands.json file so the C and/or C++ language server can make sense of the project. Oh, and I need to repeat that step if I realize later that I missed a couple defines, otherwise the lang server will think huge swathes of code are preprocessed-out. And this is all after I’ve chased down the myriad of build and runtime dependencies.
I find missing functionality and/or critical show stopping bugs regularly in the tools I use, and it doesn’t matter if the tools are commercially funded or super popular or whatever: I opened an issue for a critical miss-linking problem in Google’s bazel build rules for the Go language, and that’s still open something like 6 years later. And ironically, I discovered the bug while working on a project that uses Go, C++ and C++ protobufs — two of those things are Google inventions, and the other is a predominant language in the Google stack! Their own stuff doesn’t even work together.
So no, it’s not a matter of me choosing bad tools, to be clear. I’m just often put in a position that the best course of action is rolling up my sleeves and fixing whatever issue I’ve discovered.
For someone like me, knowing something is written Rust gives me some peace of mind, knowing that it’ll be (comparatively) easy to work on when I inevitably discover a deficiency. Not because the language is great (though that’s also true), but because the package management simply doesn’t suck.
So please, please stop with the fallacy that “it has no merits on its own”. It does have merits, though maybe you don’t personally appreciate them. And that’s okay. But some of us do appreciate those merits — and they very much unequivocally, objectively are there.
One thing that put a sour taste in my mouth for WezTerm is they document all escape sequences [0] using a deviation from standard syntax (sometimes using `:` over `;` like for RGB colors, at least I've not found any other documentation about this being commonly adopted). They do put a disclaimer at one point in the document but that only helps if you read it linearly and it feels weird to document your implementation of a standard to only work with your terminal, locking people's applications into it.
As for rendering, I had problems with what look like correct escape sequences not rendering correctly (alternative underline styles, colored underlines). I still need to dig further into this where in the stack the problem is (my application or where in WezTerm)
Using ";" is popular, and some parsers even require it, but it makes little sense. A terminal that doesn't know about 38 (anything old) will interpret that 5 as blink if you use ";", which is not what anyone wants.
The colons are more correct, but the semicolons are seemingly more broadly supported.
That doesn't stop terminals from supporting it of course, just from saying they do.
* <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-F...>
* <https://invisible-island.net/xterm/xterm.faq.html#color_by_n...>
More details in my other comment: https://news.ycombinator.com/item?id=35157058
Where would I find this documented and how broadly is it supported? So far, the most exhaustive documentation I've found is wikipedia [0], vt100 [2], and wezterm with only wezterm talking about this. I also have not seen `:` handled in the various parsers (e.g. `vte` for alacritty) or generators (e.g. `termcolor`) I've looked at.
* via <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-F...> (search for "CSI Pm m Character Attributes (SGR)"):
If 88- or 256-color support is compiled, the following apply:
[snip]
o The 88- and 256-color support uses subparameters described
in ISO-8613-6 for indexed color. ISO-8613-6 also mentions
direct color, using a similar scheme. xterm supports
that, too.
o xterm allows either colons (standard) or semicolons
(legacy) to separate the subparameters (but after the
first colon, colons must be used).
The indexed- and direct-color features are summarized in the
FAQ, which explains why semicolon is accepted as a
subparameter delimiter:
Can I set a color by its number?
(This links to <https://invisible-island.net/xterm/xterm.faq.html#color_by_n...>.)The `ctlseqs` document continues:
These ISO-8613-6 controls (marked in ECMA-48 5th edition as
"reserved for future standardization") are supported by xterm:
[snip example with colons ":"]
This variation on ISO-8613-6 is supported for compatibility
with KDE konsole:
[snip example with semicolons ";"]
So, the confusion between use of ";" and ":" separators is because the RGB values are not actually parameters (specified as separated by ";"), rather, the RGB values are subparameters (specified as separated by ":"). [Edit #1]This is covered in more detail by <https://invisible-island.net/xterm/xterm.faq.html#color_by_n...> (mentioned above):
We used semicolon (like other SGR parameters) for separating the R/G/B
values in the escape sequence, since a copy of ITU T.416 (ISO-8613-6)
which presumably clarified the use of colon for this feature was costly.
Using semicolon was incorrect because some applications could expect their
parameters to be order-independent. As used for the R/G/B values, that was
order-dependent. The relevant information, by the way, is part of ECMA-48
(not ITU T.416, as mentioned in Why only 16 (or 256) colors?). Quoting from
section 5.4.2 of ECMA-48, page 12, and adding emphasis (not in the
standard):
[snip]
Of course you will immediately recognize that 03/10 is ASCII colon,
and that ISO 8613-6 necessarily refers to the encoding in a parameter
sub-string. Or perhaps you will not.
:D [snip]
Later, in 2012 (patch #282), I extended the parser to accommodate the
corrected syntax. The original remains, simply because of its widespread
use. As before, it took a few years for other terminal developers to
notice and start incorporating the improvement. As of March 2016, not
all had finished noticing.
[snip]
* Going forward (e.g., xterm patch #357), these terminfo building blocks
are used in ncurses:
* xterm+256color2 is the building-block for standard terminals
* xterm+256color is a building-block for nonstandard terminals
It's unfortunately seemingly not possible to directly link to the specific text but hopefully that's enough pointers for people to find it themselves.Once again, seems it's amazing that anything works anywhere. :D
[Edit #1: Corrected from subparameter separator from "," (which is correct for one of the specs I read but incorrect here) to ":".]
[Edit #2: Typo fix.]
(1) With regard to the "03/10 is ASCII colon" comment: the "03/10" form is apparently known as "column-row notation" (or "column-line notation", see https://en.wikipedia.org/wiki/ISO/IEC_2022#Notation_and_nome...) and refers to the position of the character in an ASCII table such as: https://en.wikipedia.org/wiki/File:USASCII_code_chart.png
I actually found a reference (https://dicom.nema.org/medical/dicom/2019d/output/html/part0...) that explains the notation: "the [US ASCII] value can be calculated as (column * 16) + row, e.g., 01/11 corresponds to the value 27 (1BH) [i.e. 0x1B]."
It then notes "The column/row notation is used only within Section 6.1 to simplify any cross referencing with applicable ISO standards." :D
A (IMO less effective) description can also be found in ECMA-35 (6th edition; section 5.2 "Code Tables").
(2) A nuance with regard to my "the confusion between use of ';' and ':' separators is because the RGB values are not actually parameters" remark:
The ECMA-48 (5th Edition; section 5.4.2) specification (& ISO/IEC 8613-6 / ITU-T Rec. T.416) talks about "parameter string" & "parameter sub-string" rather than "sub-parameter".
(Also, the "reserved" values mentioned apparently weren't ever actually specified in a standard; and--as noted elsewhere--the format of the ANSI parameter(s) don't exactly match the ODA (ISO/IEC 8613-6 / ITU-T Rec. T.416) format anyway--see ITU-T Rec. T.416 (1993 E) page 41.)
(3) The Wikipedia talk page on ANSI escape codes makes...interesting...reading: https://en.wikipedia.org/wiki/Talk:ANSI_escape_code#Clarific...
[0] i.e. let me inflict this "knowledge" on you. :D
[n] The best explanation for how we ended up in this mess is, I think, found in "Figure 6 - Structure of 8-bit codes" of ECMA-35 (6th Edition). :D
https://sw.kovidgoyal.net/kitty/
https://github.com/kovidgoyal/kitty/releases
I used iTerm2 on macOS for many years and still keep it around, but switched to kitty as my daily driver in late 2021 and have been very happy with it.
http://www.9bis.net/kitty/index.html#!index.md
> KiTTY is a fork from version 0.76 of PuTTY
> KiTTY is only designed for the Microsoft® Windows® platform.
I have only one complain: I tried everything to configure it to open in a specific position in the screen (top left corner), and for FSM's sake, It's impossible! (Windows 10)
local wezterm = require("wezterm")
wezterm.on('gui-startup', function(cmd) -- set startup Window position
local tab, pane, window = wezterm.mux.spawn_window(cmd or
{position={x=0,y=0},width=100,height=20}
)
end)I just couldn't stand the petulant attitude of Alacritty's maintaner(s) and it didn't take much to find something else. Although I can't give this for granted now I haven't seen there's the same sort of behavior on WezTerm.
--
0: https://sw.kovidgoyal.net/kitty/conf/#shortcut-kitty.Open-UR...
1: https://wezfurlong.org/wezterm/hyperlinks.html#explicit-hype...
0: https://sw.kovidgoyal.net/kitty/conf/#shortcut-kitty.Browse-...
{ key="e", mods="CTRL|ALT", action=wezterm.action{QuickSelectArgs={
patterns={
"http?://\\S+",
"https?://\\S+"
},
action = wezterm.action_callback(function(window, pane)
local url = window:get_selection_text_for_pane(pane)
wezterm.open_with(url)
end)
} }
},It’s a very nice application, easy to configure, very customisable, stable. I’ve only have experienced one bug with app, I opened an issue in GH and Wez had a nightly version with a fix in just a few hours.
If you haven’t check it out yet, I highly recommend it.
This makes it seem pretty squarely targeted at i3/Sway/vim sorts of users, which doesn't make it bad but an odd fit for more general users.
Eventually I uninstalled it and back to iTerm2. Maybe one day I'll learn Lua and use it again.
I don't personally like all of its design decisions, but an Emacs-like vibe really intrigues and attracts me.
I spent ages trying to get Wezterm font rendering to match iTerm2. After spending far too long in the font section of configuration, it turns out this was the solution.
That would be pretty small in modern times. Certainly some people are working with terminals that size, often then spilling out into multiple terminals side by side on the screen, but modern resolutions are good enough to support a lot more text per terminal than that. As a single data point, my half-screen terminal right now is 140x75.
- WezTerm has windows/tabs/panes in it, and one use case would be ~10 panes on a 4K display in various columns/rows.
- Within those you might have applications that themselves have columns/rows of panes, like vim.
- Why not use the GPU to draw the graphics so you can offload the CPU? When there's a lot of text to go through (something scrolling stdout hard, jumping through a big file), the extra performance can be nice.A terminal editor needs GPU acceleration for the same reasons any other displayed item needs GPU acceleration.
This is basic mechanical sympathy.
Fast enough bitmap terminals have existed since the 1980's, and it takes really naive rendering for the rendering to be a bottleneck ever since then. Resolutions have increased, sure, but overall the increase in resolution and colour depth has been slower than the increase in CPU speed.
If you test various terminals, you'll find the difference in throughput is massive and the most popular ones are not necessarily the highest throughput ones, because people rarely run into the use cases where it makes a difference.
(the one step up from naive rendering that makes the most difference: the renderer should run in a separate thread, at most once per frame, and scroll the required number of lines with a single copy - that copy might well be optimised to a blit using the GPU by the X server or whatever, but that's besides the point; do just that and output does not bottleneck on the rendering as long as the renderer can render individual frames fast enough)
In general if GPU style processors had better dev experience, less fragmentation and other chilling effects, we might be running a lot of our software on them for general efficiency.
Alacritty and Wezterm have very different--if not opposite--philosophies. Choose Alacritty if you want something simple, fast, focused, done right. Choose WezTerm for flexibility and being a cross-platform environment by itself.
On Linux, it’s no contest, I love WezTerm over Kitty on Linux and didn’t have any of these issues.
I can't care less about the throughput of a terminal; xterm was fast enough 20 years ago.
that's worse than an electron terminal
something is wrong
[nix-shell:~]$ du -hs $(nix-build '<nixpkgs>' -A wezterm)
94M /nix/store/w7hpqwqqp7xq70wjsm8bd1yara0rhk9v-wezterm-20220408-101518-b908e2dd
[nix-shell:~]$ du -hs $(nix-build '<nixpkgs>' -A kitty)
25M /nix/store/9k0jn3219l6l7ywcqyvbifhd38cagfd9-kitty-0.25.2
[nix-shell:~]$ du -hs $(nix-build '<nixpkgs>' -A alacritty)
6.2M /nix/store/cq7zaagwcwn9blynw027bqszhnld2sk2-alacritty-0.10.1
Is the feature set difference between these really that much? At least, between kitty and wezterm?
If you think "but terminal multiplexing and SSH!", then feel free to add 0.9MB for screen (or 1MB for tmux) and 4.8MB for openssh, and you're still way below the size of wezterm.
On my machine:
* WezTerm 224 MB
* iTerm2 74 MB
* Terminal (default MacOS) 10 MB
And yet, I have read comments proclaiming that iTerm is bloatware.
p.s. I've been using Wezterm for more than a year, and I love it.
The on-disk size != RAM usage, and on-disk size really doesn't matter on modern systems, unless you're on a very resource constrained system, in which case, you probably don't want a feature-heavy GUI anyway.
2 % tree .
.
└── Contents
├── Info.plist
├── MacOS
│ ├── strip-ansi-escapes
│ ├── wezterm
│ ├── wezterm-gui
│ └── wezterm-mux-server
├── Resources
│ ├── terminal.icns
│ └── wezterm.sh
└── _CodeSignature
└── CodeResources
2 % du -hs Contents/MacOS/*
2.8M Contents/MacOS/strip-ansi-escapes
48M Contents/MacOS/wezterm
119M Contents/MacOS/wezterm-gui
44M Contents/MacOS/wezterm-mux-server
A bit of duplicative bloat, but there's nothing wrong with that for an young project trading a bit of space for an easier build/release process.Unlike other terminal emulators, this project is entirely statically compiled, so the code for the built-in SSH client, serial port support, muxing server/client protocol, shader compilation engines, image decoding libraries, D-BUS clients, threading libraries, HTTP(S) (1.1/2/3) with several types of compression, and of course the GPU shader compilers and APIs.
I built Wezterm on my machine and the binary is about 21MiB in size. About 12MB of that is code, the rest is debug symbols. 2MB of that is the Rust standard library and another 1MB is a LUA runtime. Then there are several image libraries that take about half a megabyte.
The compiled source code itself is only 893KiB in my file. The rest is just dependencies and random ELF segments.
So yes, the terminal emulator is about one Windows 95 installation in size, but that's all quite explainable if you care about the features.
For comparison, I've run `debtree` on gnome-terminal (3.4KiB on disk) and the dependency tree is reported to be 691MiB. Microsoft's Windows Terminal is distributed in 18.6MiB - 66.4MiB (I don't know the compression msixbundles use if any) which is comparable. iTerm2 seems to be about 74MiB decompressed. Even `xterm` (the most barebones terminal emulator I can find) relies on 20MiB of dependencies.
If xterm is all you need, then there's no need to download anything else. If you want more features in a terminal emulator, you're going to have to deal with larger binaries.