I wrote my own terminal and you won’t believe what happened next. I called it vertigo.
I was going to say that this took about two weeks of part time effort, but an early update shows me running ls and printing letters (no numbers) one month ago. Time flies when you’re having fun. There was a pause in the middle, so I think two weeks is still about right.
I don’t think it’s necessarily better than alternatives in general (I’ve been calling it the world’s worst terminal for a while now), but it’s better for me in the sense that I understand everything it does, and I can make it do things it doesn’t yet do.
The feature set is fairly basic. Run shell commands and put text on the screen. Most of the usual terminal control stuff. Plus a few features which helped push it from fun toy to terminal I actually use personally. Very few features that I don’t use.
It supports resizing, but only down to a minimum of 80x24. Smaller than that and the contents are scaled instead. When using dwm on my one laptop, xterm wants to display 79 columns which turns out to be kinda inconvenient. And I usually like to work with one or two big terminals, and then a few tiny ones on the side, but I really dislike that they end up only showing about 10 rows. I think this is not rocket science, but since opengl gives us scaling for free, here it is. (Do other terminals offer this feature? I looked, but they have so many features I couldn’t find the option to enable it.)
Support for the iconify control (
^[2t). This isn’t new, but I think it’s handy to have an option to run commands like xcalc and have the shell’s terminal disappear until the command finishes.
A font independent cell sizing approach to texture healing. This was a quick hack done because I could, but it turns out I really like the result. It’s pretty subtle, but adjusts the spacing just enough that I definitely prefer it when compared side by side. I think working over entire words is even an improvement over the original pairwise approach.
It looks best with Ubuntu Mono, because that’s the font I started with. I’m reminded that my understanding of font metrics is very limited. Other fonts seem to not draw inside the boxes I expect them to, but Ubuntu Mono is very well behaved.
There’s even a menu with an option that can be toggled.
Everything is drawn with depth testing enabled because my Intel laptop only renders a third of the screen when disabled. The hilarity of opengl programming. I have confirmed that retro effects like scanlines can be implemented, and then promptly deleted that code.
The dependencies are glfw and go. On debian, this turns out to be about 300 bizzaro lib-dev packages. Just use openbsd, pkg_add glfw and go.
It’s about 100x faster (0.30s) than xterm (19.25s) in a stupid
find | xargs cat test (counting load on Xorg). I don’t think this is particularly interesting, but I guess that’s what people care about. The surprising part is it’s also faster than kitty (1.20s) and alacritty (0.50s), which I assume have had more performance tuning done. Again, meaningless measure, but happy to perform well here. Mostly wrote the obvious code to do the obvious thing and it worked out.
The only dedicated performance work I tried was to reduce allocations. Even before this, performance was very good, and it was only by printing GC stats that I could tell it was churning memory. Go GC is pretty good at hiding latency. Now most allocations use simple ring buffer structures to reuse memory. It’s not quite zero alloc, but the remaining allocations are small and infrequent.
Memory allocation is a little higher than I’d like, but there’s no obvious target for reduction. Per process non-shared memory runs to about 30MB in actual use. So half a gig for 20 terminals seems wild compared to the 64MB Pentium I first installed OpenBSD on, but hardly anything compared to what people run just for a chat client.
I know the draw code could be faster for simple operations. When only a single character is printed, we know that only two cells need to be redrawn. A very simple damage system could optimize that, but it’s hard to justify the work when it already responds instantly. Eventually, just not urgent.
In general, the code scales up to efficiently handle full screen repaints better than it scales down. I think this is the right tradeoff, for now, as it only takes a few keystrokes to type ls, but the output is a large multiple of that.
It accumulates CPU time slowly, but somewhat faster than xterm in normal usage (not catting huge files). The difference comes out to a few seconds per day. I haven’t been able to determine if it affects battery life.
There’s about a 300ms delay between running vertigo and seeing the window. This is entirely within glfw create window, so I think I have to live with it. It’s just long enough to be noticeable, though not really annoying given that I don’t usually start new terminals more than once a minute.
The code clocks in right around 3k lines.
52 151 980 draw.glsl
166 461 3529 font.go
148 368 2201 io.go
105 260 2157 main.go
658 1728 15554 opengl.go
118 273 2045 proc.go
704 2186 13837 screen.go
388 1082 7678 term.go
629 1551 11904 window.go
2968 8060 59885 total
I would guess someday it may approach 4k lines, but I think that should be enough. A 4k terminal for a 4k world.