-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Allow terminal programs to display text in different sizes #8226
Comments
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Thank you for making a comprehensive RFC! From my experience with images in TUIs, when some operation is not contained to a single cell, then it's imperative to be able to know the precise cell area that is affected, not only to avoid unnecessary redrawing of cells, but also simply to fit UI elements together. Even rendering plain text has pitfalls when accounting for multi-width characters, where the rendered width depends on font and renderer. Rendering text twice as big seems straightforward, the area is just I could use this right away for my project mdfried, which displays big text by means of re-rendering it as images. Thanks for adding a way to query for support of this feature! I think I might even be able to extract the big-text part into a ratatui-widget, including support for this text-sizing-protocol. Cool stuff! |
No, not quite. The number of cells is determined solely by the scale and The main use of fractional scales is for super/subscripts and for |
But I can calculate the width, right? Like I would calculate the width anyway, for line wrapping etc., just adjusted to the scale. Examples:
I could break up a line too, if I had to fit it within some constraint I can use those calculations. I'm ignoring multi-width characters, so far I have just used textwrap. I understand that it's not 100% accurate, but it's not really feasible to query how wide some text renders, before rendering, in ratatui's rendering style - if this querying is at all possible. My use case is markdown headers, there are different "tiers", 1-6 IIRC. The easiest is to render tier 1 with a scale of 2.0, and the others proportional between 1.0 and 2.0. That's how it's done in mdfried and it looks quite good. |
I like the idea of removing the need to agree on a width and putting it solely
in the control of the running application. I think this part of the feature will
be the most valuable to the ecosystem is a whole. I am certain there will be
projects that balk at the idea of scaled text, but would benefit from the
explicit width portion. In that sense, it would be nice if the width portion was
pulled out to it's own sequence, with it's own query.
One option for a separate sequence for that would be as an SGR sequence with a
subparam. Something like CSI 66 : Ps m, where Ps is the width, defaults to 0
with the same meaning as your spec. This would allow simple applications to
blindly use it and hope for the best, but their text still printed. One downside
of the OSC is that you *must be certain* an application supports it, or your
text will disappear. But for some low-risk things like say - a terminal prompt -
you could put it in and not worry about support because you'll just be off a
little bit, but it's the primary screen and has less damage. A full sequence
might be:
\x1b[66:2m💪\x1b[66m
or even
\x1b[66:2m💪\x1b[m
Querying for support *could* be done the same way here, however that is the
other change I would suggest: explicity query-able support. In the case where a
terminal *does* support this, text will be printed to the screen during
application initialization. This can be prevented with sync, which I assume most
terminals supporting this would also support, but it is less than ideal.
Ideally, we'd have a nice protocol that tells you the names of features
supported but barring we don't invent one right now, I think the next best would
be to report it via XTGETTCAP. This way you get a single round trip and don't
end up printing to the screen.
Those are my initial thoughts on the protocol. I will take a look at the actual
implementation this week.
Awesome work, as always!
…--
Tim
|
On Sat, Jan 18, 2025 at 05:02:40AM -0800, Benjamin Grosse wrote:
But I can calculate the width, right? Like I would calculate the width anyway, for line wrapping etc., just adjusted to the scale.
Yes, you can calculate the width it is completely deterministic and
controlled by the parameters s and w
|
@rockorager This protocol allows setting width without scale (aka scale=1) and the two can be queried for independently as well. If a terminal wants to implement only width and not scale it can do so. I will add a note to clarify that to the spec. I dont yet see a need for an independent query escape code, since the CPR based query works fine for querying both width and scale independently. If the protocol evolves further we can revisit. Also, I highly doubt there will ever be any terminal that implements this but does not implement sync, but even if that becomes the case, one can print spaces so the only thing that the user might see is cursor movement, which can also be removed by hiding the cursor before querying. As for using a CSI based global state I deliberately chose against that because I dont like global state it just makes debuggability harder. You have to worry about resetting it, what happens if an application using it crashes, without restoring, etc. |
Added a note about separate querying for scale and width: b552d77 |
fyi, I plan to support the "width" part of the protocol in foot (https://codeberg.org/dnkl/foot/pulls/1927), unless there are major architectural changes to the protocol that makes me change my mind :) |
Cool, good to know. I don't anticipate any major changes to the protocol, but this is the RFC period so no guarantees. |
Merged into master. Will become kitty 0.40.0 |
Implement explicit width hint extension, developed by kitty. When both explicit width and mode 2027 are available, we default to explicit width. Custom event loop authors will need to update their loops to add support for this by setting the new capability value. For simplicity, we don't actually add a flag in the parser for checking between a cursor position and an F3 key. Instead, we send the cursor home, then do an explicit width command, *then* check the cursor position. If the cursor has moved - meaning the extension is supported - we will see an F3 key with the shift modifier. The response will be something like `\x1b[1;2R` which we parse as a shift+F3. But in the loop, we check the flag if we have sent queries and handle this specific event differently. Reference: kovidgoyal/kitty#8226
Implement explicit width hint extension, developed by kitty. When both explicit width and mode 2027 are available, we default to explicit width. Custom event loop authors will need to update their loops to add support for this by setting the new capability value. For simplicity, we don't actually add a flag in the parser for checking between a cursor position and an F3 key. Instead, we send the cursor home, then do an explicit width command, *then* check the cursor position. If the cursor has moved - meaning the extension is supported - we will see an F3 key with the shift modifier. The response will be something like `\x1b[1;2R` which we parse as a shift+F3. But in the loop, we check the flag if we have sent queries and handle this specific event differently. Reference: kovidgoyal/kitty#8226
I've added support for this in libvaxis. Works really well! recording.mp4 |
Implement explicit width hint extension, developed by kitty. When both explicit width and mode 2027 are available, we default to explicit width. Custom event loop authors will need to update their loops to add support for this by setting the new capability value. For simplicity, we don't actually add a flag in the parser for checking between a cursor position and an F3 key. Instead, we send the cursor home, then do an explicit width command, *then* check the cursor position. If the cursor has moved - meaning the extension is supported - we will see an F3 key with the shift modifier. The response will be something like `\x1b[1;2R` which we parse as a shift+F3. But in the loop, we check the flag if we have sent queries and handle this specific event differently. Reference: kovidgoyal/kitty#8226
As a note to possible later implementers, I don't actually have any other place in libvaxis that does a cursor position report. So instead of adding a flag in the parser for determining if I have an F3 or a cursor position report, I've instead done the query like so:
Then in my event handler, I can check the flag and if I received a shift+F3 ( |
Implement explicit width support based on the spec developed by kitty. In the presence of both mode 2027 and explicit width, we opt for explicit width Reference: kovidgoyal/kitty#8226
You mean you added support for the width part not scale, correct? |
You mean you added support for the width part not scale, correct?
Yes, so far I have only added support for the width flag. I'm thinking of how I
can get the scaled text in - I just need to read the spec some more to make sure
I understand how writing text over scaled text works.
|
Cool, feel free to ask for clarification, if needed. |
this is really impressive work, @kovidgoyal . good to see you continuing to break new ground in the terminal paradigm. my major questions were all answered by the "Wrapping and overwriting behavior" section of your spec. things there look sane. one question remains, which might have been answered: if i change global font size, does everything rescale based off of the new default? |
Hi all,
After several months of hard work refactoring kitty internals extensively, I am excited to announce kitty now has the ability to let terminal programs display text in multiple font sizes. This is implemented in a backwards compatible way, using a new escape code that terminal programs can opt-in to use if they wish too.
This feature works with the existing cell based grid terminal, we now just have the concept of "multicell" characters (these are an extension of the existing way "wide" characters and Emoji are handled) a single character can now extend over a block of cells spanning multiple lines.
A screenshot showing seamless mixed multi-sized text
For a quickstart, some simple commands you can use to display sized text:
To use these commands you must either use kitty nightly or build and run kitty from source.
For more details on how the escape code works and the various features see the text-sizing-protocol.rst file in the cell branch of kitty. If anyone knows of a conflicting use of OSC 66 please do let know, the number can be easily changed at this point.
This protocol also robustly solves the long standing problem in the terminal ecosystem of determining the width in cells of a string by allowing the client program to simply inform the terminal of how many cells to render any particular piece of text in.
Finally, as a bonus treat, since I was refactoring rendering anyway, I implemented underline gaps for descenders. What this means is that underlines are now rendered with gaps or holes where the text has a descender below the baseline. This is the famous? skip ink CSS feature implemented in browsers. It can be controlled by a new option in kitty.conf called underline_exclusion.
Since text-sizing required extensive changes to kitty internals, I would appreciate testing from some of you that are willing to test beta software. Note that it causes an approx 10% performance penalty in throughput, this is unavoidable because of the extra bookkeeping to deal with multiline characters. But, I think the ability to use different font sizes is worth the tradeoff.
I am also looking for feedback on the protocol design itself, please read the spec document and comment. I am particularly interested in feedback from people implementing terminal programs where such a feature may be useful, to that end I am pinging some such people I know of, please excuse the noise: @rockorager @neurocyte @dankamongmen @justinmk @swsnr @benjajaja @aymanbagabas @mfontanini
Anyone else is of course welcome to comment as well. I dont know how much interest this feature will generate, in case it generates a lot, please remember I am only one person and I have limited bandwidth. I will try to respond to any and all serious comments and I am willing to entertain reasonable changes/additions to how this feature works, but please remember everything has tradeoffs and I may not judge every tradeoff to be worth it.
The text was updated successfully, but these errors were encountered: