magiblot/tvision

Query terminal color capabilities

electroly opened this issue · 10 comments

In my RGB color picker, I find it would be helpful to know what the terminal supports. There are two related situations I'm concerned about:

  1. The user's terminal only supports 16 colors, and I'm showing a 256 color palette. Swaths of colors in the palette look the same to the user, but they really have different RGB values. If the user clicks a color from the palette, they don't know what color they've actually selected and if their work is later displayed on a 256 color terminal, the colors won't be what they had intended. It seems better to show a 16 color palette to users who can't see 256 colors.

  2. The user has TERM=vt100. In this case I'd hide the palette entirely and make the user enter R/G/B numbers.

image
image

Hey Brian, sorry for the wait.

The classical way to check for screen capabilities is to look at the TScreen::screenMode variable:

if ((TScreen::screenMode & 0xFF) == TDisplay::smMono)
    ; // Monocolor (as in `TERM=vt100`).
else if ((TScreen::screenMode & 0xFF) == TDisplay::smBW80)
    ; // Grayscale (not used by any terminal I know of).
else // if ((TScreen::screenMode & 0xFF) == TDisplay::smCO80)
    ; // Color (as in `TERM=xterm`).

TDisplay::smMono, TDisplay::smBW80 and TDisplay::smCO80 are DOS video modes. Although we are not in DOS anymore, these values continue to be used so that the application can check the screen color mode.

But what actually controls the look of Turbo Vision applications are the global variables Boolean TView::showMarkers, which enables markers (» «) in several views, and int TProgram::appPalette, which selects between the three predefined application palettes (cpAppMonochrome, cpAppBlackWhite, cpAppColor).

Although the hierarchical palette system feels obnoxious, it has the merit of allowing applications to adapt themselves to different color modes without becoming more complex. Note how not a single view bundled with Turbo Vision ever checks on TScreen::screenMode in its draw method to reason about screen capabilities.

My objective is to prevent client applications from reasoning about terminal capabilities as much as possible, because it only adds complexity. For example, the number of colors supported by terminals is usually one of 0, 8, 16, 256 and 256³. If you have to worry about each of these cases, you need at least 5 if statements, and every time you forget one of those ifs you get a bug.

In the end, what we want is to provide a good user experience. For this reason my initial proposal is the following:

  • New flag TDisplay::smColor256 which can be tested in TScreen::screenMode and which indicates that the display supports 256 colors or more.
  • New flag TDisplay::smColorHigh which can be tested in TScreen::screenMode and which indicates that the display supports enough colors not to worry (e.g. true color).

This should provide you with enough information to:

  • Decide which kind of color picker should be shown.
  • Show a warning to advice the user to fix their terminal environment.

So, to sum it up, you would be able to check color modes as follows:

if ((TScreen::screenMode & 0xFF) == TDisplay::smMono)
    ; // Monocolor (as in `TERM=vt100`).
else if ((TScreen::screenMode & 0xFF) == TDisplay::smBW80)
    ; // Grayscale (not used by any terminal I know of).
else // if ((TScreen::screenMode & 0xFF) == TDisplay::smCO80)
{
    // Color mode. 16 colors are assumed to be available by default.
    // New flags provide additional about the display's color support:
    if (TScreen::screenMode & TDisplay::smColorHigh)
        ; // Enough colors not to worry, e.g. true color.
    if (TScreen::screenMode & TDisplay::smColor256)
        ; // 256 colors or more.
}

The user has TERM=vt100. In this case I'd hide the palette entirely and make the user enter R/G/B numbers.

In my honest opinion, the most useful thing you can do is show a warning to the user, because I very much doubt this will happen for a reason other than a configuration issue. If this happens often, and users can't change it, another option would be for Turbo Vision to force color support, because a real vintage terminal would not be able display Turbo Vision properly anyway (think of UTF-8 support).

For the use case of enabling the black-and-white mode out of curiosity, this can always be done by manipulating the Boolean TView::showMarkers and int TProgram::appPalette variables.

Regarding the color picker, I'd like to suggest a different design:

mpv-shot0008

On the left is the original version, and on the right is how it looks with the xterm-256color palette. Taken from https://www.w3schools.com/colors/colors_picker.asp.

This design was used back when 256-color or 16-color video adapters were still common. For example, in Word 97:

Screenshot_20210330_001131

Although I acknowledge it may be difficult to replicate in a cell-based display.

Commit a1b4887 (in a separate branch) contains the proposed extensions so that you can try them out.

Also, I forgot to say that to import the TDisplay constants you need to #define Uses_TScreen.

Thank you for the thoughtful reply! Your proposal looks to me like it will work great. As a matter of curiosity I've been trying to support TERM=vt100 but really only because it seems like tvision itself has gone to some effort to support it (e.g., with the markers for buttons and such). None of my intended target platforms would actually be restricted to vt100, so maybe I should indeed just not worry about it. As a practical matter, 16-color vs. 256-color is the only distinction that I'd want to know about.

I like the suggestion about the hexagonal color picker. I think I can make it work; the hexagon should fit in a 26x13 bounding box if each cell is a 2x1 rectangle. I have indeed already found my color picker design to be difficult to use because similar colors are not physically near each other.

Another thought comes to mind -- is there a way for me to call tvision's algorithm for reducing an RGB triple down to one of the 16 colors? It might be nice to show 256-color users how their selected color will appear on a 16-color display without them having to actually change their terminal settings.

Thanks!

is there a way for me to call tvision's algorithm for reducing an RGB triple down to one of the 16 colors?

Yes, there are two ways to do this:

  1. The RGBtoBIOS free function or the TColorDesired::toBIOS(bool isForeground=true) member function to convert a single color.
  2. The TColorAttr::toBIOS() member function to convert the whole cell attributes at once (probably more fit for your case).

Perfect, thank you. I'll give it a try!

I couldn't figure out how to extend that hexagonal color chart to include the full xterm-256color palette. Here's what I came up with instead. It's not perfect but I think it'll do. I detect 16 vs. 256 colors using your suggested code and show the appropriate picker. In the 16-color picker the status text explicitly mentions reading the help to enable more colors.

It looks like xterm supports only 8 background colors, but Windows conhost supports 16 background colors. I couldn't figure out how to differentiate the two, and ended up using an #ifdef _WIN32 to decide whether to show 8 or 16 background colors. It would be nice to get this info from tvision somehow.

image

Hey, Brian. Good job on the color picker!

It looks like xterm supports only 8 background colors, but Windows conhost supports 16 background colors. I couldn't figure out how to differentiate the two, and ended up using an #ifdef _WIN32 to decide whether to show 8 or 16 background colors. It would be nice to get this info from tvision somehow.

Actually, 16+8 colors is what is originally supported by Turbo Vision. Even the built-in color picker only allows choosing 8 background colors:

imatge

But in the case of terminal emulators, I very much doubt any terminal emulator which is still reasonably used today does not support 16 fg+bg colors. If you only get 8 background colors, something is likely wrong with the terminal settings. In the case of XTerm you must set the XTerm.termName: xterm-256color option in the ~/.Xresources file. If the terminal does not support 16 bg colors even with TERM=xterm-16color, you should ask yourself if it wouldn't be better to switch to another terminal emulator.

By fixing the terminal settings not only you get more colors in TMBASIC, but also in any other application, so the improvement in user experience is greater than if we spent that effort in handling suboptimal situations in our applications. Although I usually like things to work out-of-the-box, in this case I think the defaults are not good enough and letting the users revise their terminal settings manually is worth it.

Having said this, it costs me nothing to add another screen mode flag. But I still don't like the idea of applications having to worry much about the display capabilities, so maybe I could leave 16 fg+bg color support always enabled.

Cheers.

The main issue I have with requiring configuration is I'm stuck with a game of telephone: my users are the TMBASIC programmers, and they will produce and distribute executables to their own users who know nothing about TMBASIC. Any instructions I give about terminal configuration will make it to the programmers, but it's up to them to pass that information on to their own users. I have little confidence that this will happen. So I think ultimately I need to live with the defaults available in the popular terminal emulators, because those downstream users won't know any better.

maybe I could leave 16 fg+bg color support always enabled.

Do you mean that you could force 16 bg colors for TERM=xterm? In practice you're right, no popular terminal emulator actually limits background colors to 8, it's just the result of the TERM configuration being wrong. If forcing 16 bg colors actually works, that would be the best solution for me.

Do you mean that you could force 16 bg colors for TERM=xterm? In practice you're right, no popular terminal emulator actually limits background colors to 8, it's just the result of the TERM configuration being wrong.

It's not that terminals limit background colors, the question is whether they support the escape sequences which set the bright colors. All I can do is emit those escape sequences even if the terminal capabilities do not advertise 16 colors. The situation where the terminal sets TERM=xterm but actually supports more colors is quite common, so I guess this will be actually useful.

Implemented in 287fc13.

I've updated to the latest. Looks great! Every terminal I tried that defaults to TERM=xterm is happy to display the 16-color background palette. I've removed the 8-color version of my color picker. Closing this issue because you've taken care of everything. Thanks for all the good work!