dankamongmen / notcurses

blingful character graphics/TUI library. definitely not curses.
https://nick-black.com/dankwiki/index.php/Notcurses
Other
3.61k stars 113 forks source link

need a method of getting screen geometry in windows #1982

Closed dankamongmen closed 3 years ago

dankamongmen commented 3 years ago

In Windows, we don't have termios. We need a method to get the terminal's geometry, ideally both in pixels and cells, or else most routines won't work. Right now we've simply got these routines #defined out for Windows.

dankamongmen commented 3 years ago

saw this over on https://rosettagit.org/drafts/terminal-control-dimensions/; i'd want to do more research before blindly plugging this in anywhere.

#include <windows.h>
#include <wchar.h>

int
main()
{
    HANDLE console;
    CONSOLE_SCREEN_BUFFER_INFO info;
    short rows;
    short columns;
    /* Create a handle to the console screen. */
    console = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
        0, NULL);
    if (console == INVALID_HANDLE_VALUE)
        return 1;

    /* Calculate the size of the console window. */
    if (GetConsoleScreenBufferInfo(console, &info) == 0)
        return 1;
    CloseHandle(console);
    columns = info.srWindow.Right - info.srWindow.Left + 1;
    rows = info.srWindow.Bottom - info.srWindow.Top + 1;

    wprintf(L"%d columns by %d rows\n", columns, rows);

    return 0;
}
dankamongmen commented 3 years ago

is it possible that it supports CSI 14 t? as implemented just now in #1891?

dankamongmen commented 3 years ago

if so, we're going to get this solved for free =]. let's have them implement it if they don't now!

dankamongmen commented 3 years ago

it doesn't look like this is likely to happen anytime soon in Windows Terminal, but they're not implementing any bitmap support, either, so it hardly matters.

dankamongmen commented 3 years ago

yeah, i'd say until they're supporting a bitmap protocol in Windows Terminal, we have no need for this. cheerfully closing! @j4james makes good points in the bug linked to in #1891.

j4james commented 3 years ago

For the record, I do actually have a Sixel-supporting fork of the Window console, and I can confirm that notcurses doesn't recognise it as supporting bitmaps, but screen geometry isn't the problem (at least not the only problem). The first place it fails is on your query of XTerm's color register report, which also prevents notcurses working on Mintty (at least the version I have), and several other Sixel-supporting terminals. If I were you, I'd just drop that check and assume 256 colors - in theory you could probably generate a 256-color palette which falls back to a reasonable grayscale on terminals that only have 16 colors.

And as far as screen geometry goes, I'm not sure TIOCGWINSZ could ever be supported on Windows, and in general it's not a great approach, because even on Linux it doesn't work over telnet AFAIK (I'm not sure about ssh though). Also the sizes are unreliable, because some terminals return the text area, and some return the window size (which may be bigger). You can usually get away with rounding in those cases, but one day you're going to encounter a terminal that is using panes, and then those values will be completely meaningless.

And note that CSI 14t has the same sort of problem (i.e. some terminals report window size and some report text area), so you're also setting yourself up for failure if you rely on that. If what you're actually trying to determine is the cell size, then I think your best bet is CSI 16t. If that doesn't work, you can fallback to CSI 14t. And if neither of those work, I'd recommend you assume a cell size of 10x20, which is what the VT340 used (that'll get you support on all the real terminal emulators, including my Windows console fork).

And while we're on the subject of a VT340-compatible cell size, I should mention that this was something that was being considered for the VTE Sixel implementation too. I don't think anything has been decided one way or the other, but we were discussing whether it was feasible for terminals to be VT340-compatible by default (i.e. fix the cell size to 10x20), but allow modern apps a way to request pixel-perfect Sixels if that's what they really wanted.

I eventually gave up on the idea as unworkable, because it would require buy-in from libraries like notcurses and Jexer, which I didn't think was likely. But if you're interested, you may want to follow up with the VTE devs.

dankamongmen commented 3 years ago

For the record, I do actually have a Sixel-supporting fork of the Window console, and I can confirm that notcurses doesn't recognise it as supporting bitmaps, but screen geometry isn't the problem (at least not the only problem). The first place it fails is on your query of XTerm's color register report, which also prevents notcurses working on Mintty (at least the version I have),

wait, so do you have a running notcurses binary on windows? i just barely have one, in a branch. or is this based on analysis of the code? either way, let's get a bug report on it! what have you been doing over there, you secretive fellow?

do you mean that windows console and mintty both do not answer XTSMGRAPHICS for the creg case? i guess i can go find out easily enough.

and several other Sixel-supporting terminals. If I were you, I'd just drop that check and assume 256 colors - in theory you could probably generate a 256-color palette which falls back to a reasonable grayscale on terminals that only have 16 colors.

well, i can presumably use some other signal, while retaining that check. i'm thinking use the result from it if one is provided, but otherwise, if the max pixel XTSMGRAPHICS is answered, assume 16 colors, or even 2 colors, or a single color (probably 16, either way i'm talking piffle for now). looks like the vt330 only offered 4 colors, so i'd want to build that map up in heuristics, assuming i can differentiate between the two.

And as far as screen geometry goes, I'm not sure TIOCGWINSZ could ever be supported on Windows, and in general it's not a great approach, because even on Linux it doesn't work over telnet AFAIK (I'm not sure about ssh though). Also the sizes are

it does work over ssh, in all my experimentation

unreliable, because some terminals return the text area, and some return the window size (which may be bigger). You can

yep, this world is sadly imperfect. ideally this would be addressed by heuristics, where possible (i.e. for known, rigorously-differentiated cases). where not possible, we'd do something else, up to and possibly including disabling sixels.

usually get away with rounding in those cases, but one day you're going to encounter a terminal that is using panes, and then those values will be completely meaningless.

well, not necessarily "completely meaningless"; more likely "less meaningful". there are already fundamental races that keep even rows and columsn from working in the sense of me having a correct idea of their values, when i need them -- SIGWINCH is a signal, and can come in while i'm writing, can be dropped, etc. if i think the screen to be a few pixels bigger than it is, well, hopefully it eats the extra pixels. if it disrupts the image, hopefully i get a bug report and deal with yet another individual snowflake of a terminal. it's an if i add once. not so big a cost.

And note that CSI 14t has the same sort of problem (i.e. some terminals report window size and some report text area), so you're also setting yourself up for failure if you rely on that. If what you're actually trying to determine is the cell size, then I think your best bet is CSI 16t. If that doesn't work, you can fallback to CSI 14t. And if neither of those work, I'd recommend you assume a cell size of 10x20, which is what the VT340 used (that'll get you support on all the real terminal emulators, including my Windows console fork).

can i assume such "real terminal emulators" export a TERM of vt340, or express that they are a vt340 via some escape? i can definitely hardcode 10x20 in association with a conclusively-identified vt340, and also 16 colors. that sounds like a great idea.

i'm surprised the Terminal of Record chose a cell height that wasn't a multiple of 6 (yes, i'm aware sixel was originally for printers).

And while we're on the subject of a VT340-compatible cell size, I should mention that this was something that was being considered for the VTE Sixel implementation too. I don't think anything has been decided one way or the other, but we were discussing whether it was feasible for terminals to be VT340-compatible by default (i.e. fix the cell size to 10x20), but allow modern apps a way to request pixel-perfect Sixels if that's what they really wanted.

i can work with either one, so long as i know the true definition. i already have to deal with cell sizes that aren't multiples of 6, which i do by supplying transparent pixels on any excess lines--another reason why implementation of P2=1 Sixel unspecified pixels as transparent is important, when possible.

I eventually gave up on the idea as unworkable, because it would require buy-in from libraries like notcurses and Jexer, which I didn't think was likely. But if you're interested, you may want to follow up with the VTE devs.

my good man @j4james, you underestimate me. what have i done to give such offense? i want the best world possible given the imperfect constraints forced upon us. wherever people seek to change those constraints, so long as it does not unduly damage the ecosystem, i am with them. all it costs is my effort, and though a hypercapitalist, i'm a New Soviet Man when it comes to writing open source wholly unrelated to my job.

j4james commented 3 years ago

wait, so do you have a running notcurses binary on windows?

Not in the sense you mean. I'm just running it in the WSL subsystem. A pure Windows build is of less use to me, because the Sixel functionality would then have no way of working on anything other than the Windows console. Third-party terminals have to run over the conpty layer, which can't support Sixel at this point.

do you mean that windows console and mintty both do not answer XTSMGRAPHICS for the creg case?

I don't know about the latest Mintty, but the version I have (3.5.0) doesn't support it. And last I tested, neither did Yaft, st, Reflection Desktop, and IBM Personal Communications (those are all Sixel-capable btw). Mintty does support CSI 14t and CSI 16t, but the others don't AFAIK.

looks like the vt330 only offered 4 colors

Yeah, I think VT330 had 4 shades of gray, but the VT340 had 16 colors (or 16 shades of gray on the monochrome version), and I'd say that's the minimum you're likely to encounter in modern terminals. But again you could probably generate the palette in such a way that you get at least some kind of fallback on a 4-color device if you really cared. I need to try that out to see how well it works.

SIGWINCH is a signal, and can come in while i'm writing, can be dropped, etc.

This is where having a fixed cell size is an advantage - it's unaffected by font size changes mid-render. And one of the ideas we were discussing (in the VTE tracker), was for apps that want pixel-perfect Sixels to include the target cell size as additional parameters on the raster attributes command. So if you've queried the terminal and determined a cell size of say 7x14, and mid-render the user changes the font size to 8x16, the terminal would still be able to render that image correctly because it would know it was targetting a 7x14 cell and scale it accordingly.

can i assume such "real terminal emulators" export a TERM of vt340, or express that they are a vt340 via some escape?

I wouldn't think you could rely on that. Generally an emulator will let the user choose what terminal to identify as, but would probably default to the highest level they support. As for the current Windows implementation, I've got my fork identifying as a level-2 device (so somewhere in the VT2xx range) with a Sixel extension, since we really don't have most of the level-3 capabilities. And in the distant future we might be identifying as a VT5xx, but I'd still implement Sixels in a VT340-compatible way, just because most things work better that way IMO.

i'm surprised the Terminal of Record chose a cell height that wasn't a multiple of 6

The VT382 variants had a cell height of 30 if that's any consolation, but they were less widely used as far as I'm aware.

my good man @j4james, you underestimate me. what have i done to give such offense?

Well the DECSDM fiasco was one sign that getting cooperation on interoperability was going to be something of an uphill battle. 😉 And in general my attempts to push for a backwards-compatible Sixel standard has been meant with more hostility that I had expected (from various people). I think my best strategy going forward is to push for XTerm to improve its VT340 compatibility, and rely on that driving apps to adapt accordingly.

j4james commented 3 years ago

Btw, I tried hacking out the color register check, and made the cell size query fall back to 10x20, so I could eventually get the demo working on my console fork. The Sixel parts work surprisingly well as far as I could tell, albeit slow as balls (but I was kind of expecting that).

I could also get Mintty working that way, but that required manually selecting a 10x20 font. It doesn't work as well - there's a lot of black flickering behind images - but I suspect that's because it doesn't support transparent background select. I don't know if that's something you've seen with other terminals? As far as I'm aware, there are very few that support transparency.

dankamongmen commented 3 years ago

Well the DECSDM fiasco was one sign that getting cooperation on interoperability was going to be something of an uphill battle. And in general my attempts to push for a backwards-compatible Sixel standard has been meant with more hostility that I had expected (from various people). I think my best strategy going forward is to push for XTerm to improve its VT340 compatibility, and rely on that driving apps to adapt accordingly.

yeah this was not my proudest moment; i admit without reservation that i made the wrong call here, in a rush to have things working, rather than doing what was right in terms of engineering.

dankamongmen commented 3 years ago

I'm now using GetConsoleScreenBufferInfo() on the output `HANDLE* and successfully retrieving cell geometry on Windows, at least in a local Windows Terminal. It doesn't work for local mintty, and I doubt it works for anything else, really, but it does get us correct cell geometry for this important case.