[HN Gopher] Anatomy of a Terminal Emulator
       ___________________________________________________________________
        
       Anatomy of a Terminal Emulator
        
       Author : imsnif
       Score  : 244 points
       Date   : 2021-11-02 13:01 UTC (9 hours ago)
        
 (HTM) web link (www.poor.dev)
 (TXT) w3m dump (www.poor.dev)
        
       | admin786 wrote:
       | Thanks top message
        
       | blamod wrote:
       | damn dude this was such a good article until i got to rust
       | code...
        
       | dundarious wrote:
       | Casey Muratori's refterm video series is a great example of how
       | to do application design. It focuses on writing a terminal
       | emulator for Windows, but almost all the concepts apply to nixes
       | as well -- even down to the level of OS primitives like memory
       | mappings to create a ring buffer, etc. He builds a nearly
       | complete terminal that appears to better support unicode and
       | escape sequences than windows-terminal, in drastically less code
       | and with literally orders of magnitude better performance.
       | 
       | In particular, I like his focus on experimentation to figure out
       | a reasonable upper bound on performance (so you can measure
       | success/failure), "non-pessimisation" (don't do things extrinsic
       | to the actual task), understanding the actual machine and its
       | capabilities (he doesn't use the term, but sometimes called
       | "mechanical sympathy"), and tactics for isolating "bad code" that
       | you probably have to use (at least to get started).
       | 
       | https://www.youtube.com/watch?v=pgoetgxecw8
        
       | michaelsbradley wrote:
       | For those interested in learning more about terminal emulators
       | and character graphics, checkout Nick Black's book _Hacking the
       | Planet (with Notcurses): A Guide to TUIs and Character Graphics_
       | [1].
       | 
       | While the overall focus of the book is on programming with
       | Notcurses[2], the author shares a wealth of related info and
       | history throughout its pages.
       | 
       | [1] https://nick-black.com/htp-notcurses.pdf
       | 
       | [2] https://github.com/dankamongmen/notcurses#readme
        
       | nothrowaways wrote:
       | The domain name is hilarious
        
       | leph wrote:
       | While I was an economics student, I somehow landed a job to build
       | out an elevator monitoring system at my college. Figured out that
       | terminal emulators could connect to the elevator controller and
       | that the output spec was called Wyse-60. My first "professional"
       | program was written in Python using ncurses to parse the serial
       | output [0].
       | 
       | The program is very cringe but it was my best attempt at the time
       | as I couldn't find any literature on terminal emulators (what
       | actually happened was that I didn't know what to search for)
       | 
       | Anyways, I've successfully pivoted my career away from economics
       | and into software development which was the goal when I took the
       | job in college.
       | 
       | [0]: https://github.com/lee-pham/Pyse-60
        
         | doctor_eval wrote:
         | I used to use a white phosphor Wyse 60 as my daily driver. It
         | was connected to a DG AViiON running DG/UX - most of the tools
         | were GNU.
         | 
         | Those were the days!
        
       | doodpants wrote:
       | One thing I've been wondering for a long time (and my Google-fu
       | is apparently too weak to find on my own): how do certain console
       | applications change the output color _without_ inserting ANSI
       | escape sequences into the output stream? The specific case I have
       | in mind is when writing console programs in C# /.NET, and using
       | the System.Console.ForegroundColor property to vary output colors
       | on the fly. The resulting output text does not have ANSI escape
       | characters in it, yet the colors are displayed properly in the
       | terminal.
        
         | saulr wrote:
         | On Linux, .NET does write ANSI escape sequences to stdout for
         | you. ConsolePal.Unix.cs (Pal in this context referring to
         | 'platform abstraction layer') does the work for you behind the
         | scenes:
         | https://github.com/dotnet/runtime/blob/10eb1a9ff1c09de4a2a1f...
        
         | imsnif wrote:
         | Personally I'm not a C#/.NET developer so wouldn't know where
         | to look for the source code, but to check you can run the
         | program in the examples of the post with the compiled binary on
         | the other side (in place of the SHELL) and see what output you
         | get.
         | 
         | I'm 99% sure it's inserting ANSI escape codes (I'm maintaining
         | a terminal emulator myself, and really that's how everything
         | works), but I could of course be wrong.
        
           | mattowen_uk wrote:
           | WAY back in the DOS days Qbasic and command.com could both
           | change foreground and background colours via system hooks
           | (INT bios calls) without using ANSI. I know this because
           | Qbasic console apps could be colourful without having to load
           | ansi.sys in config.sys
           | 
           | I am thinking that .foregroundColor and .backgroundColor do
           | the same thing via legacy emulation in conhost.
        
             | queuebert wrote:
             | Turbo Pascal had a Crt library that did this.
        
               | yardshop wrote:
               | True, but you could also get the address of the screen
               | buffer and define a structured type to overlayed it that
               | allowed you to put values directly into memory.
               | 
               | Each screen location was two bytes, one byte for the
               | character value, and one for the character attributes
               | which were a set of bits that controlled red, green, blue
               | and intensity for both the foreground color and
               | background color.
               | 
               | Then you could write routines that would fill in a
               | rectangular region with a color, scroll the text of a
               | region up or down, and do all sorts of other windowy
               | things. There were also interrupt routines you could call
               | that did some of these, and certainly routines in Crt
               | that did some of this. Then you were on your way to
               | developing your own TUI library!
               | 
               | Alternatively, you would issue an interrupt call to put
               | the screen into 320x200 256 color mode, get the address
               | of that buffer ($B800 if memory serves), similarly
               | overlay it with a typed grid, then start poking byte
               | values in and getting all sorts of nice colors out of it.
               | Super fun!!
        
               | imsnif wrote:
               | This comment wins.
        
           | JonathonW wrote:
           | The Windows console is a bit more sophisticated than the
           | traditional *nix "shuffle text back and forth" approach; the
           | Windows Command Line blog goes into some detail here (and in
           | the rest of this series):
           | https://devblogs.microsoft.com/commandline/windows-
           | command-l...
           | 
           | Short version: there is support for ANSI and VT sequences in
           | the Windows console (which relatively recently got
           | substantially expanded), but that's not what it speaks
           | "natively"-- for Win32 console applications, there's a native
           | console API that works by passing IOCTLs back and forth
           | between the app and console driver.
           | 
           | (If you're writing a terminal emulator for Windows, you don't
           | necessarily see this-- the new ConPTY mechanism you use to
           | build these as of Windows 10 abstracts away the Windows
           | specifics so you see text and VT sequences just as you would
           | on *nix.)
        
         | ensiferum wrote:
         | Is this on Windows? If so Windows provides a typical Win32 type
         | of HANDLE API for the console which can be used to change the
         | properties of the console among other things.
        
           | doodpants wrote:
           | To clarify: .NET being cross platform, I write console apps
           | that I run on both Windows and MacOS, and in both cases if I
           | redirect the color output to a .txt file and open it in an
           | editor, there are no ANSI escape sequences in the file. So I
           | was wondering if there is some other standard for sending
           | control codes to a terminal that is independent of the output
           | stream.
           | 
           | Or is there some voodoo by which the ANSI sequences get
           | stripped from the output when redirected to a file?
        
             | irishsultan wrote:
             | > Or is there some voodoo by which the ANSI sequences get
             | stripped from the output when redirected to a file?
             | 
             | Not quite, but programs in general use some voodoo to
             | detect when they are being redirected and won't output ANSI
             | codes when they detect that.
             | 
             | Often there will be a flag to enable/disable color, or let
             | it detect when color is desired. On Linux ls accepts the
             | --color=WHEN parameter, where WHEN can be always (`ls
             | --color=always > ls-with-ansi-codes.txt` will output color
             | codes), it can be never (just don't output ANSI codes, no
             | matter whether you detect output redirection) or it can be
             | auto (will show colors when you execute `ls --color=auto`
             | but not when you do `ls --color=auto > ls-without-ansi-
             | codes.txt`).
        
             | imsnif wrote:
             | They are sometimes stripped when redirecting. Maybe try
             | executing it headless and reading the output?
        
             | ninkendo wrote:
             | Are you manually inserting the ANSI sequences into the
             | stream? Or are you using a library that does this for you?
             | 
             | Often times libraries that let you specify output color
             | will helpfully query the capabilities of the output device,
             | and avoid writing the control codes if it's (for example) a
             | plain text file.
        
             | yumaikas wrote:
             | On windows, there are APIs for setting color that could be
             | ignored if STDOUT isn't a terminal.
             | 
             | On posix platforms, there's an API to check if a given file
             | handle is a tty or not. https://github.com/corasaurus-
             | hex/isatty/blob/main/isatty.c is an example of using the
             | API, in a Janet context.
             | 
             | I assume that .NET's support for Linux/macOS is using that
             | API decide if it should strip color codes or not.
        
             | ensiferum wrote:
             | Well yes like I said on Windows you can access the terminal
             | and set its properties without encoding terminal escape
             | sequences in the output stream. On Linux afaik escape
             | sequences are the only way. On MacOS i have no idea.
             | Generally speaking the escape sequences aren't "standard"
             | but different terminals (and these days terminal emulators)
             | all have their own codes. Hence ncurses which tries to
             | encapsulate this and provide a uniform API so that the
             | application doesn't have to deal with these murky details.
             | 
             | Have you double checked with s hex editor what is in the
             | txt file? Suppose it could also be your text editor that
             | doesn't want to render those codes.
        
       | dekhn wrote:
       | I absolutely love terminals. But, just about the time I could
       | have learned curses, I switched to GUIs. Now I've switched back
       | and uses curses to make UIs (so I can ssh into remote computers
       | with low bandwidth and CPU). It's so funny how terminals are
       | controlled and all the edge cases of each implementation.
        
       | eatonphil wrote:
       | Well done guide! Was visually enjoyable to read/watch as well as
       | being well-written.
        
       | timw4mail wrote:
       | Why do I have to click a link to read the article? I don't
       | understand why it's a thing to add a "fold" to a web article.
       | 
       | More on topic, this does seem like a decent overview.
        
         | Uberphallus wrote:
         | Not a design/advertising guy, but often times I think it's to
         | be able to display an ad at the bottom without the user to
         | fully read the article, and without putting it ahead of the
         | article (which is ugly IMO).
         | 
         | Here though I don't think it's the case.
        
           | imsnif wrote:
           | There are no ads on my blog and will never be. That's a
           | promise :)
        
             | mongol wrote:
             | That is why you are a poor dev :-)
        
               | PaulHoule wrote:
               | Ads on dev blogs pay peanuts, if that. It's a way to stay
               | poor.
        
         | imsnif wrote:
         | Sorry about that! The SVGs are a bit on the heavy side and the
         | various social preview crawlers didn't appreciate it.
        
           | forrestthewoods wrote:
           | I put my heavy blog content on BunnyCDN. It's impossibly
           | cheap. I'm happy to pay a few quarters if a blog post happens
           | to blow up.
        
           | hwc wrote:
           | Would a `<details>` block also work?
        
           | ggerganov wrote:
           | Hey, I really like the animated diagrams in your posts. Could
           | you share a few tips about how you create them?
        
             | imsnif wrote:
             | Thanks! I use Inkscape to create the SVGs and then animate
             | them with javascript and greensock. I still haven't quite
             | found the right layer of abstraction for this, so it is a
             | tedious process that takes a lot of time. Once I do, I
             | might release a tool that will help with it.
        
       | davemp wrote:
       | Great article. Terminal emulators / shells are an area where we
       | haven't seen much improvement for years. I can't help but think
       | there could be a much nicer command-line-esque interface other
       | than a classic psuedoterminal and shell. At the same time, I
       | doubt any improvements could really be large enough to gain
       | adoption.
        
         | matheusmoreira wrote:
         | Can terminals even be improved without breaking everything?
         | Even maintenance work and fixing bugs has historically caused
         | problems:
         | 
         | https://lwn.net/Articles/343828/
        
         | vidarh wrote:
         | There have been _many_ attempts. Sixel and ReGIS being among
         | the oldest attempt at augmenting terminals with graphics. But
         | most modern apps that run in terminals don 't even push the
         | limits of what terminals can do, and that makes it kind-of hard
         | to justify putting effort into adding more capabilities to
         | terminals.
         | 
         | Part of the challenge is to find capabilities to add that are
         | sufficiently simple and compelling to use in command line apps
         | vs. as a web app or native GUI app that'd be more widely
         | accessible than a new terminal capability.
        
           | imsnif wrote:
           | IMO the issue is not the capabilities of the terminal, but
           | rather the culture around it. It is very often seen as a
           | "leet" platform for hackers and advanced users. Its users
           | looked at with respect and envy. Which is unhealthy for the
           | kind of adoption one would wish for in their apps.
           | 
           | Really, I think this mostly requires imagination from those
           | of us developing for this platform. We have all the
           | capabilities we need. We just need to create apps that don't
           | require you to look up cheat sheets and man pages to use, and
           | provide powerful alternatives to existing GUIs. There's a bit
           | of a renaissance in this area as of late, but I feel this is
           | just the beginning.
        
             | lakecresva wrote:
             | Could not agree more with this. Thanks for the interesting
             | article and your work on zellij.
        
             | vidarh wrote:
             | I think we're looking for very different things.
             | 
             | I use my own text editor. I expect to remain its only user,
             | and that's fine. It's not intentional. It just isn't a
             | priority for me to make it usable for anyone else (though I
             | do separate out parts of it into libraries / gems as
             | functionality matures).
             | 
             | As such, I'm looking for improved terminals not to make
             | things that doesn't require cheat sheets (because to use my
             | editor you'd have to be comfortable with reading the
             | source). It's fine that others want other things, but I
             | think this is part of the challenge with improving
             | terminals: A lot of the user base _are_ advanced users who
             | want to improve their own workflows, not write something
             | user friendly, and who wants tools that are easy to combine
             | and chain, where the priority is not something user
             | friendly.
             | 
             | The balance is fine in that if you're looking for something
             | particularly user friendly in the sense of friendly to less
             | advanced users, targeting a GUI framework or the web is
             | very quickly going to provide a better experience.
             | 
             | EDIT: I saw zellij mentioned in a sibling, and looked at
             | it, and incidentally it's quite similar to my setup, except
             | I use bspwm and scripts to manipulate the tiling so that
             | e.g. doing a vertical or horizontal split in my editor
             | results in a new editor instance in a new tiled window
             | rather than in a new pane in a single terminal. That way I
             | can treat the few GUI apps I use exactly the same way
             | (mostly Chrome). I'd love it if there was a standard API to
             | split panes and start apps in a given pane, so both
             | terminal multiplexers and tiling wms could be targeted with
             | a single standard mechanism.
        
               | imsnif wrote:
               | I agree that's how things are today. I get where you're
               | coming from and I'm certainly not trying to take this
               | away from you.
               | 
               | My experience has taught me that tools become better the
               | more people use them and are able to participate in their
               | creation and maintenance. The more user friendly a tool
               | is, the more users you'll have. Personally, I think a
               | tool can be very user friendly and still powerful enough
               | for advanced users.
               | 
               | I used a very similar setup to yours before I started
               | Zellij! The reason I started the project was in order to
               | be able to formalize such a setup (for me it was
               | implemented as a soup of bash scripts which I dreaded
               | moving to another machine, not to mention handing to
               | another user).
               | 
               | One of the things we want to do with Zellij is port it to
               | the web and maybe even in the future to use it as a sort
               | of "backend" to power tiling window managers. I'd be
               | totally open to do this in a standardized way if
               | maintainers of other tiling managers are game.
        
               | vidarh wrote:
               | > My experience has taught me that tools become better
               | the more people use them and are able to participate in
               | their creation and maintenance. The more user friendly a
               | tool is, the more users you'll have. Personally, I think
               | a tool can be very user friendly and still powerful
               | enough for advanced users.
               | 
               | I've taken the approach that rather than try to shoehorn
               | my use into a full app, I aim for my text editor to be as
               | small as possible, by reusing external tools whenever
               | possible, as I do agree with you that it's worthwhile to
               | have as much as possible of the code used by other
               | people. But at the same time I realised there are editors
               | smaller than my old Emacs config. So e.g. my editor
               | relies on bspwm to split panes, and on rofi (or anything
               | that can take a list of things to choose from and return
               | the chosen thing) to select files or themes or buffers,
               | Rouge for syntax highlighting, and anything that I can
               | make generic enough I'm splitting into gems (the editor
               | itself is written in Ruby). The way I see it, I want the
               | editor to be a tiny little core that's mostly configuring
               | other components. Currently it's about ~2.6kloc, but much
               | of that is code that can be split out or will disappear
               | as I clean some things up. I don't want it to get much
               | bigger than that - preferably it'll get smaller.
               | 
               | > I used a very similar setup to yours before I started
               | Zellij! The reason I started the project was in order to
               | be able to formalize such a setup (for me it was
               | implemented as a soup of bash scripts which I dreaded
               | moving to another machine, not to mention handing to
               | another user).
               | 
               | The big limiting factor for me with something like Zellij
               | over my current setup would be having it work alongside
               | e.g. Chrome and the occasional other gui app.
               | 
               | > One of the things we want to do with Zellij is port it
               | to the web and maybe even in the future to use it as a
               | sort of "backend" to power tiling window managers. I'd be
               | totally open to do this in a standardized way if
               | maintainers of other tiling managers are game.
               | 
               | If you provide a mechanism that allows a client to split
               | panes already, then maybe the easiest starting point is
               | for someone to just pick a config location/format tools
               | can look for a command line to do splits in. E.g. on
               | bspwm, the command given to split horizontally might
               | simply be sh -c 'bspc node -p east ; exec #{cmd}' &. On
               | i3wm, the same would be i3-msg 'split horizontal; exec
               | #{cmd}'. Currently my editor just blindly executes
               | "split-horizontal re --buffer numeric-id-of-the-buffer",
               | and I have a "split-horizontal" script in my ~/bin. It'd
               | be trivial enough to have it read a config file to find
               | out what command to execute instead.
        
         | 0235005 wrote:
         | I think that for sure something line the Plan9 shell woukd be
         | something cooler to have
        
           | MisterTea wrote:
           | As in graphics in the terminal? The way that works is through
           | the draw(3) kernel device which is a 2d engine with an rpc
           | interface. You load text and bitmaps into the draw device and
           | then issue rendering commands. The kernel terminal device,
           | cons(3) is where the terminal text is written to and cons
           | sends that to to draw. When you start a graphical program, it
           | overwrites the window graphics from cons(3) until the
           | graphical program exits or the window deleted. There is no in
           | band cursor control in plan 9 as it is a graphics oriented
           | OS.
           | 
           | So its not just porting a terminal. It's the entire OS. Of
           | course there is p9p, plan 9 port, which is a port of the core
           | plan 9 user space tools to Unix systems. It does offer a draw
           | server that can be mounted.
        
             | vidarh wrote:
             | A lot of terminals supports at least somewhat similar
             | functionality via Sixel (bitmaps) and ReGIS (vector
             | graphics). It's limited and certainly could be improved a
             | lot, though.
        
         | uuddlrlr wrote:
         | TempleOS comes to mind.
        
         | imsnif wrote:
         | I totally agree. I'm actively working in that direction.
        
       | 0xdky wrote:
       | Very well written article, thank you.
       | 
       | Especially when my daughter is taking a course on Unix and the
       | instructor is superficially touching in these fundamental topics
       | (due to their own limited understanding), I am going to use this
       | to teach the underlying concepts.
        
       | hawski wrote:
       | I recommend going through st source:
       | https://git.suckless.org/st/file/st.c.html
       | 
       | I may have fallen out of love to suckless.org, but the code is
       | usually simple so one can at least learn from it.
        
         | matheusmoreira wrote:
         | I recommend it as well. It's the simplest terminal emulator
         | I've found, other projects I've explored were a lot more
         | complex.
        
         | dudik wrote:
         | Why have you fallen out of love to suckless? I recently
         | switched from dwm back to bspwm and I was also looking for a st
         | replacement, but couldn't find one. Which is ok, but were you
         | able to find something "better" in the terminal emulator or
         | window manager space?
        
           | hawski wrote:
           | I wanted/tried to be a part of the community. I could say I
           | was not a cultural fit.
           | 
           | As much as I sympathize with cutting things down and making
           | frugal software I am too messy to make it all work for my
           | advantage. I now prefer to mix and match and not attaching
           | myself to any group. But I am fond of the actual code. It is
           | refreshingly simple and easy to track. I was using dwm the
           | most from all of the projects and I still think of going back
           | to it. Though currently I'm getting tired of software all
           | together.
        
       ___________________________________________________________________
       (page generated 2021-11-02 23:00 UTC)