OK, let's get started.
Building a minimal terminal UI library for language
Lua, but it's close platform.
So it probably would, if anyone wants to build something
like this, might be interesting to see what we ran into.
Small shout out to LabLua and G-Sup for helping
us out funding the project, which really helped us last summer.
In the set for a sake of time, let's skip on that.
I can show it demo of what it does today.
So this is Lua Rocks, which is the CLI interface for a package
manager, and we simply use it as an example.
Like, can we wrap it in something useful in a text-based UI?
So the first thing we did is we created some widgets on the command line.
So it's at the command line becomes interactive.
So it can actually walk around down execute commands and exit in this case.
The second example we did is full screen application.
Well, we have panels, we have a header bar, a keyboard shortcuts,
we can move around, we can hike panels, show panels.
So this is the current state of what it does.
In fact, the presentation.
So the goals, cross-platform, it had to work.
The idea is mainly from when you start working with Lua,
Lua's source distributed, which is hard on windows.
So can we have a novice user built something of just
works across the board?
Just a basic application to get people started.
Non-blocking input, Lua uses cover-teens.
So non-blocking input is something that we really wanted.
And no external dependencies to make installation easier.
Non-bills explicitly, no mouse support, no overlapping windows and this kind of stuff.
So what does it look like?
It's the operating system.
And there's two libraries.
The first one is Lua's system, which is only providing
C code with interfaces to the operating system.
And the second one is the one which has far more code,
that actually does all the drawing and everything else.
But the good thing is that you're already in the target language.
So you can write everything in the target language.
We wanted Lua's system to be independent of platform,
but because terminals are so fundamentally different
on both systems and physics and windows,
some details do leak through the terminal that Lua library.
The easy part.
To get this done, I'll see support.
Windows gained it in 2019.
That was like the big thing that made it viable,
otherwise it just wouldn't have worked in my opinion.
And getting a terminal size, this is a single API call,
it's an old platforms.
Everything else was a bit harder.
The biggest challenge by far was the ambiguous with,
display with, or display with in general.
Windows does not have for UGFA or Unicode characters.
Like emojis, which are two columns wide on the text screen.
Chinese, East Asian languages, you have a lot of
characters, two columns wide.
And for everything you draw on screen,
you just don't want stuff to mess up your,
display your construction, whatever you have.
On Windows was fairly easy to do the output,
but on Windows the hard part was like doing the input proper.
So to handle this, we had to implement,
use the by market school and implementation
that wasn't available on Windows.
And for the width checking, we had to
create functions for character and strings, which is,
like a very simple.
And we had to implement a string.sub, which is the
lower method for string mangling, cutting
and pieces and rearranging.
And we had to write two, one for UGFA characters,
because it was a bit based.
And the second one is the same thing,
but then based on display columns.
Because everything you do in your UI is basically based on the columns,
you display and not on bytes or individual characters.
This was the hard part.
On top of that, we built,
it goes input is one by by at a time from the keyboard.
And it meant that we had to rebuild a line editor.
We built this thing and it turned out this thing actually became like,
the core of all text handling for everything we displayed,
because it has these methods to check with columns everything.
And so this thing, we wrote as a line editor,
edges is all over the place in everything we do.
The next picture challenge was non-blocking input.
On post-6 it's fairly standard.
You said it's a non-blocking, you read one by at the time,
canonical mode and the whole shabang.
When I wind those, there just is no such thing.
So we fell back on the very old conno.h,
which is like, I think somewhere from the 80s,
but it's still available, it still works everywhere.
And they can read directly from the keyboard buffer.
Which means that on post-6, we can just read the single byte and on Windows,
we have to go through a bunch of hoops.
The main issue is that on Windows,
you read a white character, you want to output a single byte.
So we need to implement a buffer to store whatever the remainder is of the character that we're reading.
And there's a bit of a thing with Windows that
occasionally leaks, keyboard scan codes, raw codes,
through the interface, you want to ask me why.
Turn more scan, keep up, and you have to catch that.
But that's one general way to go up against, it's easy to fix,
but figuring out why the weird bytes are just coming in,
that's the hard part.
In the tellerization, this is a bit tricky on Windows,
over in post-6 actually on Windows, it was pretty easy.
But on post-6, it's the usual canonical echo and setting it to a non-blocking.
But what turned out on, especially on Macs,
it seems that if you set standard in to non-blocking,
you're also setting standard out to non-blocking,
and that's something you don't want.
And the reason is that you have a file descriptor,
but they can point to the same file description.
If you set it to non-blocking, you modify the file description,
and then the other one that points to the same description,
also becomes non-blocking.
So that's where we had another one of those,
what the heck is happening here.
Once you know, it's easy to fix, you make sure that each file descriptor
gets its own file description, and then they're independent,
then you can do whatever you want.
So if you have individual bytes for keyboard reading coming in,
then the first thing we do on the lowest level,
like the three functions you see here,
the top one is the C implementation that you showed.
The Riki is actually the one where we immediately
power this in a timeout and a sleep function.
The reason we do it is because if we change the underlying
implementation of the sleep function,
to something non-blocking, if you use a coroutine scheduler,
or whatever you have in the language that you're using
for a lure that is co-routines,
then it means that everything beyond it just becomes non-blocking.
And the last function is actually the one that does
all the extraction for parsing-answer codes and everything else.
To give you a quick idea of what it looks like.
So if you have a simple prompt in a terminal,
you can see the double with characters,
like the two Chinese characters of the ball with,
as is the rocket.
You can, well, you can copy paste some stuff, just works.
But if you place the sleep method that we have in the input,
then this is the second example, exact same code.
Other than we replace the sleep function with something
that deals with an algorithm in the top right and corner,
we have a clock ticking, and despite it,
I'm usually, if I would use the standard C functions from the C library
to read input, it would be blocking.
But in this case, we can do background work, timers, whatever.
So from there,
another thing to never ran into was spurring a slow initially,
we tried, when we want to, if you draw some kind of skin,
screen, you want to reset the state to what it was before you started drawing.
So you can create a terminal for foreground colors, background colors,
cursor position, but it turns out to be really slow.
The reason it's slow is that when you write a sequence to the terminal,
immediately read, the answer won't be there yet.
Terminal needs processing time, so you have to do a sleep,
and then you run into the underlying decision of the operating system,
and a typically is anywhere between two and 15 milliseconds.
So if you do it once on drawing a screen, not a problem.
Typically, you create widgets.
Every widget does the same thing like getting state,
we store it afterwards.
And if you do that, then it quickly adds up to 150 milliseconds,
and then you will start noticing it and it comes sluggish.
The way we work around this is actually by tracking state,
to create its stacks in which we push the state,
and then pop the state without actually querying the terminal.
And that makes it really snappy.
I use a number of items, the cursor shape, visibility,
scroll region, text attributes that we create the stacks for.
And they look pretty simple like this.
You push in the red foreground color, and it's at brightness,
everything else stays the same.
You write a text, does it?
Then you pop it, and then you restore it to the previous.
Whatever the color was, there was on the previous stack slot, basically.
This worked really nicely for all of those things
that we otherwise would have queried the terminal for.
API usage, if you look at everything in the end,
I was actually surprised that it was so little.
I would have expected to be more API calls for the underlying operating system
that we would need than the end it was fairly straightforward.
And then concluding, you look at the whole thing,
I found in the end after Google Summer Code Project,
the thing we achieved was actually way better than I expected,
considering how snappy it was that we had a keyboard,
a non-blocking handling, which has been a painful thing for a long time,
at least in the lower language.
But the main things here are the keyboard reading,
and to make it independent over the platforms,
and to display with that is really a painful thing to handle.
Like for every single string you handle,
you need to check with display columns, full summing.
That's it.
Questions?
Thank you.
Okay, the next speaker will come up.
And also when people come in, try to focus a bit more.
Any questions?
Yeah.
Sorry, can you say?
It's a client, a product client.
We could leave, basically it's an object,
and it has methods for key handling.
So you can inject FI modes if you want to.
And I think, frankly, I don't use FI, so I don't know the shortcuts,
but one of the entities that worked on it from the Google Summer Code,
actually used, not sure it was FI, or maybe another,
the competitor thing, but it should be possible.
And you're going to see?
Did you have any guidance?
Just a small amount of images?
And then you can see some of the colors.
No, no, maybe next year Google Summer Code.
I mean, it was mostly considering the platform in the penance.
That was like the media thing to achieve.
So we can have another build something that just works across the board.
Something useful.
That was the idea.
And then the images, and etc, nice to have, but in a later stage.
Yeah, that is the typical thing.
Anything else?
So, thank you very much.