#key #key-pressed #pixel #draw #graphical #window #winit

chinchilib

A graphical prototyping library for my students

11 releases

0.2.1 Nov 18, 2024
0.2.0 Nov 12, 2024
0.1.8 Nov 12, 2024
0.1.7 Oct 10, 2024

#4 in #graphical

Download history 656/week @ 2024-10-07 97/week @ 2024-10-14 230/week @ 2024-11-11 162/week @ 2024-11-18

392 downloads per month

Custom license

21KB
285 lines

Chinchilib

A rust hommage to the Bunny Library designed for teaching the fundamentals of graphical programming, such as how to place a pixel and draw lines when given an array of memory that will rendered to screen.

It's mostly a wrapper arround pixels and winit that takes care of refresh rate, and keeps track of a set of pressed keys.

Usage

WinitHandler creates a window for you and manages events and timing. You can package your code into a chinchilib::GfxApp implementing struct such as MovingPixel in the example bellow.

WinitHandler will make calls to:

  • on_tick when its time for updating your state with the keys that are pressed right now (or have been pressed in between ticks), return true when you want draw to be called afterwards;
  • draw when its time to modify the framebuffer so that the image on screen changes;
  • done when it wants to know if your app has anything left to do:
    • NotDone is self-explanatory,
    • Remain means that your app is done, but you want the result to stay on screen until the user exits,
    • Exit means that your app is done and the window should close.

WinitHandler closes the window when the user presses the close button or the escape key.

use chinchilib::pixels::Pixels;
use chinchilib::rgb;
use chinchilib::{put_pixel, GfxApp, Key, WinitHandler};

fn main() {
    env_logger::init();

    log::info!("Hello, world!");

    let moving_pixel = Box::new(MovingPixel::new(50, 100));
    let mut app = WinitHandler::new(moving_pixel, (500, 500), 60);
    // We don't have any physics or animations, false helps to preserve performance.
    app.set_always_tick(false);
    app.run().unwrap();
}

/// Example app that only feature a pixel that moves.
struct MovingPixel {
    pos: (usize, usize),
}

impl Default for MovingPixel {
    fn default() -> Self {
        Self { pos: (0, 0) }
    }
}

impl MovingPixel {
    fn new(x: usize, y: usize) -> Self {
        Self { pos: (x, y) }
    }
}

const RED: rgb::RGBA8 = rgb::RGBA8 {
    r: u8::MAX,
    g: 0,
    b: 0,
    a: u8::MAX,
};

impl GfxApp for MovingPixel {
    fn on_tick(&mut self, pressed_keys: &std::collections::HashSet<Key>) -> bool {
        let mut needs_redraw = true;
        for key in pressed_keys {
            match key {
                Key::Left => {
                    self.pos.0 -= 1;
                }
                Key::Right => {
                    self.pos.0 += 1;
                }
                Key::Up => {
                    self.pos.1 -= 1;
                }
                Key::Down => {
                    self.pos.1 += 1;
                }
                _ => {
                    needs_redraw = false;
                }
            }
        }
        needs_redraw
    }

    fn draw(&self, pixels: &mut Pixels, width: usize) {
        if self.pos.0 * self.pos.1 < pixels.frame().len() {
            put_pixel(pixels.frame_mut(), width, self.pos.0, self.pos.1, RED);
        }
    }

    /// For the sake of the example, when x goes under 50, we inidcate that we are done and that
    /// the windows should remain open, when y goes under 50 we indicate that the window should
    /// close, otherwise we are not done.
    fn done(&self) -> chinchilib::DoneStatus {
        if self.pos.0 < 50 {
            chinchilib::DoneStatus::Remain
        } else if self.pos.1 < 50 {
            chinchilib::DoneStatus::Exit
        } else {
            chinchilib::DoneStatus::NotDone
        }
    }
}

Dependencies

~7–43MB
~721K SLoC