Skip to content

Instantly share code, notes, and snippets.

@gregbuchholz
Created October 28, 2021 02:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gregbuchholz/7d731191fcea95b7450859aeb0f4eb20 to your computer and use it in GitHub Desktop.
Save gregbuchholz/7d731191fcea95b7450859aeb0f4eb20 to your computer and use it in GitHub Desktop.
Rust SDL2 with Emscripten example that works as a linux native app, but fail when compiled with --target=asmjs-unknown-emscripten
[package]
name = "js_test"
version = "0.1.0"
edition = "2018"
[dependencies]
sdl2 = "0.35.0"
#sdl2 = "0.29.0"
#.cargo/config
[target.asmjs-unknown-emscripten]
rustflags = [
"-C", "link-args=src/gxx_personality_v0_stub.o -sUSE_SDL=2"
]
//from https://stackoverflow.com/a/69198170
//compile with: emcc -c gxx_personality_v0_stub.cpp
#include "unwind.h"
#include "stdint.h"
extern "C" _Unwind_Reason_Code __gxx_personality_v0 (int version, _Unwind_Action actions, uint64_t exceptionClass, _Unwind_Exception* unwind_exception, _Unwind_Context* context) {
return _URC_NO_REASON;
}
//Original from: https://puddleofcode.com/story/definitive-guide-to-rust-sdl2-and-emscriptem
extern crate sdl2;
use std::process;
use sdl2::rect::{Rect};
use sdl2::event::{Event};
use sdl2::keyboard::Keycode;
#[cfg(target_os = "emscripten")]
pub mod emscripten;
fn main() {
let ctx = sdl2::init().unwrap();
let video_ctx = ctx.video().unwrap();
let window = match video_ctx
.window("updating SDL2 example with rust and asmjs...", 640, 480)
.position_centered()
//.opengl()
.build() {
Ok(window) => window,
Err(err) => panic!("failed to create window: {}", err)
};
let mut renderer = match window
// replace "renderer()" with "into_canvas()" since the "renderer()" method no
// longer seems available in the upgrade from rust-SDL2 v0.29.0 to v0.35.0.
// Compiling with v0.29.0 is apparently incompatible with my recent
// version of emscripten (2.0.31).
//.renderer()
.into_canvas()
.build() {
Ok(renderer) => renderer,
Err(err) => panic!("failed to create renderer: {}", err)
};
let mut rect = Rect::new(10, 10, 10, 10);
let black = sdl2::pixels::Color::RGB(0, 0, 0);
let white = sdl2::pixels::Color::RGB(255, 255, 255);
let mut events = ctx.event_pump().unwrap();
let mut main_loop = || {
for event in events.poll_iter() {
match event {
Event::Quit {..} | Event::KeyDown {keycode: Some(Keycode::Escape), ..} => {
process::exit(1);
},
Event::KeyDown { keycode: Some(Keycode::Left), ..} => {
rect.x -= 10;
},
Event::KeyDown { keycode: Some(Keycode::Right), ..} => {
rect.x += 10;
},
Event::KeyDown { keycode: Some(Keycode::Up), ..} => {
rect.y -= 10;
},
Event::KeyDown { keycode: Some(Keycode::Down), ..} => {
rect.y += 10;
},
_ => {}
}
}
println!("I'm going to panic on the next statement...");
// with an "Invalid renderer" at sdl2/render.rs:990...
let _ = renderer.set_draw_color(black);
let _ = renderer.clear();
let _ = renderer.set_draw_color(white);
let _ = renderer.fill_rect(rect);
let _ = renderer.present();
};
#[cfg(target_os = "emscripten")]
use emscripten::{emscripten};
#[cfg(target_os = "emscripten")]
emscripten::set_main_loop_callback(main_loop);
#[cfg(not(target_os = "emscripten"))]
loop { main_loop(); }
}
@gregbuchholz
Copy link
Author

// taken from https://github.com/Gigoteur/PX8/blob/master/src/px8/emscripten.rs

#[cfg(target_os = "emscripten")]
pub mod emscripten {
    use std::cell::RefCell;
    use std::ptr::null_mut;
    use std::os::raw::{c_int, c_void, c_float};

    #[allow(non_camel_case_types)]
    type em_callback_func = unsafe extern fn();

    extern {
        pub fn emscripten_set_main_loop(func: em_callback_func, fps: c_int, simulate_infinite_loop: c_int);
        pub fn emscripten_cancel_main_loop();
        pub fn emscripten_get_now() -> c_float;
    }

    thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(null_mut()));

    pub fn set_main_loop_callback<F>(callback: F) where F: FnMut() {
        MAIN_LOOP_CALLBACK.with(|log| {
            *log.borrow_mut() = &callback as *const _ as *mut c_void;
        });

        unsafe { emscripten_set_main_loop(wrapper::<F>, 0, 1); }

        unsafe extern "C" fn wrapper<F>() where F: FnMut() {
            MAIN_LOOP_CALLBACK.with(|z| {
                let closure = *z.borrow_mut() as *mut F;
                (*closure)();
            });
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment