Drawing pixels in a window and rendering text

I'm writing on an SDR software with a friend (based on radiorust) and I'm looking for a crate which allows direct access to pixels in a window (or some sort of canvas). Using the sdl2 crate, we ran (non-reproducibly under some strange conditions, which didn't really relate to the failing call) into a segmentation fault:

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `target/release/receiver'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007fb951e016be in ?? () from /lib/x86_64-linux-gnu/libSDL2-2.0.so.0
[Current thread is 1 (Thread 0x7fb950904d00 (LWP 188437))]
(gdb) 
(gdb) bt
#0  0x00007fb951e016be in ?? () from /lib/x86_64-linux-gnu/libSDL2-2.0.so.0
#1  0x00007fb951df0486 in ?? () from /lib/x86_64-linux-gnu/libSDL2-2.0.so.0
#2  0x00007fb951e25f63 in ?? () from /lib/x86_64-linux-gnu/libSDL2-2.0.so.0
#3  0x00007fb951db9da0 in ?? () from /lib/x86_64-linux-gnu/libSDL2-2.0.so.0
#4  0x000055a3eae349d4 in sdl2::render::InternalTexture::update ()

Not sure if it's sdl2's fault. We didn't use any unsafe at all, so I understand this shouldn't happen. However, there was also the soapysdr crate involved in the binary, which uses the underlying SoapySDR library, so there's C(++) code involved also (including drivers) which could have caused UB.

Nonetheless, I'm not sure if perhaps sdl2 is more likely to be the cause of the problems (due to the backtrace as well as because of other bugs causing segfaults).

We wondered, if there's a lightweight alternative to sdl2. We don't really need widgets, but rendering text would be a requirement. There is flo_draw (version 0.3 on crates.io, a version 0.4 on GitHub, not sure why it's not updated on crates.io?), but it has a weird interface where you have to pass an Arc<Vec<u8>> (which means I have to do allocations) every time I want to update texture pixels. I didn't find any more suitable interface (yet), which allows mutably accessing the pixels.

Looking for other alternatives, I also found miniquad, which has a promising claim:

Miniquad is a manifestation of a dream in a world where we do not need a deep dependencies tree and thousands lines of code to draw things with a computer.

Miniquad aims to provide a graphics abstraction that works the same way on any platform with a GPU, being as light weight as possible while covering as many machines as possible.

But running the example(s) from miniquad on my FreeBSD system just gives this:

% cargo run --example quad
warning: unused variable: `conf`
   --> src/lib.rs:180:17
    |
180 | pub fn start<F>(conf: conf::Conf, f: F)
    |                 ^^^^ help: if this is intentional, prefix it with an underscore: `_conf`
    |
    = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `f`
   --> src/lib.rs:180:35
    |
180 | pub fn start<F>(conf: conf::Conf, f: F)
    |                                   ^ help: if this is intentional, prefix it with an underscore: `_f`

warning: associated function `with_display` is never used
  --> src/lib.rs:43:19
   |
43 |     pub(crate) fn with_display(&mut self, display: &mut dyn NativeDisplay) -> &mut Context {
   |                   ^^^^^^^^^^^^
   |
   = note: `#[warn(dead_code)]` on by default

warning: `miniquad` (lib) generated 3 warnings
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/examples/quad`

And then I get back to the shell. The program just exits (with exit code 0) without doing anything. I haven't tested it on Linux yet.

So what should I do? Track down the segmentation fault that's supposedly caused by sdl2? Use flo_draw? Invest time trying to find out why miniquad doesn't work and whether it's suitable?

Or is there any out-of-the-box solution that does what I need?

P.S.: Another issue we had with sdl2 was that the way it handles lifetimes made it impossible to put all involved data structures of the user interface into a struct, because it would have required self-referential structs. It was really difficult to handle. Plus, in the documentation sentences like:

A texture can only be used by the Canvas it was originally created from, it is undefined behavior otherwise.

Do they refer to unsafe methods? And which ones? Or does this mean the crate is unsound by design?


I also looked into the gtk4 crate, which could maybe(?) used with "cairo" (which crate?) to draw on it, but documentation and ergonomics don't seem to be nice. I had a look at the documentation:

Simple GtkDrawingArea usage

:warning: The following code is in c :warning:

static void
draw_function (GtkDrawingArea *area,
…

How am I supposed to learn how to draw on it using Rust? I feel like gtk4 is only usable if you are a full-fledged GTK programmer having years of experience using it in C.

Why is this so difficult? Isn't there an easier way?

:sob:

piet works okay for direct 2D graphics programming (including text)

If you're looking for a more batteries included GUI toolkit in Rust areweguiyet has a pretty good list of options, mixed in with a bunch of lower level stuff.

iced and relm4 are generally well regarded, I think

Thanks, I will look into these options. Regarding relm4, I think I would have to use cairo too for direct drawing? Or do you or anyone else know how this works? The documentation of relm4::drawing refers me to gtk4::DrawingArea again.

Oh yeah if you need to do custom 2D rendering you probably do need to fallback to Cairo.

Piet has a Cairo backend so you could use piet to do the 2D rendering if that API looks easier to use. It looks like you can construct a Cairo piet context from a reference to a Cairo context.

Unfortunately the only crate I have any experience with here is piet and I was mostly playing around with that.

piet (or druid?)

If I understand right, then piet doesn't allow me to open a window, but only does the drawing part? I guess I would need something like druid then?

I tried to test druid on FreeBSD, but it didn't work. However, I saw a recent commit, which adds FreeBSD support (but I don't know how to use that yet, because of the dependency tree). Maybe I'll start testing druid on Linux, knowing it will eventually support FreeBSD too.

iced

I looked into iced, but its Readme says:

Iced is currently experimental software. Take a look at the roadmap, check out the issues, and feel free to contribute!

Not sure if it's mature enough to rely upon yet. However, without having looked deeper into it, this looks most promising, as it doesn't rely on some huge non-Rust library. It's example also compiled without problem on FreeBSD.

fltk

Following your advice to check out "areweguiyet", I found Rust bindings (crate fltk) for FLTK. This seemed to be quite lightweight. However, I'm not sure how much maintained/used/evolving FLTK is.

1 Like

FLTK is actively maintaned, but rather conservative in adding new features. Most recent work was about adding Wayland native support (i.e not via xwayland) and hybrid X11/wayland support. In addition to new widgets like Flex and Grid (to improve layouts).

The fltk-rs crates tries to be up to date.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.