mirror of
https://github.com/Xevion/rust-sdl2-emscripten.git
synced 2026-01-31 05:11:56 -06:00
Latest emscripten attempt
This commit is contained in:
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
[target.wasm32-unknown-emscripten]
|
[target.wasm32-unknown-emscripten]
|
||||||
# TODO: Document what the fuck this is.
|
# TODO: Document what the fuck this is.
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-O", "-C", "link-args=-O2 --profiling",
|
# "-O", "-C", "link-args=-O2 --profiling",
|
||||||
#"-C", "link-args=-O3 --closure 1",
|
#"-C", "link-args=-O3 --closure 1",
|
||||||
"-C", "link-args=-sASYNCIFY -sALLOW_MEMORY_GROWTH=1",
|
# "-C", "link-args=-sASYNCIFY -sALLOW_MEMORY_GROWTH=1",
|
||||||
"-C", "link-args=-sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS=['png']",
|
"-C", "link-args=-sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS=['png']",
|
||||||
# USE_OGG, USE_VORBIS for OGG/VORBIS usage
|
# USE_OGG, USE_VORBIS for OGG/VORBIS usage
|
||||||
"-C", "link-args=--preload-file assets/",
|
# "-C", "link-args=--preload-file assets/",
|
||||||
]
|
]
|
||||||
|
|||||||
Vendored
+1
-1
@@ -13,7 +13,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: mymindstorm/setup-emsdk@v11 # setup emscripten toolchain
|
- uses: mymindstorm/setup-emsdk@v11 # setup emscripten toolchain
|
||||||
with:
|
with:
|
||||||
version: 1.39.20
|
version: 3.1.57
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1 # get rust toolchain for wasm
|
- uses: actions-rs/toolchain@v1 # get rust toolchain for wasm
|
||||||
with:
|
with:
|
||||||
|
|||||||
Generated
+4
-4
@@ -167,9 +167,9 @@ checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sdl2"
|
name = "sdl2"
|
||||||
version = "0.35.2"
|
version = "0.36.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a"
|
checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@@ -179,9 +179,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sdl2-sys"
|
name = "sdl2-sys"
|
||||||
version = "0.35.2"
|
version = "0.36.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0"
|
checksum = "26bcacfdd45d539fb5785049feb0038a63931aa896c7763a2a12e125ec58bd29"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
colors-transform = "0.2.11"
|
colors-transform = "0.2.11"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
sdl2 = { version = "0.35", features = ["image", "ttf", "mixer"] }
|
sdl2 = { version = "0.36", features = ["image", "ttf", "mixer"] }
|
||||||
spin_sleep = "1.1.1"
|
spin_sleep = "1.1.1"
|
||||||
tracing = { version = "0.1.37", features = ["max_level_debug", "release_max_level_warn"]}
|
tracing = { version = "0.1.37", features = ["max_level_debug", "release_max_level_warn"]}
|
||||||
tracing-error = "0.2.0"
|
tracing-error = "0.2.0"
|
||||||
|
|||||||
+2
-2
@@ -17,11 +17,11 @@
|
|||||||
</style>
|
</style>
|
||||||
<body>
|
<body>
|
||||||
<canvas id="canvas"></canvas>
|
<canvas id="canvas"></canvas>
|
||||||
<script>
|
<script type="text/javascript">
|
||||||
var Module = {
|
var Module = {
|
||||||
'canvas': document.getElementById('canvas'),
|
'canvas': document.getElementById('canvas'),
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script src="pacman.js"></script>
|
<script type="text/javascript" src="pacman.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Executable
+8
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
echo "Building WASM with Emscripten"
|
||||||
|
./scripts/build-wasm.sh
|
||||||
|
|
||||||
|
echo "Serving WASM with Emscripten"
|
||||||
|
python3 -m http.server -d ./dist/ 8080
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
// SOURCE: https://users.rust-lang.org/t/sdl2-emscripten-asmjs-and-invalid-renderer-panic/66567/2
|
||||||
|
|
||||||
|
// Based on emscripten.rs from https://github.com/therocode/rust_emscripten_main_loop
|
||||||
|
// This file interacts with the Emscripten API to provide a scheduling mechanism for main looping.
|
||||||
|
|
||||||
|
// Since Emscripten only schedules the looping to be executed later by the browser, we need to make sure that the
|
||||||
|
// data object looped upon lives as long as the looping is scheduled, as well as being properly destroyed afterwards.
|
||||||
|
|
||||||
|
// The Emscripten function used for this is emscripten_set_main_loop which will do the scheduling as well as terminate the current code flow
|
||||||
|
// to prevent scopes from being exited which would cause objects to be destroyed prematurely. To be able to destroy the data object properly
|
||||||
|
// as looping is terminated, the object is stored in thread_local storage.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
|
// Declare our FFI to the Emscripten functions we need. These will be linked in when building for Emscripten targets.
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
type em_callback_func = unsafe extern "C" fn();
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub fn emscripten_set_main_loop(
|
||||||
|
func: em_callback_func,
|
||||||
|
fps: c_int,
|
||||||
|
simulate_infinite_loop: c_int,
|
||||||
|
);
|
||||||
|
pub fn emscripten_cancel_main_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
// This is where the data object will be kept during the scheduled looping. The storage structure is justified as follows
|
||||||
|
|
||||||
|
// thread_local - we need it outside of function scope. thread_local is enough since we only expect interactions from the same thread.
|
||||||
|
// RefCell<..> - allows for mutable access from anywhere which we need to store and then terminate. Still borrow-checked in runtime.
|
||||||
|
// Option<..> - we don't always have anything scheduled
|
||||||
|
// Box<dyn ...> - make it work generically for any closure passed in
|
||||||
|
|
||||||
|
static MAIN_LOOP_CLOSURE: RefCell<Option<Box<dyn FnMut()>>> = RefCell::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedules the given callback to be run over and over in a loop until it returns MainLoopEvent::Terminate.
|
||||||
|
// Retains ownership of the passed callback
|
||||||
|
pub fn set_main_loop_callback<F: FnMut() + 'static>(callback: F) {
|
||||||
|
// Move the callback into the data storage for safe-keeping
|
||||||
|
MAIN_LOOP_CLOSURE.with(|d| {
|
||||||
|
*d.borrow_mut() = Some(Box::new(callback));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Define a wrapper function that is compatible with the emscripten_set_main_loop function.
|
||||||
|
// This function will take care of extracting and executing our closure.
|
||||||
|
unsafe extern "C" fn wrapper<F: FnMut()>() {
|
||||||
|
// Access and run the stashed away closure
|
||||||
|
MAIN_LOOP_CLOSURE.with(|z| {
|
||||||
|
if let Some(closure) = &mut *z.borrow_mut() {
|
||||||
|
(*closure)();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule the above wrapper function to be called regularly with Emscripten
|
||||||
|
unsafe {
|
||||||
|
emscripten_set_main_loop(wrapper::<F>, 0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is used to de-schedule the main loop function and destroy the kept closure object
|
||||||
|
pub fn cancel_main_loop() {
|
||||||
|
// De-schedule
|
||||||
|
unsafe {
|
||||||
|
emscripten_cancel_main_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the stored closure object
|
||||||
|
MAIN_LOOP_CLOSURE.with(|d| {
|
||||||
|
*d.borrow_mut() = None;
|
||||||
|
});
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::process;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use sdl2::event::Event;
|
||||||
|
use sdl2::keyboard::Keycode;
|
||||||
|
use sdl2::pixels::Color;
|
||||||
|
use sdl2::rect::Rect;
|
||||||
|
use sdl2::render::WindowCanvas;
|
||||||
|
use sdl2::Sdl;
|
||||||
|
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
pub mod emscripten;
|
||||||
|
|
||||||
|
static BLACK: Color = Color::RGB(0, 0, 0);
|
||||||
|
static WHITE: Color = Color::RGB(255, 255, 255);
|
||||||
|
|
||||||
|
pub fn main_loop(ctx: Rc<RefCell<Sdl>>, rect: Rc<RefCell<Rect>>, canvas: Rc<RefCell<WindowCanvas>>) -> impl FnMut() {
|
||||||
|
let mut events = ctx.borrow_mut().event_pump().unwrap();
|
||||||
|
|
||||||
|
move || {
|
||||||
|
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.borrow_mut().x -= 10;
|
||||||
|
},
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Right), ..} => {
|
||||||
|
rect.borrow_mut().x += 10;
|
||||||
|
},
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Up), ..} => {
|
||||||
|
rect.borrow_mut().y -= 10;
|
||||||
|
},
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Down), ..} => {
|
||||||
|
rect.borrow_mut().y += 10;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = canvas.borrow_mut().set_draw_color(BLACK);
|
||||||
|
let _ = canvas.borrow_mut().clear();
|
||||||
|
let _ = canvas.borrow_mut().set_draw_color(WHITE);
|
||||||
|
let _ = canvas.borrow_mut().fill_rect(rect.borrow().clone());
|
||||||
|
let _ = canvas.borrow_mut().present();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+64
-47
@@ -4,9 +4,12 @@ use tracing::event;
|
|||||||
use tracing_error::ErrorLayer;
|
use tracing_error::ErrorLayer;
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
|
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
pub mod emscripten;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let sdl_context = sdl2::init().unwrap();
|
let ctx = sdl2::init().unwrap();
|
||||||
let video_subsystem = sdl_context.video().unwrap();
|
let video = ctx.video().unwrap();
|
||||||
|
|
||||||
// Setup tracing
|
// Setup tracing
|
||||||
let subscriber = tracing_subscriber::fmt()
|
let subscriber = tracing_subscriber::fmt()
|
||||||
@@ -17,14 +20,16 @@ pub fn main() {
|
|||||||
|
|
||||||
tracing::subscriber::set_global_default(subscriber).expect("Could not set global default");
|
tracing::subscriber::set_global_default(subscriber).expect("Could not set global default");
|
||||||
|
|
||||||
let window = video_subsystem
|
let window = video
|
||||||
.window("SDL2 Test", 800, 800)
|
.window("SDL2 Test", 800, 800)
|
||||||
.position_centered()
|
.position_centered()
|
||||||
|
.opengl()
|
||||||
.build()
|
.build()
|
||||||
.expect("Could not initialize window");
|
.expect("Could not initialize window");
|
||||||
|
|
||||||
let mut canvas = window
|
let mut canvas = window
|
||||||
.into_canvas()
|
.into_canvas()
|
||||||
|
.present_vsync()
|
||||||
.build()
|
.build()
|
||||||
.expect("Could not build canvas");
|
.expect("Could not build canvas");
|
||||||
|
|
||||||
@@ -34,7 +39,7 @@ pub fn main() {
|
|||||||
|
|
||||||
// let texture_creator = canvas.texture_creator();
|
// let texture_creator = canvas.texture_creator();
|
||||||
|
|
||||||
let mut event_pump = sdl_context
|
let mut event_pump = ctx
|
||||||
.event_pump()
|
.event_pump()
|
||||||
.expect("Could not get SDL EventPump");
|
.expect("Could not get SDL EventPump");
|
||||||
|
|
||||||
@@ -44,55 +49,59 @@ pub fn main() {
|
|||||||
// The start of a period of time over which we average the frame time.
|
// The start of a period of time over which we average the frame time.
|
||||||
let mut sleep_time = Duration::ZERO;
|
let mut sleep_time = Duration::ZERO;
|
||||||
|
|
||||||
let mut main_loop = || {
|
let mut main_loop = move || {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
// TODO: Fix key repeat delay issues by using VecDeque for instant key repeat
|
// TODO: Fix key repeat delay issues by using VecDeque for instant key repeat
|
||||||
for event in event_pump.poll_iter() {
|
// for event in event_pump.poll_iter() {
|
||||||
match event {
|
// match event {
|
||||||
Event::Window { win_event, .. } => match win_event {
|
// Event::Window { win_event, .. } => match win_event {
|
||||||
WindowEvent::Hidden => {
|
// WindowEvent::Hidden => {
|
||||||
event!(tracing::Level::WARN, "Window hidden");
|
// event!(tracing::Level::WARN, "Window hidden");
|
||||||
shown = false;
|
// shown = false;
|
||||||
}
|
// }
|
||||||
WindowEvent::Shown => {
|
// WindowEvent::Shown => {
|
||||||
event!(tracing::Level::WARN, "Window shown");
|
// event!(tracing::Level::WARN, "Window shown");
|
||||||
shown = true;
|
// shown = true;
|
||||||
}
|
// }
|
||||||
_ => {}
|
// _ => {}
|
||||||
},
|
// },
|
||||||
// Handle quitting keys or window close
|
// // Handle quitting keys or window close
|
||||||
Event::Quit { .. } => {
|
// Event::KeyDown {
|
||||||
event!(tracing::Level::INFO, "Exit requested. Exiting...");
|
// keycode: Some(sdl2::keyboard::Keycode::Escape),
|
||||||
return false;
|
// ..
|
||||||
}
|
// }
|
||||||
_ => {
|
// | Event::Quit { .. } => {
|
||||||
event!(tracing::Level::WARN, "Unhandled event: {:?}", event);
|
// event!(tracing::Level::INFO, "Exit requested. Exiting...");
|
||||||
}
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// _ => {
|
||||||
|
// event!(tracing::Level::WARN, "Unhandled event: {:?}", event);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if shown {
|
if shown {
|
||||||
// Set background to black
|
// Set background to black
|
||||||
canvas.set_draw_color(sdl2::pixels::Color::RGB(0, 0, 0));
|
canvas.set_draw_color(sdl2::pixels::Color::RGB(0, 0, 0));
|
||||||
canvas.clear();
|
canvas.clear();
|
||||||
|
|
||||||
// Draw a square under the mouse
|
// // Draw a square under the mouse
|
||||||
let mouse_state = event_pump.mouse_state();
|
// let mouse_state = event_pump.mouse_state();
|
||||||
let mouse_x = mouse_state.x();
|
// let mouse_x = mouse_state.x();
|
||||||
let mouse_y = mouse_state.y();
|
// let mouse_y = mouse_state.y();
|
||||||
let square = sdl2::rect::Rect::new(mouse_x - 25, mouse_y - 25, 50, 50);
|
// let square = sdl2::rect::Rect::new(mouse_x - 25, mouse_y - 25, 50, 50);
|
||||||
|
|
||||||
canvas.set_draw_color(sdl2::pixels::Color::RGB(0, 255, 255));
|
// canvas.set_draw_color(sdl2::pixels::Color::RGB(0, 255, 255));
|
||||||
canvas.fill_rect(square).unwrap();
|
// canvas.fill_rect(square).unwrap();
|
||||||
|
|
||||||
canvas.set_draw_color(sdl2::pixels::Color::RGBA(255, 255, 255, 50));
|
// canvas.set_draw_color(sdl2::pixels::Color::RGBA(255, 255, 255, 50));
|
||||||
canvas
|
// canvas
|
||||||
.draw_line(
|
// .draw_line(
|
||||||
sdl2::rect::Point::new(0, 0),
|
// sdl2::rect::Point::new(0, 0),
|
||||||
sdl2::rect::Point::new(mouse_x, mouse_y),
|
// sdl2::rect::Point::new(mouse_x, mouse_y),
|
||||||
)
|
// )
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
canvas.present();
|
canvas.present();
|
||||||
|
|
||||||
@@ -124,13 +133,21 @@ pub fn main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
#[cfg(target_family = "wasm")]
|
||||||
if !main_loop() {
|
use crate::emscripten;
|
||||||
break;
|
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
emscripten::set_main_loop_callback(main_loop);
|
||||||
|
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
{
|
||||||
|
use std::thread::sleep;
|
||||||
|
loop {
|
||||||
|
// main_loop(Rc::clone(&ctx), Rc::clone(&rect), Rc::clone(&canvas))();
|
||||||
|
main_loop();
|
||||||
|
sleep(Duration::from_millis(10))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user