dankamongmen / notcurses Goto Github PK
View Code? Open in Web Editor NEWblingful character graphics/TUI library. definitely not curses.
Home Page: https://nick-black.com/dankwiki/index.php/Notcurses
License: Other
blingful character graphics/TUI library. definitely not curses.
Home Page: https://nick-black.com/dankwiki/index.php/Notcurses
License: Other
Once we can render images via #23, we can render video with a little bit more effort. Make it so.
I've already fixed this, but in the box demo, I had forgotten to move the cursor back up to the next box in. When I created a box from where the previous perimeter had left us, we segfaulted out. See if this can't be reproduced and locked down.
In the grid demo, if I enable an ncplane_erase()
at the start of each of the three sections, we crash. If I don't have them there, we do not see the crash, nor any valgrind complaints throughout.
With #1, we have a z-buffer and a first-order drawable plane. We ought be able to use this together with the idea of a default color to have transparent plane backgrounds. These cells ought show the next non-transparent layer's background, and if there is no such layer, show the underlying desktop.
In the case where an image is being rendered below, things get complicated, because there we're going to be using unicode fill characters to fake higher resolutions. With a foreground character, though, this can't be done. For a first-order solution, choose the most prominent color. A better solution would add dithering, methinks.
We probably ought generalize this to a full alpha channel, at least for backgrounds.
I built up the panelreel abstraction as part of outcurses last week. We now need it in notcurses. Once we have z-index support working (#1), move 'em over.
With notcurses, palette fades were trivial, since we had an actual palette. With directcolor, it'll be a bit more complex. First, I think we'll have to manually redraw and refresh the region. Secondly, we'd need modify the actual colors associated with each touched cell.
I think we might have an easy way out, though -- once we get alpha blending working, can a fade not just be a scaled alpha, independent of the actual colors? I would think so, except that alpha applies to both foreground and background, and we might only want to do one. Think about it.
Since speeding up our rendering in #41 , we lost a few characters off our last line.
On the intro screen of the demo, we don't lose anything until we print the welcome message. so maybe it depends on the total number of characters present somehow?
Now that we can create and destroy ncplanes, let's add the functionality necessary to move and resize them. For moving, it's fine to just accept a new origin point (perhaps relative to either the screen or some other ncplane). For resize, rather than ncurses's wresize()
, which takes new dimensions but applies them from the origin, i want to accept a new subsection of the ncplane, which will be left where it is (even if the resize is a growth, and it's now partially offscreen). move moves, resize resizes, and the resize is general.
actually, a fully general resize would require:
where the new section must be wholly within the new ncplane, but the ncplane can be beyond the screen. note that move is still distinct: in this sceme, the resize is "anchored" by the subsection that we keep.
if you want to resize or move, but don't care about keeping any of it, i guess it can apply to the origin. so to resize down and to the right by 2, currently at yoff/xoff with dimensions ylen/xlen, it would be
ncplane_resize(zero-area section, {0,0}, {ylen + 2, xlen + 2})
and to resize by 2 but put the current origin at the lower right, it would be:
ncplane_resize(zero-area section, {ylen + 2 - 1, xlen + 2 - 1}, {ylen + 2, xlen + 2})
and to resize by 2 to the up and left, it would be:
ncplane_resize(zero-area section, {2, 2}, {ylen + 2, xlen + 2}
in the first example, you get two new empty rows and two new empty columns, retaining all current data. in the second, you lose all data, and have a window with its lower-right corner where the upper-left corner was. in the third, you have two new empty rows and two new empty columns, retaining all current data, but they're above and to the left of you. yeah, i like that.
you still can't implement move() via resize(), which i like. resize() is fundamentally a data-rearranging operation within the ncplane, whereas move() is rearranging data at the notcurses level.
If the user indicates that they want it, lines in boxes ought be able to be colored along a gradient from one endpoint to the other. In order to do so, ncplane_box()
needs to instruct ncplane_hline()
etc. of the same. Perhaps hline/vline could accept two styles, and gradient between them? To avoid said gradient, just supply the same styles on either side....nah, probably make it explicit.
But this would be pimp!
If we want to do libpanelw-like stuff, the first thing we'll need is a good data structure for maintaining multiple virtual screens along a z axis. Each cell must be able to hold its own wchar_t
and attributes, including color of some depth.
ugh, when we get to a certain size in our egcpool, we're corrupting the hell out of it. see the lengthy details in #57. in widecolors-demo, we ought see output like this:
[00/00] [В]
[00/01] [о]
[00/02] [й]
[00/03] [н]
[00/04] [а]
[00/05] [и]
[00/06] [м]
[00/07] [и]
[00/08] [р]
[00/09] [Б]
[00/10] [р]
[00/11] [а́]
[00/12] [т]
[00/13] [ь]
[00/14] [я]
[00/15] [К]
[00/16] [а]
but instead we're seeing
[00/00] [Вλ]
[00/01] [оа]
[00/02] [йⱏ]
[00/03] [нн]
[00/04] [аⰸ]
[00/05] [ид]
[00/06] [мⰺ]
[00/07] [их]
[00/08] [рλ]
[00/09] [Бэ]
[00/10] [р]
[00/11] [а́ⰿ]
[00/12] [тд]
[00/13] [ьα]
despite extracting everything out all nice
In utf8_gce_len()
, we currently only take the first multibyte character. We want an entire EGC, and indeed we must properly account for this to get general layout working (see #25 ). Loop forward on wcwidth() == 0
in this function.
Man pages are one thing, but we don't even have an API summary. Copy most of the notcurses.h header into README.me and explicate upon it.
We're going to need a capability to open image files and turn them into RGB arrays.
We have declarations for both notcurses_newplane()
and ncplane_destroy()
, but they've not yet been implemented.
notcurses_render() is a fundamental API point which updates the physical screen to match the virtual screen (this has nothing to do with "alternative screens" as mentioned in #3). It needs be implemented atop the data structure defined in #1 (the cell plane).
For the first pass, blit the entirety of the virtual buffer to the physical screen, using a 1bpc changemask (don't replace character cells that haven't been touched; notcurses_clear()
and friends touch all cells). This will validate the basics of our framebuffer.
Next, take advantage of advanced terminal blitting capabilities (clear-to-eol, etc.) if they're available.
Next, ensure we're not emitting more escape sequences than are necessary. Avoiding two passes over the buffer will require keeping some metadata about how far foward a state applies.
Ideally, we can just use terminfo. xterm-direct
, for instance, advertises 16M colors (see dankamongmen/panelreels#41). Do some exploring and document what various terminal families are currently offering.
When we send UTF-8 outside the 7-bit clean ASCII single-byte baby encodings, we get the nastyglyph: �
We're PoCing with single-byte write(2)
calls, which obviously isn't our endgame. Does translation occur where exactly?
I'm having some weird problems with things not rendering until I give them some explicit styling. This seems to happen for ncplane_box()
, for instance. Without styling the individual cells, we don't see the output, even if the plane has a current color.
Line 14 in 0867e43
If data structures make cells index'able then please consider that many monospace characters take up multiple cells.
TEST(strwidth, testCjkWidesAndCombiningLowLines_withThompsonPikeEncoding) {
/*───────────────────────────────────────────────────┬─*/
EXPECT_EQ(20, strwidth(/**/ "𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(20, strwidth(/**/ "(╯°□°)╯𐄻︵ ̲┻̲━̲┻▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(20, strwidth(/**/ "ちゃぶ台返し▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(20, strclen(/*─*/ "𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(22, strclen(/*─*/ "(╯°□°)╯𐄻︵ ̲┻̲━̲┻▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(14, strclen(/*─*/ "ちゃぶ台返し▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(68, strlen(/*──*/ "𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(56, strlen(/*──*/ "(╯°□°)╯𐄻︵ ̲┻̲━̲┻▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(42, strlen(/*──*/ "ちゃぶ台返し▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(20, wcswidth(/**/ L"Table flip▒▒▒▒▒▒▒▒▒▒" /*│*/, -1));
EXPECT_EQ(20, wcswidth(/**/ L"(╯°□°)╯︵ ̲┻̲━̲┻▒▒▒▒▒▒▒" /*│*/, -1));
EXPECT_EQ(20, wcswidth(/**/ L"ちゃぶ台返し▒▒▒▒▒▒▒▒" /*│*/, -1));
EXPECT_EQ(20, wcslen(/*──*/ L"Table flip▒▒▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(22, wcslen(/*──*/ L"(╯°□°)╯︵ ̲┻̲━̲┻▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(14, wcslen(/*──*/ L"ちゃぶ台返し▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(20, strwidth16(/**/ u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(20, strwidth16(/**/ u"(╯°□°)╯𐄻︵ ̲┻̲━̲┻▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(20, strwidth16(/**/ u"ちゃぶ台返し▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(20, strclen16(/*─*/ u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(22, strclen16(/*─*/ u"(╯°□°)╯𐄻︵ ̲┻̲━̲┻▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(14, strclen16(/*─*/ u"ちゃぶ台返し▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(28, strlen16(/*──*/ u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(23, strlen16(/*──*/ u"(╯°□°)╯𐄻︵ ̲┻̲━̲┻▒▒▒▒▒▒" /*│*/));
EXPECT_EQ(14, strlen16(/*──*/ u"ちゃぶ台返し▒▒▒▒▒▒▒▒" /*│*/));
/*───────────────────────────────────────────────────┴─*/
}
There seems no fundamental reason to invalidate the standard plane on a resize, as we claim to do. Just use the existing ncplane structure, and realloc() the framebuffer. Easy enough.
In the unicodeblocks demo, I want to write to the 5th column (idx 4). Instead, I always end up writing to the far right. What the hell is up with that?
We'll need some way to return a composed keypress from stdin, one which ideally natively handles wide characters and e.g. keypad movement.
Whilst standing in line at the Chik-Fil-A, I realized that the way I've written ecgpool_stash
is broken for the case of a ASCII character followed by a EGC suffix. We check for an ASCII lead, and if one is there, immediately load the fast path of the cell
. We need look to the character beyond the header.
In the uniblock demo, I get a big nasty trail of black empty cells following the name line. This is presumably due to us clearing the remainder of the line (so as not to retain cruft from longer names), but it should be transparent, as I've not set a background.
When we run the modified widecolor demo, the summary text ought always be at (2, 2). This is indeed where it gets rendered in the notcurses equivalent. In our four pages, however, it keeps moving to the left. I assume this is due to prior characters occupying more than one column (though we ought be printing the same characters at the same locations each time through, no)?
Since merging #53 for #35 , we're failing in Drone:
[----------] 2 tests from LibavTest
[ RUN ] LibavTest.LoadImage
/drone/src/github.com/dankamongmen/notcurses/tests/libav.cpp:10: Failure
Expected: (nullptr) != (nc_), actual: (nullptr) vs NULL
[ FAILED ] LibavTest.LoadImage (0 ms)
[ RUN ] LibavTest.LoadVideo
/drone/src/github.com/dankamongmen/notcurses/tests/libav.cpp:10: Failure
Expected: (nullptr) != (nc_), actual: (nullptr) vs NULL
[ FAILED ] LibavTest.LoadVideo (0 ms)
[----------] 2 tests from LibavTest (0 ms total)
[----------] 16 tests from NcplaneTest
I wonder if we're perhaps missing codecs on there or something? Urk.
When I maximize my terminal (239x64 here at home) and render a screen which changes the color/style at every coordinate, we're entirely too slow. We still need to do some pretty basic damage-oriented optimizations in notcurses_render()
for sure, but that won't help in this kind of worst case. We simply need to take less time:
491 renders, 41.6s total (0.062s min, 2.4s max, 0.085s avg)
2.4s! Abominable and unacceptable. The first problem is gonna be that tputs(3)
from libtinfo wants to call back to us and have us emit a character at a time. We're currently doing that with a write()
:
static int
erpchar(int c){
if(write(STDOUT_FILENO, &c, 1) == 1){
return c;
}
return EOF;
}
so this already sucks because we don't get a user context, and are just hoping STDOUT_FILENO is good. we should probably use a thread-specific datum there. so tputs(3)
seems to rely on this behavior to insert delays etc., but it's fundamentally bad for us. We could stack this output up in a garbage buffer acquired via TSD (or just a global for now even), whether that was a POSIX memstream or whatever, but at that point we might as well skip tputs(3)
entirely....which I'd rather not do, if possible...
I've seen it claimed that a QR code can be emitted as Unicode box drawing characters, and be successfully scanned. Too cool. We obviously need such functionality.
Now that we're able to extract AVFrames from video and images, it's time to render them down. We're first looking for the quality level of e.g. mpv -vo tct
, but then we ought be able to use better drawing characters, better palette matching, and better dithering to improve on it.
If you look carefully at the output of the boxdemo, the first column holds a right side, and the top doesn't seem to hold anything. what's up?
With #26 and friends done, we need a good demo to show off ncplanes. Do a sliding puzzle, and sex it up a little.
We're writing a color escape for every cell on the screen in notcurses_render()
. We only need to write it if it's changed from the previous cell. This will reduce the bandwidth sent to the terminal substantially.
│ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ│
│ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ│
│ΔΔΔΔΔ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ █ ▇ ▆ ▅ ▄ ▃ ▂▁ ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ│
│ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ│
│ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ│
│ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ Die Welt ist alles, was der Fall ist. ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ│
│ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ│
│ΔΔΔΔΔΔΔΔΔ Wovon man nicht sprechen kann, darüber muss man schweigen. ΔΔΔΔΔΔΔ│
│ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ│
│ΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ│
what's this crap
I've added most of the necessary declarations to notcurses.h. We now need implementations.
right to left scripts fuck up our uniblock demo. i'd like to undo the damage, if possible.
damaged blocks include:
Neither the smcup
nor the rmcup
capabilities are advertised by the linux
terminfo type as of ncurses 6.1. We error out if either is not supported. I think we can just skip them in that case instead?
We need some unit testing to ensure we're reporting:
cell_load()
, BUTI'm pretty sure we're doing the first one right, or other things wouldn't work. Not sure about the second, nor how much we can trust e.g. wcwidth()
.
We currently skip wide characters in uniblock despite having resolved #6. We ought be able to display these, and just make the block a bit bigger. It's not critical right now, but it would be good to have done soon.
Especially for notcurses_render()
, we'll want to keep some timing information, and ideally run some benchmarks in our CI.
When I run notcurses-demo
, I'm seeing two problems:
The widecolors demo always manages to incompletely fill the screen. More precisely, I think, the widecolors demo fills up a bit more than the screen, scrolling its message back (if the amount moved back from [2,2] is equal to the amount unfilled, that's definitely our problem). This could very well come from failing to account for multicolumn Asian characters (see #6), so we would just need to pipe that down.
The "default colors" are achieved by using the op
terminfo capability, which on my terminal maps to 39 and 49:
39 Default foreground color implementation defined (according to standard)
49 Default background color implementation defined (according to standard)
so when the default bits are active (or rather the "not default" bits are not active), we need be using this. I don't seem to see a terminfo capability for doing these distinctly.
memu=\Em, oc=\E]104\007, op=\E[39;49m, rc=\E8, rev=\E[7m,
We ought be using -fvisibility=hidden so we only export symbols we want to. Right now we're making available things like egcpool_grow() etc.
Currently, our output doesn't release the resources held by a cell
they're blowing away. These need to be dropped from the egcpool.
Where supported, we ought make available the various WA_*
attributes (though probably not any marked color-incompatible by the no_color_video
terminfo property).
When printscreen is pushed, render the current virtual buffer to a file (the physical on-screen buffer can be grabbed any number of ways). Provide an option to disable this behavior, and probably also allow it to be remapped.
Our color interfaces are defined in terms of 3x8-bit channels, RGB. On many terminals, this won't fly. We need operate with at least the following:
To do this properly is the problem of color quantization. For a fixed image, there are well-known algorithms available. For colors thrown at us on the fly, we can keep the raw inputs, and requantize as the image changes. For a fixed palette, we can probably just do a fixed conversion; there's a known O(1) one, and several that trade greater complexity for better results.
That cursor is really fucking with me on the unicodeblock demo, and it slows down our drawing. Turn it off with the civis
capability.
By default, we ought register a signal handler for most fatal signals, and use it to restore the terminal. We should make this optional via a new member of notcurses_opts
. This handler ought remember the previously configured action, and invoke it.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.