Skip to content

Speedy2D glutwindow draw callback #38

@Almoped

Description

@Almoped

First of all, thanks for all the videos, examples and making fltk-rs!

I've been trying to learn Rust and write an application that uses FLTK and Speedy2D. Currently on the Speedy2D page the instructions for managing GL oneself is this:

let mut renderer = unsafe {
    GLRenderer::new_for_gl_context((640, 480), |fn_name| {
        window_context.get_proc_address(fn_name) as *const _
    })

which is a bit different than what is in the fltk/Speedy2D demo. But I've managed to make a program run with this in Cargo.toml:

edition = "2024"
[dependencies]
fltk = { version = "^1.5", features = ["enable-glwindow"] }
speedy2d = { version = "2.1.0", default-features = false }

The problem is writing the glutwindow draw() function if it needs to move in data from a struct owned by self. A custom draw_on_glut(&mut self) function, which invokes Speedy2D, somewhat works but it's not always called when needed. I am unsure if packing the variables in std::rc, extending the glutwindow widget somehow or using a different layout is the way to go.

This example shows the structure I am currently at:

use fltk::{app::{self, event_key, Receiver, Sender}, enums::{Event, Key}, prelude::*, window::{self, Window}};
use speedy2d::dimen::Vector2;

fn main() {
    let args: Vec<_> = std::env::args().collect();
    let mut app = MyApp::build(args);
    app.launch();
}

pub enum Message {
    Up,
    Down,
    Left,
    Right,
    Reset,
    GlutResized,
}

pub struct MyApp {
    app: app::App,
    tx: Sender<Message>,
    rx: Receiver<Message>,
    display: MyDisplay,
}

impl MyApp {
    pub fn build(args: Vec<String>) -> Self {
        let (tx, rx) = app::channel::<Message>();
        let app = app::App::default();
        let mut main_win = Window::default().with_size(800, 600);
        main_win.make_resizable(true);
        
        let glut_win = fltk::window::GlutWindow::default_fill();
        glut_win.end();
        
        main_win.end();
        main_win.show();

        let mut display: MyDisplay = MyDisplay::build(glut_win, tx);
        display.draw_on_glut();

        Self {
            app,
            tx,
            rx,
            display,
        }
    }

    pub fn launch(&mut self) {
        while self.app.wait() {
            use Message::*;
            if let Some(msg) = self.rx.recv() {
                match msg {
                    Up => self.display.up(),
                    Down => self.display.down(),
                    Left => self.display.left(),
                    Right => self.display.right(),
                    Reset => self.display.reset(),
                    GlutResized => self.display.resize_glut(),
                }
            }
        }
    }
}

pub struct MyDisplay {
    glut_win: window::GlutWindow,
    renderer: speedy2d::GLRenderer,
    xpos: f32, //example of field in struct used to draw circle
    ypos: f32,
}

impl MyDisplay {
    pub fn build(mut glut_win: fltk::window::GlutWindow, tx: app::Sender<Message>) -> Self {
        
        let w = glut_win.width();
        let h = glut_win.height();

        let renderer = unsafe {
            speedy2d::GLRenderer::new_for_gl_context((w as u32, h as u32), |fn_name| {
            glut_win.get_proc_address(fn_name) as *const _
        })}.expect("cannot connect glcontext");
        
        glut_win.resize_callback(move|_sel, _xpos, _ypos, _wid, _hei| {
            tx.send(Message::GlutResized);            
        });

        glut_win.draw(|_| {
            //make this work somehow
            //have speedy2d renderer, graphics, draw_circle in here?
        });

        glut_win.handle(move |widget, event| {
            match event {
                Event::Focus => {
                    true
                },

                Event::KeyDown => {
                    match event_key() {
                        Key::Up => {
                            tx.send(Message::Up);
                            true
                        },

                        Key::Down => {
                            tx.send(Message::Down);
                            true
                        },

                        Key::Left => {
                            tx.send(Message::Left);
                            true
                        },

                        Key::Right => {
                            tx.send(Message::Right);
                            true
                        },

                        Key::Enter => {
                            tx.send(Message::Reset);                            
                            true
                        },

                        _ => false,
                    }
                },

                _ => false,
            }
        });

        Self {
            glut_win,
            renderer,
            xpos: 200.,
            ypos: 100.,
        }
    }

    pub fn resize_glut(&mut self) {
        self.draw_on_glut();
    }

    pub fn draw_on_glut(&mut self) {
        self.renderer.set_viewport_size_pixels(Vector2::new(self.glut_win.width() as u32, self.glut_win.height() as u32));
        self.renderer.draw_frame(|graphics| {
            graphics.clear_screen(speedy2d::color::Color::DARK_GRAY);
            graphics.draw_circle((self.xpos, self.ypos), 50., speedy2d::color::Color::RED);
        });
        self.glut_win.flush(); //seems to need damage or flush to draw...
        //self.glut_win.set_damage(true);
    }

    pub fn reset(&mut self) {
        self.xpos = 300.;
        self.ypos = 60.;
        self.draw_on_glut();        
    }

    pub fn up(&mut self) {
        self.ypos -= 1.;
        self.draw_on_glut();
    }
    
    pub fn down(&mut self) {
        self.ypos += 1.;
        self.draw_on_glut();
    }

    pub fn left(&mut self) {
        self.xpos -= 1.;
        self.draw_on_glut();
    }

    pub fn right(&mut self) {
        self.xpos += 1.;
        self.draw_on_glut();
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions