diff --git a/examples/breakout/breakout.roc b/examples/breakout/breakout.roc index 64ce4901e7..70e1c9610d 100644 --- a/examples/breakout/breakout.roc +++ b/examples/breakout/breakout.roc @@ -5,47 +5,52 @@ app "breakout" program = { render } -render = \state -> - numRows = 4 - numCols = 8 - numBlocks = numRows * numCols +render = \event -> + when event is + Resize size -> + numRows = 4 + numCols = 8 + numBlocks = numRows * numCols - blocks = List.map (List.range 0 numBlocks) \index -> - col = - Num.rem index numCols - |> Result.withDefault 0 - |> Num.toF32 + blocks = List.map (List.range 0 numBlocks) \index -> + col = + Num.rem index numCols + |> Result.withDefault 0 + |> Num.toF32 - row = - index // numCols - |> Result.withDefault 0 - |> Num.toF32 + row = + index // numCols + |> Result.withDefault 0 + |> Num.toF32 - red = (col / Num.toF32 numCols) |> Result.withDefault 0 - green = ((row / Num.toF32 numRows) |> Result.withDefault 0) - blue = (Num.toF32 index / Num.toF32 numBlocks) |> Result.withDefault 0 + red = (col / Num.toF32 numCols) |> Result.withDefault 0 + green = ((row / Num.toF32 numRows) |> Result.withDefault 0) + blue = (Num.toF32 index / Num.toF32 numBlocks) |> Result.withDefault 0 - color = { r: red * 0.8, g: 0.2 + green * 0.6, b: 0.2 + blue * 0.8, a: 1 } + color = { r: red * 0.8, g: 0.2 + green * 0.6, b: 0.2 + blue * 0.8, a: 1 } - { row, col, color } + { row, col, color } - blockWidth = state.width / numCols |> Result.withDefault 0 - blockHeight = 80 + blockWidth = size.width / numCols |> Result.withDefault 0 + blockHeight = 80 - rects = - List.map blocks \{ row, col, color } -> - left = Num.toF32 col * blockWidth - top = Num.toF32 (row * blockHeight) + rects = + List.map blocks \{ row, col, color } -> + left = Num.toF32 col * blockWidth + top = Num.toF32 (row * blockHeight) - Rect { left, top, width: blockWidth, height: blockHeight, color } + Rect { left, top, width: blockWidth, height: blockHeight, color } - paddle = - color = { r: 0.8, g: 0.8, b: 0.8, a: 1.0 } - width = state.width * 0.25 - height = blockHeight - left = (state.width * 0.5) - (width * 0.5) - top = state.height - (height * 2) + paddle = + color = { r: 0.8, g: 0.8, b: 0.8, a: 1.0 } + width = size.width * 0.25 + height = blockHeight + left = (size.width * 0.5) - (width * 0.5) + top = size.height - (height * 2) - Rect { left, top, width, height, color } + Rect { left, top, width, height, color } - List.append rects paddle + List.append rects paddle + + _ -> + [ Text "TODO handle other events than Resize!" ] diff --git a/examples/breakout/platform/Package-Config.roc b/examples/breakout/platform/Package-Config.roc index 2efff31ced..cee1604c09 100644 --- a/examples/breakout/platform/Package-Config.roc +++ b/examples/breakout/platform/Package-Config.roc @@ -9,8 +9,8 @@ Rgba : { r : F32, g : F32, b : F32, a : F32 } Elem : [ Rect { color : Rgba, left : F32, top : F32, width : F32, height : F32 }, Text Str ] -State : { width : F32, height : F32 } +Event : [ Resize { width : F32, height : F32 }, KeyDown U32, KeyUp U32 ] # TODO allow changing the window title - maybe via a Task, since that shouldn't happen all the time -programForHost : { render : (State -> List Elem) as Render } +programForHost : { render : (Event -> List Elem) as Render } programForHost = program diff --git a/examples/breakout/platform/src/gui.rs b/examples/breakout/platform/src/gui.rs index 23a46d41e3..9f4af16a34 100644 --- a/examples/breakout/platform/src/gui.rs +++ b/examples/breakout/platform/src/gui.rs @@ -9,7 +9,7 @@ use crate::{ text::build_glyph_brush, }, }, - roc::{self, RocElem, RocElemTag}, + roc::{self, Bounds, RocElem, RocElemTag, RocEvent}, }; use cgmath::{Vector2, Vector4}; use glyph_brush::{GlyphCruncher, OwnedSection}; @@ -23,7 +23,7 @@ use wgpu_glyph::GlyphBrush; use winit::{ dpi::PhysicalSize, event, - event::{Event, ModifiersState}, + event::{ElementState, Event, ModifiersState}, event_loop::ControlFlow, platform::run_return::EventLoopExtRunReturn, }; @@ -36,17 +36,17 @@ use winit::{ const TIME_BETWEEN_RENDERS: Duration = Duration::new(0, 1000 / 60); -pub fn run_event_loop(title: &str, state: roc::State) -> Result<(), Box> { +pub fn run_event_loop(title: &str, window_bounds: Bounds) -> Result<(), Box> { // Open window and create a surface let mut event_loop = winit::event_loop::EventLoop::new(); let window = winit::window::WindowBuilder::new() - .with_inner_size(PhysicalSize::new(state.width, state.height)) + .with_inner_size(PhysicalSize::new(window_bounds.width, window_bounds.height)) .with_title(title) .build(&event_loop) .unwrap(); - let mut elems = roc::app_render(state); + let mut elems = roc::app_render(RocEvent::resize(window_bounds)); let instance = wgpu::Instance::new(wgpu::Backends::all()); @@ -150,10 +150,10 @@ pub fn run_event_loop(title: &str, state: roc::State) -> Result<(), Box Result<(), Box { - use event::ElementState::*; - use event::VirtualKeyCode::*; - - match keycode { - Left => match input_state { - Pressed => println!("Left pressed!"), - Released => println!("Left released!"), - }, - Right => match input_state { - Pressed => println!("Right pressed!"), - Released => println!("Right released!"), - }, - _ => { - println!("Other!"); - } + let roc_event = match input_state { + ElementState::Pressed => RocEvent::key_down(keycode), + ElementState::Released => RocEvent::key_up(keycode), }; - elems = roc::app_render(roc::State { - height: size.height as f32, - width: size.width as f32, - }); + elems = roc::app_render(roc_event); + + window.request_redraw(); } //Modifiers Changed Event::WindowEvent { @@ -318,12 +305,6 @@ fn begin_render_pass<'a>( }) } -#[derive(Copy, Clone, Debug, Default)] -pub struct Bounds { - pub height: f32, - pub width: f32, -} - #[derive(Clone, Debug)] struct Drawable { pos: Vector2, diff --git a/examples/breakout/platform/src/lib.rs b/examples/breakout/platform/src/lib.rs index fbb4c75f26..196a65c017 100644 --- a/examples/breakout/platform/src/lib.rs +++ b/examples/breakout/platform/src/lib.rs @@ -4,12 +4,12 @@ mod roc; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let state = roc::State { + let bounds = roc::Bounds { width: 1900.0, height: 1000.0, }; - gui::run_event_loop("RocOut!", state).expect("Error running event loop"); + gui::run_event_loop("RocOut!", bounds).expect("Error running event loop"); // Exit code 0 diff --git a/examples/breakout/platform/src/roc.rs b/examples/breakout/platform/src/roc.rs index 3f73d436bd..d25ffca628 100644 --- a/examples/breakout/platform/src/roc.rs +++ b/examples/breakout/platform/src/roc.rs @@ -7,13 +7,14 @@ use std::ffi::CStr; use std::fmt::Debug; use std::mem::MaybeUninit; use std::os::raw::c_char; +use winit::event::VirtualKeyCode; extern "C" { #[link_name = "roc__programForHost_1_exposed_generic"] fn roc_program() -> (); #[link_name = "roc__programForHost_1_Render_caller"] - fn call_Render(state: *const State, closure_data: *const u8, output: *mut RocList); + fn call_Render(event: *const RocEvent, closure_data: *const u8, output: *mut RocList); #[link_name = "roc__programForHost_size"] fn roc_program_size() -> i64; @@ -23,11 +24,71 @@ extern "C" { fn size_Render() -> i64; } -#[derive(Debug)] #[repr(C)] -pub struct State { - pub height: f32, - pub width: f32, +pub union RocEventEntry { + pub key_down: winit::event::VirtualKeyCode, + pub key_up: winit::event::VirtualKeyCode, + pub resize: Bounds, +} + +#[repr(u8)] +#[allow(unused)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RocEventTag { + KeyDown = 0, + KeyUp = 1, + Resize = 2, +} + +#[repr(C)] +#[cfg(target_pointer_width = "64")] // on a 64-bit system, the tag fits in this pointer's spare 3 bits +pub struct RocEvent { + entry: RocEventEntry, + tag: RocEventTag, +} + +impl Debug for RocEvent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use RocEventTag::*; + + match self.tag() { + KeyDown => unsafe { self.entry().key_down }.fmt(f), + KeyUp => unsafe { self.entry().key_up }.fmt(f), + Resize => unsafe { self.entry().resize }.fmt(f), + } + } +} + +impl RocEvent { + #[cfg(target_pointer_width = "64")] + pub fn tag(&self) -> RocEventTag { + self.tag + } + + pub fn entry(&self) -> &RocEventEntry { + &self.entry + } + + pub fn resize(size: Bounds) -> Self { + Self { + tag: RocEventTag::Resize, + entry: RocEventEntry { resize: size }, + } + } + + pub fn key_down(keycode: VirtualKeyCode) -> Self { + Self { + tag: RocEventTag::KeyDown, + entry: RocEventEntry { key_down: keycode }, + } + } + + pub fn key_up(keycode: VirtualKeyCode) -> Self { + Self { + tag: RocEventTag::KeyUp, + entry: RocEventEntry { key_up: keycode }, + } + } } #[no_mangle] @@ -193,7 +254,7 @@ pub struct ButtonStyles { pub text_color: Rgba, } -pub fn app_render(state: State) -> RocList { +pub fn app_render(state: RocEvent) -> RocList { let size = unsafe { roc_program_size() } as usize; let layout = Layout::array::(size).unwrap(); @@ -212,10 +273,17 @@ pub fn app_render(state: State) -> RocList { } } -unsafe fn call_the_closure(state: State, closure_data_ptr: *const u8) -> RocList { +unsafe fn call_the_closure(event: RocEvent, closure_data_ptr: *const u8) -> RocList { let mut output = MaybeUninit::uninit(); - call_Render(&state, closure_data_ptr as *const u8, output.as_mut_ptr()); + call_Render(&event, closure_data_ptr as *const u8, output.as_mut_ptr()); output.assume_init() } + +#[derive(Copy, Clone, Debug, Default)] +#[repr(C)] +pub struct Bounds { + pub height: f32, + pub width: f32, +}