#libvaxis: a TUI library for zig

1 messages · Page 3 of 1

obsidian rover
#

When spamming it, the terminal is mid-event in it's write

#

So it sends a bunch and the last one is \x1bO

#

And the next read will be B\x1bOB....

old skiff
wooden lantern
#

libvaxis doesn't currently support the style used in the post does it?

obsidian rover
#

It's complicated to do well

wooden lantern
#

ahhh, I see. thanks for pointing it out though :)

obsidian rover
old skiff
#

ok so build it

#

then uh

#

i guess you could just run any old python file

#

do zig-out/osmium examples/calculator.py --debug

obsidian rover
#

hmm

#

it doesn't build

old skiff
#

:(

obsidian rover
#

what zig version?

old skiff
#

what's the issue?

#

13

obsidian rover
#
error: FileNotFound
/home/tim/.zig/versions/zig-linux-x86_64-0.13.0/lib/std/process/Child.zig:423:5: 0x1196f67 in waitPosix (build)
    return self.term.?;
    ^
/home/tim/.zig/versions/zig-linux-x86_64-0.13.0/lib/std/process/Child.zig:300:9: 0x1196fe3 in wait (build)
        try self.waitPosix();
        ^
/home/tim/.zig/versions/zig-linux-x86_64-0.13.0/lib/std/process/Child.zig:400:17: 0x1138b5f in run (build)
        .term = try child.wait(),
                ^
/home/tim/repos/osmium/tests/matrix.zig:30:20: 0x11387ed in addCase (build)
    const result = try std.process.Child.run(.{
                   ^
/home/tim/repos/osmium/tests/matrix.zig:21:26: 0x1139310 in addCases (build)
        const test_run = try addCase(b, test_file, exe);
                         ^
/home/tim/repos/osmium/tests/cases.zig:19:30: 0x1139878 in addCases (build)
        parent_step.dependOn(try matrix.addCases(b, dir, exe));
                             ^
/home/tim/repos/osmium/build.zig:97:5: 0x113a5e0 in build (build)
    try cases.addCases(b, exe, test_step);
    ^
/home/tim/.zig/versions/zig-linux-x86_64-0.13.0/lib/std/Build.zig:2117:24: 0x111caa7 in runBuild__anon_8821 (build)
        .ErrorUnion => try build_zig.build(b),
                       ^
/home/tim/.zig/versions/zig-linux-x86_64-0.13.0/lib/compiler/build_runner.zig:301:9: 0x1117dcf in main (build)
        try builder.runBuild(root);
        ^
error: the following build command failed with exit code 1:
/home/tim/repos/osmium/.zig-cache/o/ed52e6c312b90fa99360cb68c1cf0811/build /home/tim/.zig/versions/zig-linux-x86_64-0.13.0/zig /home/tim/repos/osmium /home/tim/repos/osmium/.zig-cache /home/tim/.cache/zig --seed 0xeda4b3bb -Zed9a954b4dc94e10
old skiff
#

uh, well what's further up the stack trace?

#

ah well, you're running the tests

#

they expect you to have a python3.10 binary in your PATH

#

i probably should make it use the python it builds instead, but i can do that later

#

just build with zig build osmium

obsidian rover
#

Ah, I was just doing zig build

#

Huh

#

Still the same error

old skiff
#

actually i dont have a step for osmium

#

lol

#

interesting

#

would you just comment out that try cases.addCases line?

obsidian rover
#

Ok it's building now!

old skiff
#

nice

obsidian rover
#

I assume this is on ghostty?

old skiff
#

nope

#

it's rio

#

lol

obsidian rover
#

ok

old skiff
#

hence me being 70% sure it's the terminal's fault

obsidian rover
#

I was wondering why it was getting SS3s for arrows, I know ghostty does the CSI variant by default

#

Both are valid

old skiff
#

does mouse wheel down just translate to arrow down?

obsidian rover
#

Sometimes, yes

#

There is a mode that when in alternate screen, mouse wheel sends arrow keys

#

vaxis doesn't enable this - so rio must have it on by default, or maybe only on if mouse reports aren't turned on

#

Ok I am in the tui and I can press arrows right away and it goes between the two

#

I can't trigger the panic

#

I will install rio

old skiff
#

you tried scrolling really hard?

obsidian rover
#

Yeah

#

I mean as hard as I can

#

Huh

#

rio ...doesn't like my input

old skiff
#

wdym?

#

lol

obsidian rover
#

I type and nothing happens

old skiff
#

i can confirm that the panic only happens in rio

#

so i must assume it's a rio skill issue

#

i'll just scroll really gently

obsidian rover
#

I am still going to add this if statement for a partial event

#

I didn't allow for partial events when I first wrote the parser

#

I just am not sure how you ended upa t the else arm

old skiff
#

cuz it doesn't handle the 'O' char

#

why is 'O' used here for scrolling any idea?

obsidian rover
#

\x1b O O is "/"

#

I mean, unless rio has opted for non-standard encodings

#

do you also have kitty installed?

old skiff
#

kitty doesn't run on windows

obsidian rover
#

kitten show-key would be interesting to see what you generate from your mouse

#

Ohh this is windows?

old skiff
#

mhm

#

well

#

it's a windows terminal that is sshed into a linux machine

#

i'm 99% sure that ssh isn't the issue

obsidian rover
#

ok in your shell there, type cat -v and press arrows and your mouse wheel

#

on the linux machine i mean

old skiff
#

left arrow then right arrow

#

mouse wheel doesn't do anything

obsidian rover
#

Ok, these are what ghostty sends as well

#

ok now try printf "\x1b[?1049h" && cat -v

#

Your mouse will probably send stuff there

old skiff
#

this is scrolling down

obsidian rover
#

ok

#

That all seems correct

#

Oh wait

#

I think this is a rio bug

old skiff
#

me as well :P

obsidian rover
#

Ok so you set it to return 0 bytes and you get an unhandled 1b

old skiff
#

that is O

#

yes

obsidian rover
#

1b is escape

old skiff
#

ah

obsidian rover
#

I would guess rio is writing all their events, and the last one fails to write completely - so they rewrite it at the beginning of the next syscall

#

So the sequential writes come in as \x1bOB\x1O....next write \x1bOB\x1bOB...

old skiff
#

ah

#

that makes sense

obsidian rover
#

they didn't writeAll 😉

old skiff
#

time to go dig through rio

#

im pretty sure it's in rust

obsidian rover
#

gl! I know zero rust

#

Yeah, i had to install rust to build it 🙂

#

man that will throw off my parser state really bad

old skiff
#

at least it isn't your bug :P

obsidian rover
#

That's why you are getting hte OB spammed

#

Because it eats \x1bO\x1b and the rest never gets parsed as an escape sequence

#

Becaaause it's now off by one

#

that is my bug 🙂

#

Need to recover from that nicely

old skiff
#

returning the N = length is the best way :P

#

just skip over it

#

that would probably mess up some further stuff tho

obsidian rover
#

That works in this case with rio, but presumably anyone could send an \x1bO\x1b sequence at anytime

#

The standard state machine handles this fine

#

I just made some shortcuts for input parsing

old skiff
#

ah

obsidian rover
#

And because - until now, i have never seen a borked escape sequence like this

old skiff
#

i am sort of an expert at breaking things

#

im about to suffocate in my room, brb while it builds :D

#

this cali summer is no joke

obsidian rover
#

Just pushed the fix

#
    const key: Key = switch (input[2]) {
        0x1B => return .{
            .event = null,
            .n = input.len,
        },
#

I also guard for the len < 3, but by this point we've already check that 0 and 1 are what we want, but if 2 is an escape, we have an invalid event - consume the bytes and move along

#

Ah...it should be input.len -1

#

Well, it should be 2 🙂

old skiff
#

it must be 2

#

because it would have paniced on the earlier index access

obsidian rover
#

Yeah

#

Ok it's fixed

#

You should be panic free

old skiff
#

yep

#

and the warning is gone of course

#

currently installing vs 2017 so i can build rio

#

gonna try tracking down this rio bug

obsidian rover
#

There is a chance this is not a bug on their end

#

I mean, it's kind of a bug but they could be delivering all of the correct events already

#

It's only a bug if the partial write results in a mismatch of the number of events sent

#

SO don't kill yourself on it 😉

old skiff
#

i will take that as an excuse to not hunt for it, and get back to designing this bad tui :P

#

this BOBOBO thing is kinda annoying tho

obsidian rover
#

It shouldn’t still be spamming that?

old skiff
#

tis is

obsidian rover
#

(╯°□°)╯︵ ┻━┻

old skiff
#

no panics tho!

obsidian rover
#

Alright I will take a look this week at making the parser more robust

old skiff
#

aside and possibly silly question. i'm printing this line to seperate cli commands but i dont know how to correctly calculate how long it should be

#

right now i have:

try writer.writeBytesNTimes("⎯", state.text_width);
#

it's "" because that character is a utf8 and doesnt fit in ascii

#

text_width is just the width of the box

#

but it's a bit too long right now

obsidian rover
#

This is a child window?

#

It should be win.width

old skiff
obsidian rover
#

The child window has it's width set when you create it

old skiff
#

yeah

#

and i use that width after creating it

obsidian rover
#

you are writing direct to the tty?

old skiff
#

no

obsidian rover
#

Or is that in the scrollview?

old skiff
#

so writer is an ArrayList(u8) which buffers the input

#

then i create a Segment whos text is the items of that array list

obsidian rover
#

Ah ok

old skiff
#

all the logic there is fine i think, i just dont know how to correctly calculate the width of the character

#

it seems to be a bit larger than a cell

obsidian rover
#

win.gwidth

old skiff
#

maybe i just chose a stupid character

old skiff
#

uno momento

obsidian rover
#

Something seems off though

#

It should be one cell

#

Oh maybe not

#

yeah that is a weird one

old skiff
#

what character do you use for the borders?

#

i'll use that

obsidian rover
#

hopefully measuring with gwidth will fix it, otherwise use a box drawing character:

old skiff
#

using the box drawing character worked perfectly

obsidian rover
#

A lot of terminals will custom draw these to have no gaps (ghostty, kitty, foot, alacritty, etc)

old skiff
#

it is a bit thick though

obsidian rover
#

There is a thinner variety

#

"─"

feral charm
#

how has no one been able to make a decent-looking alternative to the kernel console

old skiff
#

why would you

obsidian rover
#

That hting has to work everywhere and be tiny!

#

and have an embedded font that is tiny as well

feral charm
#

tru

#

that's complicated

old skiff
#

well using gwidth isn't really useful because it obviously can't return a fractional width or something

#

probably rio is having issues with rendering

#

using is perfect

old skiff
#

rio crash #1 😄

fleet schooner
#

@obsidian rover The unicode on windows fix is not quite as simple as I had hoped. It seems we need to decode UTF-16 first before we can encode to UTF-8.

#

which makes sense

fleet schooner
#

@obsidian rover it looks like bracketed paste is supported in conhost. Does this mean we can enable it somehow in libvaxis on windows?

obsidian rover
#

We just blindly issue the enabling code

fleet schooner
#

ah ok. I will test it again. I thought it was not working.

fleet schooner
#

nope, it doesn't seem to work

#

It looks to me like windows\Tty.zig eats all escape sequences and never actually sends them to parser.parse anywhere. So I don't see how bracketed paste can work.

obsidian rover
#

Does it allow bracketed paste mode if there is no vt processing?

fleet schooner
#

It might still be a good idea to actually parse escape sequences into events even on windows. Do you want me to PR my changes?

obsidian rover
#

So there's likely some case somewhere that happens on the output side

fleet schooner
#

Yes, I've definitely seen a few escape sequences arriving. That's why I assumed there would be bracketed paste sequences too.

wooden lantern
#

@obsidian rover any idea why when in -Doptimize=ReleaseSafe, debug stuff is still printed to the term?

#

is there a way to stop this without redirecting output

obsidian rover
#

(Which is how I would stop it: custom logFn and filter the scopes you don’t want)

wooden lantern
#

sounds good, thanks for that man

#

any updates on the github issue I put up btw? It's all good if you haven't had the time, i appreciate your work :) just curious

old skiff
#

are you passing in the optimization mode to the libvaxis dependency

obsidian rover
wooden lantern
wooden lantern
#

same thing lol

#
fn build_targets(b: *std.Build, build_options_module: *std.Build.Module) !void {
    const targets: []const std.Target.Query = &.{
        .{ .cpu_arch = .aarch64, .os_tag = .linux },
        .{ .cpu_arch = .x86_64, .os_tag = .linux },
    };
    const optimize = .ReleaseSafe;

    for (targets) |t| {
        const target = b.resolveTargetQuery(t);
        const libvaxis = b.dependency("vaxis", .{ .target = target, .optimize = optimize }).module("vaxis");
        const fuzzig = b.dependency("fuzzig", .{ .target = target, .optimize = optimize }).module("fuzzig");

        const exe = b.addExecutable(.{
            .name = "sftm",
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        });
        exe.root_module.addImport("fuzzig", fuzzig);
        exe.root_module.addImport("vaxis", libvaxis);
        exe.root_module.addImport("options", build_options_module);
        b.installArtifact(exe);

        const target_output = b.addInstallArtifact(exe, .{
            .dest_dir = .{
                .override = .{
                    .custom = try t.zigTriple(b.allocator),
                },
            },
        });

        b.getInstallStep().dependOn(&target_output.step);
    }
}
still hare
#

fyi you don't need custom logfn to filter log scopes, std_options has log_scope_levels

#
pub const std_options: std.Options = .{
  .log_scope_levels = &.{
    .{
      .scope = .terminal,
      .level = .err,
    },
  },
};
#

i'd prefix the scopes in vaxis with vaxis_ though, it's good idea for libraries to do that imo

obsidian rover
still hare
#

yeah you have to add all of them to the array

#

you can do it reverse too though, have default scope level be err and only list your own scopes to override

obsidian rover
#

I was thinking of dropping them all under a single .vaxis scope to make it easy. I'm not sure more granularity than that is needed

#

But prefixing would be a good idea too - we'll see where it ends up!

still hare
#

yeah as long as the scope names avoid name collisions was the point

obsidian rover
still hare
#

@tagName(scope) probably :P

wooden lantern
#

Hey @obsidian rover, is there planned mac support for pty?

obsidian rover
stone tendon
#

any ideas on why the terminal would glitched and a weird offset after quitting ?

obsidian rover
stone tendon
obsidian rover
stone tendon
stone tendon
#

when handling events, if the event won't change anything visually, is it a good idea to just continue instead of rerendering everything ?

obsidian rover
stone tendon
#

Im using the box drawing characters you ve mentioned before

#

from the wiki

obsidian rover
#

The gap where they meet? If you know the exact coordinate then I would draw the lines and then at that cell draw the connecting glyph

stone tendon
#

the way im drawing them is the following

obsidian rover
#

stone tendon
#

header:

pub fn header_component(parent: vaxis.Window, opts: ?OffsetOptions) !vaxis.Window {
    const opts_or_default = opts orelse OffsetOptions{};
    const header_child = parent.child(.{
        .x_off = opts_or_default.x_off,
        .y_off = opts_or_default.y_off,
        .height = .{ .limit = parent.height * 3 / 100 },
        .width = .{ .limit = parent.width },
    });

    const segment: vaxis.Segment = .{
        .text = header,
    };
    _ = try header_child.printSegment(segment, .{
        .col_offset = header_child.width / 2 - header.len / 2,
        .row_offset = header_child.height * 20 / 100,
    });

    const h_cell: vaxis.Cell = .{
        .char = .{ .grapheme = "━" },
    };
    for (0..header_child.width) |i| {
        header_child.writeCell(i, header_child.height - 1, h_cell);
    }

    return header_child;
}
#

sidebar:

pub fn sidebar_component(parent: vaxis.Window, opts: ?OffsetOptions) !vaxis.Window {
    const opts_or_default = opts orelse OffsetOptions{};
    const sidebar_child = parent.child(.{
        .x_off = opts_or_default.x_off,
        .y_off = opts_or_default.y_off,
        .height = .{ .limit = parent.height - opts_or_default.y_off },
        .width = .{ .limit = parent.width * 30 / 100 },
    });

    const border_char: vaxis.Cell = .{
        .char = .{ .grapheme = "┃" },
    };
    for (0..sidebar_child.height) |i| {
        sidebar_child.writeCell(sidebar_child.width - 1, i, border_char);
    }

    return sidebar_child;
}

but for the sidebar i pass y_off eql to the header_component.height

stone tendon
stone tendon
# obsidian rover

worked perfectly

  win.writeCell(sidebar_component.width - 1, header_component.height - 1, intersection);
#

thanks !

stone tendon
#

is there a way to get the []u8 inside the TextInput widget without clearing the inner slice ?

old skiff
#

just input.buf.items?

obsidian rover
stone tendon
#

but it mentions that the pointers might get invalidated by various functions of the GapBuffer still fine to use ?

stone tendon
#

basically this is what i am trying to do, the input takes a search term, and it would show the result as the files that contains the term (col, line etc) i don t see the need to allocate each time i ll send the search term when clicking enter

#

don t mind the result just for debugging purposes 😂

obsidian rover
#

I almost wonder if this shouldn't change into a TextArea widget (implying it is for large text editing, and give it proper wrapping etc) and make a simpler TextInput widget meant for small inputs

#

That way it's easy to access the full input value without an extra buffer or allocator

stone tendon
#

OHH now i get what you mean nvm

#

I ll try that

obsidian rover
#

And a simple ArrayList can make it easier to use

stone tendon
#

btw any reason on why the offset in the segment is only in the first line ? or should i split them by new line and make a segment for each is that how the Segment is supposed to be used ?

obsidian rover
#

For right now though, you could get the length from input.buf.realLength, then allocate a buffer and memcpy input.buf.items into it, then memcpy input.buf.secondHalf() into it

stone tendon
obsidian rover
#

Imagine a paragraph with a single bold word in the middle

#

It would need three Segments

#

If you have "different" chunks of text (bulletted lists, etc), I would print those with separate function calls

stone tendon
#

Ohh now i understand how Segments work, it s even shown in one of the examples as well i think in table example i should ve just payed attention

#

thanks !

obsidian rover
#

Think of a segment as a singular run of constant styled text within a larger body of text

stone tendon
#

something similar to this

#

search and replace on multiple files

obsidian rover
#

Oh cool

stone tendon
#

seems to be fun, and so far libvaxis seems very intuitive, specially how you can handle both vaxis events and your own custom events in the same event_loop

stone tendon
#

any tips and tricks on how to debug ? the only way im debugging with so far is just start the process attach lldb to it and breakpoints

obsidian rover
stone tendon
ember robin
stone tendon
stone tendon
#

when rendering a window that has some segments/cells written, is there anything specific that has to be done in case you want that window to be empty again ? like saving a ref of that window and call clear or something ?

obsidian rover
stone tendon
#

the next !

#
    pub fn add_result_view_component(
        result_view: *ResultView,
        parent: vaxis.Window,
        result: []const u8,
        opts: vaxis.Window.ChildOptions,
        allocator: std.mem.Allocator,
    ) !vaxis.Window {
        const child = parent.child(opts);
        log.debug("result: {s}, len: {d}\n", .{ result, result.len });
        if (result.len == 0) return child;

        var lines = std.mem.splitScalar(u8, result, '\n');

        var row_offset: usize = 1;
        var line_number: usize = 1;

        while (lines.next()) |line| {
            defer line_number += 1;

            const line_number_str = try std.fmt.allocPrint(allocator, "{d}: ", .{line_number});
            try result_view.line_numbers.append(allocator, line_number_str);

            print_line_number(&child, line_number_str, row_offset);

            row_offset += try print_line(
                &child,
                line,
                row_offset,
            );
        }

        return child;
    }
#

even though i am sure i am not printing those line numbers in when there is no result

obsidian rover
#

I usually clear every cycle and draw everything fresh

#

Clear the root, redraw everything

stone tendon
stone tendon
obsidian rover
#

It seems like there might be a desync happening somehow…like those characters are leftover due to an incorrect width calculation

#

What terminal, and are there possibly control bytes in what’s being printed?

stone tendon
#

as it shouldn't go through that loop in the first place to print the line_numbers

#

you can see in the logs here

#

last render of result_view is called with a result that has len: 0

#

tried in iterm2 same thing, it will be just something dumb i have done, i ll keep you updated in case it is not !

obsidian rover
#

Hmm, yes something odd is happening!

inner otter
#

I'm currently rendering in the event loop, but sometimes I just want to signal a re-render from other sources without an input coming in. What is the best way to do that? I guess I need to separate the rendering and event loop? Is there a way to simply add a timer event as well?

obsidian rover
#

Ie a redraw event with no payload that gets posted from some thread on a timer

inner otter
#

Ah I just add it to my own event union and somehow add it to the event loop?

obsidian rover
#

Yep

#

loop.postEvent

inner otter
#

ah and that is thread safe?

obsidian rover
inner otter
#

neat

fleet schooner
#

@obsidian rover How do I get colored undercurls? I set cell.style.ul to an rgb color and cell.style.ul_style to .curly, but nothing happens. The undercurls are always in the cell.style.fg color. I tried it on a bunch of terminals

obsidian rover
#

woops

#

Just pushed the fix for that, didn't test though but that has to be it

fleet schooner
#

Doesn't seem to make any difference. Does ul apply to any understyling? Or just underline?

fleet schooner
#

@obsidian rover I pushed it to flow anyway. Even though it doesn't seem to have an effect. So if you want something to test style.ul you can use flow.

obsidian rover
#

It should apply to any understyle

feral charm
#

Is there a reason in particular for why you chose the MIT license for this project?

obsidian rover
feral charm
obsidian rover
feral charm
#

I'm just intrigued by why people use it so much, even in projects where community engagement is supposed to prime over proprietary adoption.

#

It doesn't make sense to me that people just use it as a go-to license even when the project is community-oriented

obsidian rover
#

I do this for fun - the more people that use the library the more fun it is for me

feral charm
#

Do you care mostly about individuals or companies using it? :v

obsidian rover
#

Makes no difference to me

#

TBH if tigerbeetle made some TUI app with this I would be honored

feral charm
#

I mean I sort of get that

#

But don't you think that if you provide a free, open-source work to someone, they should give you the favor back?

obsidian rover
#

For example - This is an investment in the terminal ecosystem. If someone can make a business off it somehow I'll still be pretty happy because I like to see the ecosystem move in a certain direction

stone tendon
stone tendon
#

is there a way to higlight a part of a word or the whole line like on the sidebar ?

obsidian rover
#

One way is to handle it via Segments:

window.print(&.{
  .{.text = "re"},
  .{.text = "main",
  .style = .{.reverse = true}},
  .{.text = "ing"},
});
#

You can do reverse, or set a bg color

#

Another is to do something like

var cell = window.readCell(row, col);
cell.style.bg = true;
window.writeCell(row, col, cell);
stone tendon
obsidian rover
#

Yeah

fleet schooner
obsidian rover
#

I tried both rgb and indexed underline colors and they both worked

fleet schooner
#

I figured it out. I was resetting the whole style on the next line. clueless

obsidian rover
#

Still uncovered a vaxis bug though!

stone tendon
fleet schooner
stone tendon
stone tendon
#

im still facing the same issue of back then where i am having some already rendered cells not cleared after calling win.clear

#

allocated line numbers as a str

--------------------
len: 11
4: 
7: 
8: 
10: 
18: 
20: 
21: 
22: 
28: 
29: 
32: 
--------------------
len: 6
4: 
93: 
155: 
160: 
163: 
201: 
stone tendon
#

changing it from using a Cell to a Segment fixed it realized that i was using Cell in the wrong way i was passing []const u8 that was represented in more than one col which i guess Cell has to represent only one column ? which is the reason why it clears only one column in the screenshots above

obsidian rover
inner otter
#

What are good examples of how to use widgets?

#

Ah I found your comlink repo, that looks good 👍

obsidian rover
#

I was going to say that the examples directory has examples for each widget

#

But comlink uses a few as well

inner otter
#

I was trying to understand TextView, or basically any scrolling text widget with buffer. Didn't see an example for that sadly

obsidian rover
#

Check out @still hare ‘s spurdo editor

inner otter
obsidian rover
inner otter
#

Ah nevermind, I didn't update the submodules

#

Nice works well!

dense turtle
#

this looks great! i'm going to try it out for my next project :)

dense turtle
#

i'm loosely following the text input example, and i'm wondering what i'm doing wrong ... when i add this bottom line with the border, the program crashes with an integer overflow

dense turtle
#

after some poking around, i found out the reason for the crash: for some reason the parent window (which i got from doing vx.window()) has a width and a height of 0

#

i guess it seems like Screen never gets initialized with width and height values other than 0 ?

#

idk ;w;

obsidian rover
#

When you receive a winsize event you have to pass it in to vx.resize. The resize function is what sets the screen size

#

It’s handled that way to prevent a lock on the screen which would be pretty expensive with how often it would need locking

dense turtle
#

oh oops, i forgot to add the winsize event to my loop

#

thats probably the issue, i test later

dense turtle
#

yep that was the issue, i stupid

#

thank you

stone tendon
#

is it normal for the program to crash if you don t call win.clear on each event iteration ? (win coming from vx.window() where vx is instance of Vaxis), want to see if there is any way to rerender only a child instead of the whole window

obsidian rover
serene forge
#

I think zig is not yet registering with the wider cli community but I think it 100% should be

obsidian rover
stone tendon
obsidian rover
stone tendon
#

but it s possible right ?

obsidian rover
#

Yeah - it should work just about the same

#

Do you have some code I could take a look at?

stone tendon
#

example on when the window have to resize all components should rerender

#
                .winsize => |ws| {
                    try app.vx.resize(app.allocator, app.tty.anyWriter(), ws);
                    win = app.vx.window();
                    win.clear();
                    header_view_component.update_win_component(&win, .{
                        .width = .{ .limit = win.width },
                        .height = .{ .limit = win.height * 3 / 100 },
                    });
                    sidebar_view_component.update_win_component(&win, .{
                        .width = .{ .limit = @min(50, win.width * 30 / 100) },
                        .height = .{ .limit = win.height - header_view.win.height },
                    });
                    search_input_view_component.update_win_component(&sidebar_view.win, .{
                        .width = .{ .limit = sidebar_view.win.width * 98 / 100 },
                        .height = .{ .limit = 3 },
                    });
                    result_view_component.update_win_component(&win, .{
                        .x_off = sidebar_view.win.width + 1,
                        .y_off = header_view.win.height,
                    });
                    selected_path_view_component.update_win_component(&win, .{
                        .y_off = 9,
                    });
                    // queue renders
                    try app.render_queue.writeItem(&header_view_component);
                    try app.render_queue.writeItem(&sidebar_view_component);
                    try app.render_queue.writeItem(&search_input_view_component);
                    try app.render_queue.writeItem(&result_view_component);
                    try app.render_queue.writeItem(&selected_path_view_component);
                    win.writeCell(sidebar_view.win.width - 1, header_view.win.height - 1, intersection);
                },```
#

and at the end of each iteration i rerender the components that need to rerender

#
...
            while (app.render_queue.readItem()) |component| {
                try component.render_component();
            }

            // Render the screen
            try app.vx.render(app.tty.anyWriter());
#

Component type:

pub const Component = struct {
    ptr: *anyopaque,

    render: *const fn (ptr: *anyopaque) anyerror!void,
    update_win: *const fn (ptr: *anyopaque, parent: *const vaxis.Window, opts: vaxis.Window.ChildOptions) void,

    pub fn render_component(component: *Component) !void {
        return component.render(component.ptr);
    }

    pub fn update_win_component(component: *Component, parent: *const vaxis.Window, opts: vaxis.Window.ChildOptions) void {
        return component.update_win(component.ptr, parent, opts);
    }
};```
#

update_win_component is ran when the window is resized

obsidian rover
#
                .winsize => |ws| {
                    try app.vx.resize(app.allocator, app.tty.anyWriter(), ws);
                    win = app.vx.window();
                    win.clear();
                    header_view_component.update_win_component(&win, .{
                        .width = .{ .limit = win.width },
                        .height = .{ .limit = win.height * 3 / 100 },
                    });

Does the component retain the reference to the window?

#

The window goes out of scope after .winsize block

stone tendon
#

oh yeah that s true is the reason why it segfautls, but the thing is, win width and height is set to 0 unless you get a new instance of Window from vx.window after calling vx.resize

obsidian rover
#

The windows hold a reference to the underlying screen, which is replaced when you call resize

#
pub fn resize(
    self: *Vaxis,
    alloc: std.mem.Allocator,
    tty: AnyWriter,
    winsize: Winsize,
) !void {
    log.debug("resizing screen: width={d} height={d}", .{ winsize.cols, winsize.rows });
    self.screen.deinit(alloc);
    self.screen = try Screen.init(alloc, winsize, &self.unicode);
#

So after resize, any previous window is no longer valid

#

You could reimplement Window in your application though, and instead of it holding a reference to *Screen, it could hold a reference to *Vaxis and then access vaxis.screen and always have an available screen

#

Window is really just a helper to make it easier to limit regions of printing to the Screen - nothing about it is internal to Vaxis. You could just copy the file and remove the stuff you aren't using

stone tendon
#

i ll keep you updated !

proud blade
obsidian rover
proud blade
#

Although my current code works, it’s getting messy to add new features. I’ve been following vaxis from the day you first shared it and I started rewriting the UI of zf last week to use vaxis. It’s probably going to be a while before it’s done because I don’t have much free time these days

#

But I should say, thanks for libvaxis! It’s been really nice to use so far

obsidian rover
still hare
#

the gap_buffer in build.zig.zon probably should be changed to point to a commit rather than the main branch

obsidian rover
obsidian rover
#

Sounds like Kovid is on board for implementing in Kitty

obsidian rover
#

Kitty has implemented!

proud blade
#

That was fast

obsidian rover
#

@proud blade When you are working on your zf rewrite, it would be cool if zf could be made easily into a widget. Basically have a couple easy ways to pass it events and draw to a window. Nearly everything I make has some usecase for a fuzzy finder

proud blade
#

Would that be weird for vaxis and zf to depend on each other? thinkies

obsidian rover
#

There are already a few applications using both libvaxis and zf

proud blade
#

That makes a lot more sense! I'll get the rewrite finished and then see about making it a widget

proud blade
obsidian rover
proud blade
#

Thanks!

#

I actually do have a question on the zf vaxis rewrite. I don't want to use the alternate screen and instead draw the UI under the terminal prompt (like zf currently does). I'm not sure how I would best approach this though...

I'm just not finding a way to determine where the cursor is at the beginning so I can do all of my printing relative to that. Any thoughts on this?

obsidian rover
#

It's safe to call clear, but the screen will scroll to the furthest row printed that contains a non-default cell

#

My guess is you'll grab a vx.window(), clear it,, then create a child the exact size you want to limit all printing to ... win.child(.{.height = .{.limit = <h>}}). Print anything to this, then render

proud blade
#

Right now the relative printing works until I resize the terminal... then everything becomes relative to the top left of the terminal window, will a child window fix that?
Hmm it didn't...

obsidian rover
#

That's a good question

#

Currently on resize, we go to column 0 ('\r') then RI up the number of rows from where our cursor ended

#

This might conflict with what the shell prompt does

proud blade
#

Hmm I just tried in bash and zsh where I don't have any fancy prompt stuff setup and I still see the issue where resizing changes the relative drawing origin

obsidian rover
proud blade
#

🙏 thank you. Lmk if I can help with anything

obsidian rover
#

I won't be able to fix today, but I think it shouldn't be too bad. I need to add some linefeeds to prevent reflow from messing everything up too much and then change some logic in vx.resize

proud blade
#

No rush, I've got plenty of other projects to keep myself busy in the meantime

obsidian rover
obsidian rover
#

I forgot to include a fix for @proud blade's resize-on-the-primary-screen issue. I've (mostly*) fixed that and tagged 0.4.1!

#

@proud blade It should work much better now. there is still some issues if you have a border. I can fix this by disabling autowrap in the terminal, but I'm not sure yet if I want to do that because it's pretty unexpected - since libvaxis requires library users to have a tty they can write to anyways people could do this on their own if it is absolutely needed

#

But I did a little demo of what i imagine zf will look like and the resize worked great now

proud blade
#

@obsidian rover thank you! I just tested really quick and it seems like everything works

fleet schooner
#

@obsidian rover is it possible to use the same zg as libvaxis? without adding it to my zon file

obsidian rover
#

Not currently - I will rexport it though

fleet schooner
#

is there perhaps a build system way to access sub dependencies of a dependency?

obsidian rover
#

Since zg is broken up into modules, I will only export the modules vaxis loads (grapheme, code_point, and DisplayWidth)

#

Hmm

#

I think another way is that I can export the module from build.zig

fleet schooner
#

I guess I could just read the import_table field of the vaxis Module

obsidian rover
#

Oh yeah, that shoudl do it

fleet schooner
#

hmm, libvaxis doesn't actually use the module I need though. I guess I'll leave it as a local dependency then.

obsidian rover
#

Which module are you using?

fleet schooner
#

CaseData

obsidian rover
#

Hmm

#

Probably best to do a local dep then

charred mountain
fleet schooner
uncut torrent
#

hey, any chance this project just works with master? I saw that the target version is 0.13

obsidian rover
uncut torrent
#

aight, thanks for the quick reply

fleet schooner
#

Are we going to get OSC 176 (set application ID) support in libvaxis too?

obsidian rover
obsidian rover
#

I think that paste_allocator will need to be renamed allocator, and the library will hold onto the first value it receives from a query so it can restore it prior to exiting

fleet schooner
#

makes sense

fleet schooner
#

@obsidian rover I would have PRed a fix instead of opening #65, but I didn't have time and I didn't want to forget about it.

inner otter
#

How do I post my own event in the loop, postEvent?

fleet schooner
inner otter
fleet schooner
#

@obsidian rover Have you ever tried flow on windows? It looks pretty good, but there is a weird issue where cells are randomly rendered at half brightness. Have you seen this before?

#

It seems to be differential update related. Full renders are usually fine.

obsidian rover
#

Is that whole line supposed to be red background?

#

Oh I see, it’s the imports too, hmm

fleet schooner
#

its the imports

fleet schooner
#

It looks like I can query the default terminal background with queryColor(..., .bg), but not set it. I'd like to set it and have it reset automatically in Vasix.resetState. Shall I PR this? (I could also just do it in Flow directly)

obsidian rover
#

@fleet schooner Have you seen XTPOPCOLORS / XTPUSHCOLORS? I'm thinking of implementing that at some point

#

kitty, xterm, and just recently foot added it

fleet schooner
#

Yeah, I saw it. I don't have a use case for it in flow though.

feral charm
#

How compliant is libvaxis with the X/Open Curses standard of the Single Unix Specification?

obsidian rover
feral charm
#

To be fair I also struggle to find copies of it :/

obsidian rover
#

Just briefly looking at it, I'd say "very little"

feral charm
#

oh wow that was quick

feral charm
obsidian rover
#

It actually has some similar concepts

#

Screen, windows, subwindows

feral charm
#

Well yes, you can't reinvent the wheel without making it round.

#

Does it matter tho

obsidian rover
#

But this is a very prescriptive specification regarding memory, function names, etc

#

And libvaxis does not comply with that

feral charm
#

want it to stay that way?

obsidian rover
#

Without knowing the benefit of compliance, yeah...it would be pretty simple to write a compliance wrapper it seems

#

But I don't know what compliance gains

civic condor
#

Hey, I’ve been lurking here for a while and following Vaxis development for a while. It looks like a fantastic library!

I’ve got a similar library for c++ code (although less battle tested and well rounded) and I’m trying to develop a way of managing splits across the terminal, something like how vim splits or tmux works. I wanted to ask how Vaxis handles splits or panes or windows within a terminal window. What approach did you take here?

obsidian rover
civic condor
#

Is there an example on the repo that shows what the interface is like here? How does the window link to the text it’s meant to draw? Does it take a (might use CPP terminology here) pointer to a vector of strings and just draw that?

obsidian rover
# civic condor Is there an example on the repo that shows what the interface is like here? How ...

The two main datastructures you'll want to look at are src/Screen.zig and src/InternalScreen.zig. Both of these have a "cell" structure for each cell on the terminal window. When drawing, a pointer to the grapheme for that particular cell is stored in Screen, the pointer must live until render is called. Before rendering, Any widget can overwrite any particular cell's value (this makes for efficient / easy drawing and clearing of areas). Once the drawing is complete, the Screen represents the state we want the terminal to look like so we call render. InternalScreen is the state of our last frame that we drew. So render compares each cell for equality (grapheme, style, urls, etc). If the cell is different, it updates the terminal. the state of the cell is copied into the InternalScreen structure. The copy is so that you can compare frame-to-frame

civic condor
#

That’s pretty clever. I guess you can then use the difference between the two for unit testing as well?

obsidian rover
#

unit what? 😂

civic condor
#

I’ll take more of a look later on - I was skimming through src/Window.zig because I thought to start there before, but thanks for pointing me in the right direction

obsidian rover
#

I don't have much testing in the diff code.

wooden lantern
#

Hey @obsidian rover, is there anyway to have the image loading done in the background? that way instead of freezing, users can still interact with the application. I know Zig doesn't have async so would I need to spawn a thread in the background to do this?

obsidian rover
# wooden lantern Hey <@827504551263207444>, is there anyway to have the image loading done in the...

There are a few new apis for images. The best performing would be to use transmitLocalImagepath. This will be much faster, but doesn't work over ssh (and currently silently fails). Next would be to spawn a thread which loads the image, converts the pixels to an allowable format, and base 64 encodes the image. You can then call transmitPreEncodedImage from the main thread which will chunk up the data and send it. This should be quite a bit faster than current, but you should always send on the main thread since the tty writer is not thread safe

#

For the thread spawning one, you can look at the code in transmitImage - basically do that function in a thread, but then call transmitPreEncodedImage in the main thread

wooden lantern
obsidian rover
#

The next option is to do all the sending yourself - you could chunk, send a chunk, check for input events, then repeat loop and send another chunk.

wooden lantern
#

thanks, i'll mess around with these options and see what works best :)

#

by "doesn't work over ssh", will it crash/error or simply not load? If I can catch on error, then I can just display an error over ssh

obsidian rover
#

It’d be delivered async

wooden lantern
obsidian rover
#

No problem!

proud blade
#

@obsidian rover it's been a while, but I'm making slow and steady progress with rewriting zf to use libvaxis! Got a nice preview pane working today. It is much easier to add new features now. Thanks for vaxis!

wooden lantern
#

Hey @obsidian rover, is there a way to force a flush in instances like this where I need to free memory after writing?

obsidian rover
wooden lantern
#

oh... I think i'm the stupid one lol

#

i'm calling render after the memory is freed

#

😭 sorry man, I need a rubber duck or something aye

obsidian rover
#

No worries! Happy hacking

obsidian rover
#

I'm doing some gardening in libvaxis. Lots of breaking changes are currently on the unreleased HEAD, but it will all be worth it 🙂

One of the major changes coming is the introduction of a TUI framework. The framework (named vxfw..."Vaxis framework") is by no means mandatory, and is basically just built on top of the Vaxis core that is out there today. There is a draft PR up https://github.com/rockorager/libvaxis/pull/105. Would appreciate any commentary if people have thoughts! Overall the idea is for vxfw to be the high level side of libvaxis. For those familiar with Rust, imagine vxfw as Ratatui and libvaxis as crossterm. Or in Go...vxfw is tview and libvaxis is tcell. Or something to that effect.

As a quick example, here is a very basic fuzzy finder implemented in 235 LOC with full unicode and full mouse support. This one uses Text, RichText, ListView, and TextField.

wooden lantern
fierce pond
frigid sundial
wooden lantern
#

Hey @obsidian rover, do you know of any libraries that render md content that work with libvaxis?

obsidian rover
#

Would be a good project though 😉

wooden lantern
#

haha, totally.

#

do you know in terms of the image library if there's a way to see what formats are currently supported? e.g. for my file explorer, i would love to know if the file i'm about to pass to the library is even an image type that it supports

#

currently i'm just passing it any file the user selects and seeing if it spits an error or not lol, not ideal

#

like, i can manually check the libraries readme and hardcode these,,, but ideally there would be something that updates automatically for this

obsidian rover
#

I’m not sure - I’d have to look through their docs, my guess would be to do what you are doing … zigimg has pretty good errors so you could probably do something by switching on the specific error you get

#

I think that table is out of date

#

JPEG is definitely supported

wooden lantern
#

for example, this error occurs when you attempt to pass a .ico file into the function

obsidian rover
#

Hmm, that one seems like a zigimg bug, not an issue with unsupported formats

wooden lantern
wooden lantern
obsidian rover
wooden lantern
wooden lantern
wooden lantern
#

Hey @obsidian rover, I was wondering if you had any further thoughts on issue https://github.com/rockorager/libvaxis/issues/79 in regards to sixel images and the latest two comments

GitHub

Support sixel format for images. This can be useful on terminal that don't support kitty graphics but support sixel.

obsidian rover
wooden lantern
obsidian rover
#

Hey everyone! So I've decided to use Github Discussions as a more general purpose support / Q&A place. I'll still respond to questions here but just wanted people to know that there is that available as well (and is a bit easier to follow than a giant discord thread).

Also there is a Show and Tell section, please post whatever you have built or are building with libvaxis there, it's always fun to see what people are working on!

terse juniper
#

Are there any examples and screenshots of what can be built using this library? Really interested in taking a further look.

#

I'm mostly interested in building some kind of basic dashboard with separate table sections

obsidian rover
terse juniper
#

Thank you

fierce pond
fierce pond
#

pinging here in case you didn't see my question in the Github Discussions tab (my apologies if u did)

obsidian rover
#

Oh I saw it and forgot about it! Ill respond on github

fierce pond
#

No worries! Thank you

wooden lantern
#

Hey @obsidian rover, does Libvaxis support all of these image formats? https://github.com/zigimg/zigimg?tab=readme-ov-file#supported-image-formats

I noticed GIF is technically supported but won't actually render. Is there a way to know which image types will actually be rendered to the terminal?

e.g. I'm using this to do a simple check to see if zig-img supports the image but this doesn't seem to mean libvaxis supports the rendering

var match = false;
inline for (@typeInfo(vaxis.zigimg.Image.Format).@"enum".fields) |field| {
    const entry_ext = std.mem.trimLeft(
        u8,
        std.fs.path.extension(entry.name),
        ".",
    );
    if (std.mem.eql(u8, entry_ext, field.name)) {
        match = true;
    }
}
if (!match) break :unsupported;
obsidian rover
wooden lantern
obsidian rover
#

It should be working with anything supported by zigimg. When you load the file, zigimg decodes the image to pixels - the vaxis transmit function will then convert those to either png, rgb, or rgba.

#

I'm not sure exactly how this interacts with gifs because those are a sort of container of separate frames

#

Maybe we only get the first frame of a gif?

wooden lantern
wooden lantern
candid rock
#

For allocs during draw I have an arena allocator i init before I call draw and I deinit after it is rendered. What is the standard way of doing this?

still hare
#

Do not deinit, use the reset function with retain_capacity

#

You want to reuse the same arena between draws

candid rock
#

Cool, thanks

flint gale
#

@obsidian rover does the mouse scroll event listner work ? couldn't find any example. as well

obsidian rover
flint gale
obsidian rover
#

I don't see an explicit example...here is how you can handle these events (they are delivered as a .mouse event)

#

To enable mouse, you to call vx.setMouseMode(writer, true)

tranquil pasture
#

Hello !
I have a background thread working, and I'd like to display data it generates.
With the "low level API" it's easily done, as I have access the the loop and its postEventthread-safe function.
Yesterday, I played around using the vxfwhigh-level API. While its usage is simpler, and gives access to more widgets, it seems it has no capability to inject user events.
Yes, I can eventually update the model from the thread with atomics, and continously redraw instead of waiting for events, but still, I'm wondering if I missed something.
tl;dr -> Does vxfwallows user events in its loop ? If yes, how ?

obsidian rover
#

So...in some way we need to asynchronously wake up the loop with a posted event, or maybe a wakeup that the app has events available (in it's own queue, for example).

#

I get around this in comlink currently by having a timer callback and I check for events. Not event based, which is icky

tranquil pasture
#

Hi,
Thank you very much for your detailed reply.
No worries, I understand the situation and I’ve already a workaround (continuously redrawing)
Thanks again !

obsidian rover
tranquil pasture
#

Sure ! I think there is a quick easy fix first: the possibility to simply ask for a redrawing.
So a thread update some atomics and inform the app to trigger a redraw.
It’s not as clean as user events but get the job done without constantly redrawing.

#

For instance, rust cursive has a noop event

#

I admit it’s not clean nor sustainable on big projects

obsidian rover
#

For that route, I think it would be delivered to the widgets as an object on the EventContext:

pub const Wakeup = struct {
    ptr: ?*anyopaque,
    wakeupFn: *const fn (?*anyopaque, Widget) void,

    pub fn wakeup(self: Wakeup, widget: Widget) {
        self.wakeupFn(self.ptr, widget);
    }
}
#

Something like that

#

Where ptr is the event loop implementation

#

All the pointers would be stable, so this object could be held onto by the application to call from whatever thread it needs

tranquil pasture
#

Also, I know we don’t have access to the loop, and it’s on purpose. But maybe we can try to find a way to configure the loop Event enum at comptime and expose a postEvent public function ?

#

But that will make things more complex for only a special case (external threads)

#

Another approach: do nothing and wait for async back so we can select()

obsidian rover
tranquil pasture
#

Oh ok I understand. That was my main approach, but I didn't really understand the context.

#

Regarding my last sentence, I know it's confusing, but maybe it's too early to solve this. Workarounds exist, and async could solve this problem (for instance, Rust Ratatui can use Tokyo's select!)

wooden lantern
#

Hey @obsidian rover, am i misunderstanding how the postEvent function works?

I have some code which spawns up a thread to do some background work, then posts an event if all goes well

{
    const load_img_thread = std.Thread.spawn(.{}, loadImage, .{
        app,
        self.current_item_path,
    }) catch break :unsupported;
    load_img_thread.detach();
}

fn loadImage(
    app: *App,
    path: []const u8,
) error{ Unsupported, OutOfMemory }!void {
    var image = vaxis.zigimg.Image.fromFilePath(
        app.alloc,
        path,
    ) catch {
        return error.Unsupported;
    };
    defer image.deinit();

    if (app.vx.transmitImage(app.alloc, app.tty.anyWriter(), &image, .rgba)) |img| {
        app.image.data = img;
        if (app.image.path) |p| app.alloc.free(p);
        app.image.path = try app.alloc.dupe(u8, path);
        app.loop.postEvent(.{ .image_ready = app.image.path orelse "test" });
    } else |_| {
        if (app.image.data) |img| {
            app.vx.freeImage(app.tty.anyWriter(), img.id);
        }
        app.image = .{};
        return error.Unsupported;
    }
}

This is what my polling looks like

self.loop.pollEvent();
while (self.loop.tryEvent()) |event| {
    switch (event) {
        .key_press => {
            // ...
        },
        .image_ready => {
            std.debug.print("\nooooopppppps\n", .{});
        },
        else => {},
    }
}
#

The problem is, it seems that i have to key press a couple times for the event to be posted, i.e. the image to be shown

#

EDIT: disregard, this is working correctly. I simply had an incorrect if statement surrounding the logic

wooden lantern
obsidian rover
wooden lantern
#

terminal messy but here's another log from what i presume to be the same panic (did the same actions as above)

obsidian rover
#

Doesn't seem obvious, do you have that in a branch I can test locally?

obsidian rover
#

I think what is happening is that you are caching the data of the file. On first transmitImage, vaxis calls zigimg.convert to get it to the right pixel format. zigimg frees the old pixel buffer. Then, the next round you go to transmit and it's bogus data and an invalid free when you try to convert it

#

What I woudl do to make it all better - you don't need to cache the image data, the terminal does that for you. Once you have transmitted an image once you can show it by referencing it's ID (which in vaxis terms, just means draw it)

#

So on first load of image - transmit it and cache the vaxis.Image object (which is just an id, width, and height)

#

Subsequent draws will be very fast since they won't do any conversion or transmission

wooden lantern
obsidian rover
#

Within your app it should be safe

#

There is a query to see if an id is still in mem (I haven’t implemented it)

wooden lantern
wooden lantern
#

Hey @obsidian rover, do you know why when running something like cd $(jido), the alt screens seem to break completely?
https://github.com/brookjeynes/jido/blob/main/src/events.zig#L421 here i'm handling opening up $EDITOR on a file, and jumping back when that editor closes.
doing something like this in cd $(jido) seems to just present me with a blank screen that still accepts input but doesn't show anything

obsidian rover
#

I'm not sure I get it. I tried locally and it seems to behave the same if I do cd $(jido) or just jido

#

Your gif looks like a blank screen momentarlily and then it loads the dir?

wooden lantern
#

ah sorry, maybe the gif wasn't obvious with what was happening

#

when i attempt to open a file, which enters altscreen and spawns $EDITOR (in my instance neovim), the screen remains blank but still accepts keyboard input

#

this only occurs when running jido in something like cd $(jido)

#

i'm wondering if this is something i'm handling incorrect with my alt screen usage or if this is something on libvaxis end

obsidian rover
#

So you did a suspend and then call that?

wooden lantern
#

That's the flow there

obsidian rover
#

I wonder if somehow the subshell is messing with it

wooden lantern
obsidian rover
#

I'm not sure - what editor are you using?

wooden lantern
#

neovim, were you able to repro?

obsidian rover
#

Yeah I can repro

#

Hm

#

EDITOR=flow works fine

#

EDITOR=helix doesn't, neither does nvim

#

Which makes me think its a subshell thing? I know for sure that flow opens /dev/tty...helix and nvim might be checking stdin/stdout/stderr and the subshell messes with it? I don't think that should be happening though

#

I'll keep thinking about it. On the mend from covid and a bit brain foggy today

wooden lantern
#

thanks for helping out on this one

wooden lantern
main gate
#

Hi, is there any up-to-date tutorial or examples for using libvaxis with Zig 0.14.1 in order to get started with the library? The libvaxis-starter repository example seems to be broken with the latest libvaxis master version (I guess tagged releases don't work with Zig 0.14.1 as they are ancient?)

fierce pond
wooden lantern
#

Hey @obsidian rover, is rendering libvaxis via a buffered writer no longer an option in 0.15.0?

obsidian rover
#

It is, it accepts the new std.io.Writer which has buffering built in

wooden lantern
#

just this now?

try vx.render(tty.writer());
wooden lantern
willow river
wooden lantern
wooden lantern
restive grove
wooden lantern
#

Hey @obsidian rover, is there a reason we now need a buffer when reading image from path? Is there a way for this to be allocated instead of a hard-coded buffer size like in the example? I am currently caching loaded images for quick access later but I'm now needing to keep a new buffer per image alive?

fn loadImage(app: *App, path: []const u8, buffer: *[1024]u8) error{ Unsupported, OutOfMemory }!void {
    _ = buffer;
    var buf: [1024 * 1024]u8 = undefined;
    const data = vaxis.zigimg.Image.fromFilePath(app.alloc, path, &buf) catch {
        const message = try std.fmt.allocPrint(app.alloc, "Failed to load image '{s}' - error occurred while attempting to read image from path.", .{path});
        defer app.alloc.free(message);
        app.notification.write(message, .err) catch {};
        if (app.file_logger) |file_logger| file_logger.write(message, .err) catch {};
        return error.Unsupported;
    };

    app.images.mutex.lock();
    if (app.images.cache.getPtr(path)) |entry| {
        entry.status = .ready;
        entry.data = data;
    } else {
        const message = try std.fmt.allocPrint(app.alloc, "Failed to load image '{s}' - error occurred while attempting to add image to cache.", .{path});
        defer app.alloc.free(message);
        app.notification.write(message, .err) catch {};
        if (app.file_logger) |file_logger| file_logger.write(message, .err) catch {};
        return error.Unsupported;
    }
    app.images.mutex.unlock();

    app.loop.postEvent(.image_ready);
}
wooden lantern
# wooden lantern here you go, hope that's a bit easier to see

I believe there to be an issue with the deallocation of images, im unsure what i'm doing wrong unfortunately - https://github.com/brookjeynes/jido/blob/0b1d69cded56dc2677993d9b62461650b6de4fc8/src/app.zig#L95

Segmentation fault at address 0x7f4c8e8f1010
/home/bjeyn/.zvm/0.15.1/lib/std/mem/Allocator.zig:428:26: 0x13d7168 in free__anon_58795 (std.zig)
    @memset(non_const_ptr[0..bytes_len], undefined);
                         ^
/home/bjeyn/.cache/zig/p/zigimg-0.1.0-8_eo2vUZFgAAtN1c6dAO5DdqL0d4cEWHtn6iR5ucZJti/src/color.zig:1030:45: 0x13663d1 in deinit (zigimg.zig)
            .rgba32 => |data| allocator.free(data),
                                            ^
/home/bjeyn/.cache/zig/p/zigimg-0.1.0-8_eo2vUZFgAAtN1c6dAO5DdqL0d4cEWHtn6iR5ucZJti/src/Image.zig:137:23: 0x12e1ec3 in deinit (zigimg.zig)
    self.pixels.deinit(allocator);
                      ^
/home/bjeyn/projects/jido/src/app.zig:103:21: 0x127b557 in deinit (main.zig)
            d.deinit(alloc);
                    ^
/home/bjeyn/projects/jido/src/app.zig:207:29: 0x1226561 in deinit (main.zig)
        img.value_ptr.deinit(self.alloc, self.vx, &self.tty);
                            ^
/home/bjeyn/projects/jido/src/main.zig:116:25: 0x12189bb in main (main.zig)
        defer app.deinit();
                        ^
/home/bjeyn/.zvm/0.15.1/lib/std/start.zig:627:37: 0x1219db9 in posixCallMainAndExit (std.zig)
            const result = root.main() catch |err| {
                                    ^
/home/bjeyn/.zvm/0.15.1/lib/std/start.zig:232:5: 0x11c32f1 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^
???:?:?: 0x0 in ??? (???)
wooden lantern
#

Hey rockorager, in what instances would cursorLeft() and cursorRight() fail to work? Running the textinput example, left and right works but in the usage within my app it seems it fails to actually move the cursor left and right.

#

I'm a little confused as to why as i'm not doing anything particularly different to that example