From 6ad893ce40de5f95d734d889e97617cbbe168124 Mon Sep 17 00:00:00 2001 From: charlotte Date: Tue, 24 Mar 2026 12:39:40 -0700 Subject: [PATCH 1/4] Add input support; extract glfw crate. --- Cargo.lock | 24 +- Cargo.toml | 12 +- crates/processing_ffi/src/lib.rs | 508 ++++++++++++++++++++- crates/processing_glfw/Cargo.toml | 17 + crates/processing_glfw/src/lib.rs | 313 +++++++++++++ crates/processing_input/Cargo.toml | 11 + crates/processing_input/src/lib.rs | 259 +++++++++++ crates/processing_input/src/state.rs | 75 +++ crates/processing_pyo3/Cargo.toml | 4 +- crates/processing_pyo3/examples/input.py | 21 + crates/processing_pyo3/src/glfw.rs | 101 +--- crates/processing_pyo3/src/graphics.rs | 65 ++- crates/processing_pyo3/src/input.rs | 214 +++++++++ crates/processing_pyo3/src/lib.rs | 227 +++++++++ crates/processing_render/src/render/mod.rs | 2 +- examples/animated_mesh.rs | 3 +- examples/background_image.rs | 3 +- examples/box.rs | 3 +- examples/custom_attribute.rs | 3 +- examples/custom_material.rs | 3 +- examples/glfw.rs | 94 ---- examples/gltf_load.rs | 3 +- examples/input.rs | 57 +++ examples/lights.rs | 3 +- examples/materials.rs | 3 +- examples/midi.rs | 3 +- examples/pbr.rs | 3 +- examples/rectangle.rs | 3 +- examples/stroke_2d.rs | 3 +- examples/stroke_3d.rs | 3 +- examples/transforms.rs | 3 +- examples/update_pixels.rs | 3 +- examples/webcam.rs | 3 +- src/lib.rs | 1 + src/prelude.rs | 3 + 35 files changed, 1818 insertions(+), 238 deletions(-) create mode 100644 crates/processing_glfw/Cargo.toml create mode 100644 crates/processing_glfw/src/lib.rs create mode 100644 crates/processing_input/Cargo.toml create mode 100644 crates/processing_input/src/lib.rs create mode 100644 crates/processing_input/src/state.rs create mode 100644 crates/processing_pyo3/examples/input.py create mode 100644 crates/processing_pyo3/src/input.rs delete mode 100644 examples/glfw.rs create mode 100644 examples/input.rs diff --git a/Cargo.lock b/Cargo.lock index 1caf709..8bc67a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5329,9 +5329,10 @@ name = "processing" version = "0.0.1" dependencies = [ "bevy", - "glfw", "js-sys", "processing_core", + "processing_glfw", + "processing_input", "processing_midi", "processing_render", "processing_webcam", @@ -5361,6 +5362,25 @@ dependencies = [ "processing", ] +[[package]] +name = "processing_glfw" +version = "0.0.1" +dependencies = [ + "bevy", + "glfw", + "processing_core", + "processing_input", + "processing_render", +] + +[[package]] +name = "processing_input" +version = "0.0.1" +dependencies = [ + "bevy", + "processing_core", +] + [[package]] name = "processing_midi" version = "0.0.1" @@ -5375,9 +5395,9 @@ name = "processing_pyo3" version = "0.0.1" dependencies = [ "bevy", - "glfw", "png", "processing", + "processing_glfw", "processing_webcam", "pyo3", ] diff --git a/Cargo.toml b/Cargo.toml index 8815e52..d2237b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,8 @@ processing_core = { path = "crates/processing_core" } processing_pyo3 = { path = "crates/processing_pyo3" } processing_render = { path = "crates/processing_render" } processing_midi = { path = "crates/processing_midi" } +processing_input = { path = "crates/processing_input" } +processing_glfw = { path = "crates/processing_glfw" } processing_webcam = { path = "crates/processing_webcam" } [dependencies] @@ -40,6 +42,7 @@ bevy = { workspace = true } processing_core = { workspace = true } processing_render = { workspace = true } processing_midi = { workspace = true } +processing_input = { workspace = true } processing_webcam = { workspace = true, optional = true } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } @@ -50,12 +53,9 @@ js-sys = "0.3" web-sys = { version = "0.3", features = ["Window"] } [dev-dependencies] -glfw = "0.60.0" +processing_glfw = { workspace = true } rand = "0.10.0" -[target.'cfg(target_os = "linux")'.dev-dependencies] -glfw = { version = "0.60.0", features = ["wayland"] } - [[example]] name = "rectangle" path = "examples/rectangle.rs" @@ -121,6 +121,10 @@ path = "examples/stroke_3d.rs" name = "custom_material" path = "examples/custom_material.rs" +[[example]] +name = "input" +path = "examples/input.rs" + [profile.wasm-release] inherits = "release" opt-level = "z" diff --git a/crates/processing_ffi/src/lib.rs b/crates/processing_ffi/src/lib.rs index e63e542..ee30d4f 100644 --- a/crates/processing_ffi/src/lib.rs +++ b/crates/processing_ffi/src/lib.rs @@ -3,7 +3,7 @@ use bevy::{ prelude::Entity, render::render_resource::{Extent3d, TextureFormat}, }; -use processing::prelude::*; +use processing::prelude::{error::ProcessingError, *}; use crate::color::Color; @@ -1267,3 +1267,509 @@ pub extern "C" fn processing_material(window_id: u64, mat_id: u64) { let mat_entity = Entity::from_bits(mat_id); error::check(|| graphics_record_command(window_entity, DrawCommand::Material(mat_entity))); } + +// Mouse buttons +pub const PROCESSING_MOUSE_LEFT: u8 = 0; +pub const PROCESSING_MOUSE_MIDDLE: u8 = 1; +pub const PROCESSING_MOUSE_RIGHT: u8 = 2; + +// Key codes (GLFW values) +pub const PROCESSING_KEY_SPACE: u32 = 32; +pub const PROCESSING_KEY_QUOTE: u32 = 39; +pub const PROCESSING_KEY_COMMA: u32 = 44; +pub const PROCESSING_KEY_MINUS: u32 = 45; +pub const PROCESSING_KEY_PERIOD: u32 = 46; +pub const PROCESSING_KEY_SLASH: u32 = 47; +pub const PROCESSING_KEY_0: u32 = 48; +pub const PROCESSING_KEY_1: u32 = 49; +pub const PROCESSING_KEY_2: u32 = 50; +pub const PROCESSING_KEY_3: u32 = 51; +pub const PROCESSING_KEY_4: u32 = 52; +pub const PROCESSING_KEY_5: u32 = 53; +pub const PROCESSING_KEY_6: u32 = 54; +pub const PROCESSING_KEY_7: u32 = 55; +pub const PROCESSING_KEY_8: u32 = 56; +pub const PROCESSING_KEY_9: u32 = 57; +pub const PROCESSING_KEY_SEMICOLON: u32 = 59; +pub const PROCESSING_KEY_EQUAL: u32 = 61; +pub const PROCESSING_KEY_A: u32 = 65; +pub const PROCESSING_KEY_B: u32 = 66; +pub const PROCESSING_KEY_C: u32 = 67; +pub const PROCESSING_KEY_D: u32 = 68; +pub const PROCESSING_KEY_E: u32 = 69; +pub const PROCESSING_KEY_F: u32 = 70; +pub const PROCESSING_KEY_G: u32 = 71; +pub const PROCESSING_KEY_H: u32 = 72; +pub const PROCESSING_KEY_I: u32 = 73; +pub const PROCESSING_KEY_J: u32 = 74; +pub const PROCESSING_KEY_K: u32 = 75; +pub const PROCESSING_KEY_L: u32 = 76; +pub const PROCESSING_KEY_M: u32 = 77; +pub const PROCESSING_KEY_N: u32 = 78; +pub const PROCESSING_KEY_O: u32 = 79; +pub const PROCESSING_KEY_P: u32 = 80; +pub const PROCESSING_KEY_Q: u32 = 81; +pub const PROCESSING_KEY_R: u32 = 82; +pub const PROCESSING_KEY_S: u32 = 83; +pub const PROCESSING_KEY_T: u32 = 84; +pub const PROCESSING_KEY_U: u32 = 85; +pub const PROCESSING_KEY_V: u32 = 86; +pub const PROCESSING_KEY_W: u32 = 87; +pub const PROCESSING_KEY_X: u32 = 88; +pub const PROCESSING_KEY_Y: u32 = 89; +pub const PROCESSING_KEY_Z: u32 = 90; +pub const PROCESSING_KEY_BRACKET_LEFT: u32 = 91; +pub const PROCESSING_KEY_BACKSLASH: u32 = 92; +pub const PROCESSING_KEY_BRACKET_RIGHT: u32 = 93; +pub const PROCESSING_KEY_BACKQUOTE: u32 = 96; +pub const PROCESSING_KEY_ESCAPE: u32 = 256; +pub const PROCESSING_KEY_ENTER: u32 = 257; +pub const PROCESSING_KEY_TAB: u32 = 258; +pub const PROCESSING_KEY_BACKSPACE: u32 = 259; +pub const PROCESSING_KEY_INSERT: u32 = 260; +pub const PROCESSING_KEY_DELETE: u32 = 261; +pub const PROCESSING_KEY_RIGHT: u32 = 262; +pub const PROCESSING_KEY_LEFT: u32 = 263; +pub const PROCESSING_KEY_DOWN: u32 = 264; +pub const PROCESSING_KEY_UP: u32 = 265; +pub const PROCESSING_KEY_PAGE_UP: u32 = 266; +pub const PROCESSING_KEY_PAGE_DOWN: u32 = 267; +pub const PROCESSING_KEY_HOME: u32 = 268; +pub const PROCESSING_KEY_END: u32 = 269; +pub const PROCESSING_KEY_CAPS_LOCK: u32 = 280; +pub const PROCESSING_KEY_SCROLL_LOCK: u32 = 281; +pub const PROCESSING_KEY_NUM_LOCK: u32 = 282; +pub const PROCESSING_KEY_PRINT_SCREEN: u32 = 283; +pub const PROCESSING_KEY_PAUSE: u32 = 284; +pub const PROCESSING_KEY_F1: u32 = 290; +pub const PROCESSING_KEY_F2: u32 = 291; +pub const PROCESSING_KEY_F3: u32 = 292; +pub const PROCESSING_KEY_F4: u32 = 293; +pub const PROCESSING_KEY_F5: u32 = 294; +pub const PROCESSING_KEY_F6: u32 = 295; +pub const PROCESSING_KEY_F7: u32 = 296; +pub const PROCESSING_KEY_F8: u32 = 297; +pub const PROCESSING_KEY_F9: u32 = 298; +pub const PROCESSING_KEY_F10: u32 = 299; +pub const PROCESSING_KEY_F11: u32 = 300; +pub const PROCESSING_KEY_F12: u32 = 301; +pub const PROCESSING_KEY_NUMPAD_0: u32 = 320; +pub const PROCESSING_KEY_NUMPAD_1: u32 = 321; +pub const PROCESSING_KEY_NUMPAD_2: u32 = 322; +pub const PROCESSING_KEY_NUMPAD_3: u32 = 323; +pub const PROCESSING_KEY_NUMPAD_4: u32 = 324; +pub const PROCESSING_KEY_NUMPAD_5: u32 = 325; +pub const PROCESSING_KEY_NUMPAD_6: u32 = 326; +pub const PROCESSING_KEY_NUMPAD_7: u32 = 327; +pub const PROCESSING_KEY_NUMPAD_8: u32 = 328; +pub const PROCESSING_KEY_NUMPAD_9: u32 = 329; +pub const PROCESSING_KEY_NUMPAD_DECIMAL: u32 = 330; +pub const PROCESSING_KEY_NUMPAD_DIVIDE: u32 = 331; +pub const PROCESSING_KEY_NUMPAD_MULTIPLY: u32 = 332; +pub const PROCESSING_KEY_NUMPAD_SUBTRACT: u32 = 333; +pub const PROCESSING_KEY_NUMPAD_ADD: u32 = 334; +pub const PROCESSING_KEY_NUMPAD_ENTER: u32 = 335; +pub const PROCESSING_KEY_NUMPAD_EQUAL: u32 = 336; +pub const PROCESSING_KEY_SHIFT_LEFT: u32 = 340; +pub const PROCESSING_KEY_CONTROL_LEFT: u32 = 341; +pub const PROCESSING_KEY_ALT_LEFT: u32 = 342; +pub const PROCESSING_KEY_SUPER_LEFT: u32 = 343; +pub const PROCESSING_KEY_SHIFT_RIGHT: u32 = 344; +pub const PROCESSING_KEY_CONTROL_RIGHT: u32 = 345; +pub const PROCESSING_KEY_ALT_RIGHT: u32 = 346; +pub const PROCESSING_KEY_SUPER_RIGHT: u32 = 347; +pub const PROCESSING_KEY_CONTEXT_MENU: u32 = 348; + +#[unsafe(no_mangle)] +pub extern "C" fn processing_input_mouse_move(surface_id: u64, x: f32, y: f32) { + error::clear_error(); + error::check(|| input_set_mouse_move(Entity::from_bits(surface_id), x, y)); +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_input_mouse_button(surface_id: u64, button: u8, pressed: bool) { + error::clear_error(); + error::check(|| { + let btn = match button { + PROCESSING_MOUSE_LEFT => MouseButton::Left, + PROCESSING_MOUSE_MIDDLE => MouseButton::Middle, + PROCESSING_MOUSE_RIGHT => MouseButton::Right, + _ => { + return Err( + ProcessingError::InvalidArgument(format!( + "invalid mouse button: {button}" + )), + ); + } + }; + input_set_mouse_button(Entity::from_bits(surface_id), btn, pressed) + }); +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_input_scroll(surface_id: u64, x: f32, y: f32) { + error::clear_error(); + error::check(|| input_set_scroll(Entity::from_bits(surface_id), x, y)); +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_input_key(surface_id: u64, key_code: u32, pressed: bool) { + error::clear_error(); + error::check(|| { + let kc = key_code_from_u32(key_code)?; + input_set_key(Entity::from_bits(surface_id), kc, pressed) + }); +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_input_char(surface_id: u64, key_code: u32, codepoint: u32) { + error::clear_error(); + error::check(|| { + let kc = key_code_from_u32(key_code)?; + let ch = char::from_u32(codepoint).ok_or_else(|| { + ProcessingError::InvalidArgument(format!( + "invalid codepoint: {codepoint}" + )) + })?; + input_set_char(Entity::from_bits(surface_id), kc, ch) + }); +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_input_cursor_enter(surface_id: u64) { + error::clear_error(); + error::check(|| input_set_cursor_enter(Entity::from_bits(surface_id))); +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_input_cursor_leave(surface_id: u64) { + error::clear_error(); + error::check(|| input_set_cursor_leave(Entity::from_bits(surface_id))); +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_input_focus(surface_id: u64, focused: bool) { + error::clear_error(); + error::check(|| input_set_focus(Entity::from_bits(surface_id), focused)); +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_input_flush() { + error::clear_error(); + error::check(|| input_flush()); +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_mouse_x(surface_id: u64) -> f32 { + error::clear_error(); + error::check(|| input_mouse_x(Entity::from_bits(surface_id))).unwrap_or(0.0) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_mouse_y(surface_id: u64) -> f32 { + error::clear_error(); + error::check(|| input_mouse_y(Entity::from_bits(surface_id))).unwrap_or(0.0) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_pmouse_x(surface_id: u64) -> f32 { + error::clear_error(); + error::check(|| input_pmouse_x(Entity::from_bits(surface_id))).unwrap_or(0.0) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_pmouse_y(surface_id: u64) -> f32 { + error::clear_error(); + error::check(|| input_pmouse_y(Entity::from_bits(surface_id))).unwrap_or(0.0) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_mouse_is_pressed() -> bool { + error::clear_error(); + error::check(|| input_mouse_is_pressed()).unwrap_or(false) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_mouse_button() -> i8 { + error::clear_error(); + error::check(|| { + input_mouse_button().map(|opt| match opt { + Some(MouseButton::Left) => PROCESSING_MOUSE_LEFT as i8, + Some(MouseButton::Middle) => PROCESSING_MOUSE_MIDDLE as i8, + Some(MouseButton::Right) => PROCESSING_MOUSE_RIGHT as i8, + _ => -1, + }) + }) + .unwrap_or(-1) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_key_is_pressed() -> bool { + error::clear_error(); + error::check(|| input_key_is_pressed()).unwrap_or(false) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_key_is_down(key_code: u32) -> bool { + error::clear_error(); + error::check(|| { + let kc = key_code_from_u32(key_code)?; + input_key_is_down(kc) + }) + .unwrap_or(false) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_key() -> u32 { + error::clear_error(); + error::check(|| input_key().map(|opt| opt.map(|c| c as u32).unwrap_or(0))).unwrap_or(0) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_key_code() -> u32 { + error::clear_error(); + error::check(|| input_key_code().map(|opt| opt.map(key_code_to_u32).unwrap_or(0))) + .unwrap_or(0) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_moved_x() -> f32 { + error::clear_error(); + error::check(|| input_moved_x()).unwrap_or(0.0) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_moved_y() -> f32 { + error::clear_error(); + error::check(|| input_moved_y()).unwrap_or(0.0) +} + +#[unsafe(no_mangle)] +pub extern "C" fn processing_mouse_wheel() -> f32 { + error::clear_error(); + error::check(|| input_mouse_wheel()).unwrap_or(0.0) +} + +fn key_code_from_u32(val: u32) -> processing::prelude::error::Result { + match val { + PROCESSING_KEY_SPACE => Ok(KeyCode::Space), + PROCESSING_KEY_QUOTE => Ok(KeyCode::Quote), + PROCESSING_KEY_COMMA => Ok(KeyCode::Comma), + PROCESSING_KEY_MINUS => Ok(KeyCode::Minus), + PROCESSING_KEY_PERIOD => Ok(KeyCode::Period), + PROCESSING_KEY_SLASH => Ok(KeyCode::Slash), + PROCESSING_KEY_0 => Ok(KeyCode::Digit0), + PROCESSING_KEY_1 => Ok(KeyCode::Digit1), + PROCESSING_KEY_2 => Ok(KeyCode::Digit2), + PROCESSING_KEY_3 => Ok(KeyCode::Digit3), + PROCESSING_KEY_4 => Ok(KeyCode::Digit4), + PROCESSING_KEY_5 => Ok(KeyCode::Digit5), + PROCESSING_KEY_6 => Ok(KeyCode::Digit6), + PROCESSING_KEY_7 => Ok(KeyCode::Digit7), + PROCESSING_KEY_8 => Ok(KeyCode::Digit8), + PROCESSING_KEY_9 => Ok(KeyCode::Digit9), + PROCESSING_KEY_SEMICOLON => Ok(KeyCode::Semicolon), + PROCESSING_KEY_EQUAL => Ok(KeyCode::Equal), + PROCESSING_KEY_A => Ok(KeyCode::KeyA), + PROCESSING_KEY_B => Ok(KeyCode::KeyB), + PROCESSING_KEY_C => Ok(KeyCode::KeyC), + PROCESSING_KEY_D => Ok(KeyCode::KeyD), + PROCESSING_KEY_E => Ok(KeyCode::KeyE), + PROCESSING_KEY_F => Ok(KeyCode::KeyF), + PROCESSING_KEY_G => Ok(KeyCode::KeyG), + PROCESSING_KEY_H => Ok(KeyCode::KeyH), + PROCESSING_KEY_I => Ok(KeyCode::KeyI), + PROCESSING_KEY_J => Ok(KeyCode::KeyJ), + PROCESSING_KEY_K => Ok(KeyCode::KeyK), + PROCESSING_KEY_L => Ok(KeyCode::KeyL), + PROCESSING_KEY_M => Ok(KeyCode::KeyM), + PROCESSING_KEY_N => Ok(KeyCode::KeyN), + PROCESSING_KEY_O => Ok(KeyCode::KeyO), + PROCESSING_KEY_P => Ok(KeyCode::KeyP), + PROCESSING_KEY_Q => Ok(KeyCode::KeyQ), + PROCESSING_KEY_R => Ok(KeyCode::KeyR), + PROCESSING_KEY_S => Ok(KeyCode::KeyS), + PROCESSING_KEY_T => Ok(KeyCode::KeyT), + PROCESSING_KEY_U => Ok(KeyCode::KeyU), + PROCESSING_KEY_V => Ok(KeyCode::KeyV), + PROCESSING_KEY_W => Ok(KeyCode::KeyW), + PROCESSING_KEY_X => Ok(KeyCode::KeyX), + PROCESSING_KEY_Y => Ok(KeyCode::KeyY), + PROCESSING_KEY_Z => Ok(KeyCode::KeyZ), + PROCESSING_KEY_BRACKET_LEFT => Ok(KeyCode::BracketLeft), + PROCESSING_KEY_BACKSLASH => Ok(KeyCode::Backslash), + PROCESSING_KEY_BRACKET_RIGHT => Ok(KeyCode::BracketRight), + PROCESSING_KEY_BACKQUOTE => Ok(KeyCode::Backquote), + PROCESSING_KEY_ESCAPE => Ok(KeyCode::Escape), + PROCESSING_KEY_ENTER => Ok(KeyCode::Enter), + PROCESSING_KEY_TAB => Ok(KeyCode::Tab), + PROCESSING_KEY_BACKSPACE => Ok(KeyCode::Backspace), + PROCESSING_KEY_INSERT => Ok(KeyCode::Insert), + PROCESSING_KEY_DELETE => Ok(KeyCode::Delete), + PROCESSING_KEY_RIGHT => Ok(KeyCode::ArrowRight), + PROCESSING_KEY_LEFT => Ok(KeyCode::ArrowLeft), + PROCESSING_KEY_DOWN => Ok(KeyCode::ArrowDown), + PROCESSING_KEY_UP => Ok(KeyCode::ArrowUp), + PROCESSING_KEY_PAGE_UP => Ok(KeyCode::PageUp), + PROCESSING_KEY_PAGE_DOWN => Ok(KeyCode::PageDown), + PROCESSING_KEY_HOME => Ok(KeyCode::Home), + PROCESSING_KEY_END => Ok(KeyCode::End), + PROCESSING_KEY_CAPS_LOCK => Ok(KeyCode::CapsLock), + PROCESSING_KEY_SCROLL_LOCK => Ok(KeyCode::ScrollLock), + PROCESSING_KEY_NUM_LOCK => Ok(KeyCode::NumLock), + PROCESSING_KEY_PRINT_SCREEN => Ok(KeyCode::PrintScreen), + PROCESSING_KEY_PAUSE => Ok(KeyCode::Pause), + PROCESSING_KEY_F1 => Ok(KeyCode::F1), + PROCESSING_KEY_F2 => Ok(KeyCode::F2), + PROCESSING_KEY_F3 => Ok(KeyCode::F3), + PROCESSING_KEY_F4 => Ok(KeyCode::F4), + PROCESSING_KEY_F5 => Ok(KeyCode::F5), + PROCESSING_KEY_F6 => Ok(KeyCode::F6), + PROCESSING_KEY_F7 => Ok(KeyCode::F7), + PROCESSING_KEY_F8 => Ok(KeyCode::F8), + PROCESSING_KEY_F9 => Ok(KeyCode::F9), + PROCESSING_KEY_F10 => Ok(KeyCode::F10), + PROCESSING_KEY_F11 => Ok(KeyCode::F11), + PROCESSING_KEY_F12 => Ok(KeyCode::F12), + PROCESSING_KEY_NUMPAD_0 => Ok(KeyCode::Numpad0), + PROCESSING_KEY_NUMPAD_1 => Ok(KeyCode::Numpad1), + PROCESSING_KEY_NUMPAD_2 => Ok(KeyCode::Numpad2), + PROCESSING_KEY_NUMPAD_3 => Ok(KeyCode::Numpad3), + PROCESSING_KEY_NUMPAD_4 => Ok(KeyCode::Numpad4), + PROCESSING_KEY_NUMPAD_5 => Ok(KeyCode::Numpad5), + PROCESSING_KEY_NUMPAD_6 => Ok(KeyCode::Numpad6), + PROCESSING_KEY_NUMPAD_7 => Ok(KeyCode::Numpad7), + PROCESSING_KEY_NUMPAD_8 => Ok(KeyCode::Numpad8), + PROCESSING_KEY_NUMPAD_9 => Ok(KeyCode::Numpad9), + PROCESSING_KEY_NUMPAD_DECIMAL => Ok(KeyCode::NumpadDecimal), + PROCESSING_KEY_NUMPAD_DIVIDE => Ok(KeyCode::NumpadDivide), + PROCESSING_KEY_NUMPAD_MULTIPLY => Ok(KeyCode::NumpadMultiply), + PROCESSING_KEY_NUMPAD_SUBTRACT => Ok(KeyCode::NumpadSubtract), + PROCESSING_KEY_NUMPAD_ADD => Ok(KeyCode::NumpadAdd), + PROCESSING_KEY_NUMPAD_ENTER => Ok(KeyCode::NumpadEnter), + PROCESSING_KEY_NUMPAD_EQUAL => Ok(KeyCode::NumpadEqual), + PROCESSING_KEY_SHIFT_LEFT => Ok(KeyCode::ShiftLeft), + PROCESSING_KEY_CONTROL_LEFT => Ok(KeyCode::ControlLeft), + PROCESSING_KEY_ALT_LEFT => Ok(KeyCode::AltLeft), + PROCESSING_KEY_SUPER_LEFT => Ok(KeyCode::SuperLeft), + PROCESSING_KEY_SHIFT_RIGHT => Ok(KeyCode::ShiftRight), + PROCESSING_KEY_CONTROL_RIGHT => Ok(KeyCode::ControlRight), + PROCESSING_KEY_ALT_RIGHT => Ok(KeyCode::AltRight), + PROCESSING_KEY_SUPER_RIGHT => Ok(KeyCode::SuperRight), + PROCESSING_KEY_CONTEXT_MENU => Ok(KeyCode::ContextMenu), + _ => Err(ProcessingError::InvalidArgument( + format!("unknown key code: {val}"), + )), + } +} + +fn key_code_to_u32(kc: KeyCode) -> u32 { + match kc { + KeyCode::Space => PROCESSING_KEY_SPACE, + KeyCode::Quote => PROCESSING_KEY_QUOTE, + KeyCode::Comma => PROCESSING_KEY_COMMA, + KeyCode::Minus => PROCESSING_KEY_MINUS, + KeyCode::Period => PROCESSING_KEY_PERIOD, + KeyCode::Slash => PROCESSING_KEY_SLASH, + KeyCode::Digit0 => PROCESSING_KEY_0, + KeyCode::Digit1 => PROCESSING_KEY_1, + KeyCode::Digit2 => PROCESSING_KEY_2, + KeyCode::Digit3 => PROCESSING_KEY_3, + KeyCode::Digit4 => PROCESSING_KEY_4, + KeyCode::Digit5 => PROCESSING_KEY_5, + KeyCode::Digit6 => PROCESSING_KEY_6, + KeyCode::Digit7 => PROCESSING_KEY_7, + KeyCode::Digit8 => PROCESSING_KEY_8, + KeyCode::Digit9 => PROCESSING_KEY_9, + KeyCode::Semicolon => PROCESSING_KEY_SEMICOLON, + KeyCode::Equal => PROCESSING_KEY_EQUAL, + KeyCode::KeyA => PROCESSING_KEY_A, + KeyCode::KeyB => PROCESSING_KEY_B, + KeyCode::KeyC => PROCESSING_KEY_C, + KeyCode::KeyD => PROCESSING_KEY_D, + KeyCode::KeyE => PROCESSING_KEY_E, + KeyCode::KeyF => PROCESSING_KEY_F, + KeyCode::KeyG => PROCESSING_KEY_G, + KeyCode::KeyH => PROCESSING_KEY_H, + KeyCode::KeyI => PROCESSING_KEY_I, + KeyCode::KeyJ => PROCESSING_KEY_J, + KeyCode::KeyK => PROCESSING_KEY_K, + KeyCode::KeyL => PROCESSING_KEY_L, + KeyCode::KeyM => PROCESSING_KEY_M, + KeyCode::KeyN => PROCESSING_KEY_N, + KeyCode::KeyO => PROCESSING_KEY_O, + KeyCode::KeyP => PROCESSING_KEY_P, + KeyCode::KeyQ => PROCESSING_KEY_Q, + KeyCode::KeyR => PROCESSING_KEY_R, + KeyCode::KeyS => PROCESSING_KEY_S, + KeyCode::KeyT => PROCESSING_KEY_T, + KeyCode::KeyU => PROCESSING_KEY_U, + KeyCode::KeyV => PROCESSING_KEY_V, + KeyCode::KeyW => PROCESSING_KEY_W, + KeyCode::KeyX => PROCESSING_KEY_X, + KeyCode::KeyY => PROCESSING_KEY_Y, + KeyCode::KeyZ => PROCESSING_KEY_Z, + KeyCode::BracketLeft => PROCESSING_KEY_BRACKET_LEFT, + KeyCode::Backslash => PROCESSING_KEY_BACKSLASH, + KeyCode::BracketRight => PROCESSING_KEY_BRACKET_RIGHT, + KeyCode::Backquote => PROCESSING_KEY_BACKQUOTE, + KeyCode::Escape => PROCESSING_KEY_ESCAPE, + KeyCode::Enter => PROCESSING_KEY_ENTER, + KeyCode::Tab => PROCESSING_KEY_TAB, + KeyCode::Backspace => PROCESSING_KEY_BACKSPACE, + KeyCode::Insert => PROCESSING_KEY_INSERT, + KeyCode::Delete => PROCESSING_KEY_DELETE, + KeyCode::ArrowRight => PROCESSING_KEY_RIGHT, + KeyCode::ArrowLeft => PROCESSING_KEY_LEFT, + KeyCode::ArrowDown => PROCESSING_KEY_DOWN, + KeyCode::ArrowUp => PROCESSING_KEY_UP, + KeyCode::PageUp => PROCESSING_KEY_PAGE_UP, + KeyCode::PageDown => PROCESSING_KEY_PAGE_DOWN, + KeyCode::Home => PROCESSING_KEY_HOME, + KeyCode::End => PROCESSING_KEY_END, + KeyCode::CapsLock => PROCESSING_KEY_CAPS_LOCK, + KeyCode::ScrollLock => PROCESSING_KEY_SCROLL_LOCK, + KeyCode::NumLock => PROCESSING_KEY_NUM_LOCK, + KeyCode::PrintScreen => PROCESSING_KEY_PRINT_SCREEN, + KeyCode::Pause => PROCESSING_KEY_PAUSE, + KeyCode::F1 => PROCESSING_KEY_F1, + KeyCode::F2 => PROCESSING_KEY_F2, + KeyCode::F3 => PROCESSING_KEY_F3, + KeyCode::F4 => PROCESSING_KEY_F4, + KeyCode::F5 => PROCESSING_KEY_F5, + KeyCode::F6 => PROCESSING_KEY_F6, + KeyCode::F7 => PROCESSING_KEY_F7, + KeyCode::F8 => PROCESSING_KEY_F8, + KeyCode::F9 => PROCESSING_KEY_F9, + KeyCode::F10 => PROCESSING_KEY_F10, + KeyCode::F11 => PROCESSING_KEY_F11, + KeyCode::F12 => PROCESSING_KEY_F12, + KeyCode::Numpad0 => PROCESSING_KEY_NUMPAD_0, + KeyCode::Numpad1 => PROCESSING_KEY_NUMPAD_1, + KeyCode::Numpad2 => PROCESSING_KEY_NUMPAD_2, + KeyCode::Numpad3 => PROCESSING_KEY_NUMPAD_3, + KeyCode::Numpad4 => PROCESSING_KEY_NUMPAD_4, + KeyCode::Numpad5 => PROCESSING_KEY_NUMPAD_5, + KeyCode::Numpad6 => PROCESSING_KEY_NUMPAD_6, + KeyCode::Numpad7 => PROCESSING_KEY_NUMPAD_7, + KeyCode::Numpad8 => PROCESSING_KEY_NUMPAD_8, + KeyCode::Numpad9 => PROCESSING_KEY_NUMPAD_9, + KeyCode::NumpadDecimal => PROCESSING_KEY_NUMPAD_DECIMAL, + KeyCode::NumpadDivide => PROCESSING_KEY_NUMPAD_DIVIDE, + KeyCode::NumpadMultiply => PROCESSING_KEY_NUMPAD_MULTIPLY, + KeyCode::NumpadSubtract => PROCESSING_KEY_NUMPAD_SUBTRACT, + KeyCode::NumpadAdd => PROCESSING_KEY_NUMPAD_ADD, + KeyCode::NumpadEnter => PROCESSING_KEY_NUMPAD_ENTER, + KeyCode::NumpadEqual => PROCESSING_KEY_NUMPAD_EQUAL, + KeyCode::ShiftLeft => PROCESSING_KEY_SHIFT_LEFT, + KeyCode::ControlLeft => PROCESSING_KEY_CONTROL_LEFT, + KeyCode::AltLeft => PROCESSING_KEY_ALT_LEFT, + KeyCode::SuperLeft => PROCESSING_KEY_SUPER_LEFT, + KeyCode::ShiftRight => PROCESSING_KEY_SHIFT_RIGHT, + KeyCode::ControlRight => PROCESSING_KEY_CONTROL_RIGHT, + KeyCode::AltRight => PROCESSING_KEY_ALT_RIGHT, + KeyCode::SuperRight => PROCESSING_KEY_SUPER_RIGHT, + KeyCode::ContextMenu => PROCESSING_KEY_CONTEXT_MENU, + _ => 0, + } +} diff --git a/crates/processing_glfw/Cargo.toml b/crates/processing_glfw/Cargo.toml new file mode 100644 index 0000000..b2e788b --- /dev/null +++ b/crates/processing_glfw/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "processing_glfw" +version = "0.0.1" +edition = "2024" + +[lints] +workspace = true + +[features] +wayland = ["glfw/wayland"] + +[dependencies] +bevy = { workspace = true } +glfw = "0.60.0" +processing_core = { workspace = true } +processing_input = { workspace = true } +processing_render = { workspace = true } diff --git a/crates/processing_glfw/src/lib.rs b/crates/processing_glfw/src/lib.rs new file mode 100644 index 0000000..635a1d3 --- /dev/null +++ b/crates/processing_glfw/src/lib.rs @@ -0,0 +1,313 @@ +use bevy::input::keyboard::{KeyCode, NativeKeyCode}; +use bevy::input::mouse::MouseButton; +use bevy::prelude::Entity; +use glfw::{Action, Glfw, GlfwReceiver, PWindow, WindowEvent, WindowMode}; +use processing_core::error::Result; +use processing_input::{ + input_flush, input_set_char, input_set_cursor_enter, input_set_cursor_leave, input_set_focus, + input_set_key, input_set_mouse_button, input_set_mouse_move, input_set_scroll, +}; + +pub struct GlfwContext { + glfw: Glfw, + window: PWindow, + events: GlfwReceiver<(f64, WindowEvent)>, + surface: Option, + scale_factor: f32, +} + +impl GlfwContext { + pub fn new(width: u32, height: u32) -> Result { + let mut glfw = glfw::init(glfw::fail_on_errors).unwrap(); + + glfw.window_hint(glfw::WindowHint::ClientApi(glfw::ClientApiHint::NoApi)); + glfw.window_hint(glfw::WindowHint::Visible(false)); + + let (mut window, events) = glfw + .create_window(width, height, "Processing", WindowMode::Windowed) + .unwrap(); + + window.set_all_polling(true); + window.show(); + + let (scale_factor, _) = window.get_content_scale(); + + Ok(Self { + glfw, + window, + events, + surface: None, + scale_factor, + }) + } + + #[cfg(target_os = "macos")] + pub fn create_surface(&mut self, width: u32, height: u32) -> Result { + use processing_render::surface_create_macos; + let (scale_factor, _) = self.window.get_content_scale(); + let entity = surface_create_macos( + self.window.get_cocoa_window() as u64, + width, + height, + scale_factor, + )?; + self.surface = Some(entity); + Ok(entity) + } + + #[cfg(target_os = "windows")] + pub fn create_surface(&mut self, width: u32, height: u32) -> Result { + use processing_render::surface_create_windows; + let (scale_factor, _) = self.window.get_content_scale(); + let entity = surface_create_windows( + self.window.get_win32_window() as u64, + width, + height, + scale_factor, + )?; + self.surface = Some(entity); + Ok(entity) + } + + #[cfg(all(target_os = "linux", feature = "wayland"))] + pub fn create_surface(&mut self, width: u32, height: u32) -> Result { + use processing_render::surface_create_wayland; + let (scale_factor, _) = self.window.get_content_scale(); + let entity = surface_create_wayland( + self.window.get_wayland_window() as u64, + self.glfw.get_wayland_display() as u64, + width, + height, + scale_factor, + )?; + self.surface = Some(entity); + Ok(entity) + } + + #[cfg(all(target_os = "linux", feature = "x11"))] + pub fn create_surface(&mut self, width: u32, height: u32) -> Result { + use processing_render::surface_create_x11; + let (scale_factor, _) = self.window.get_content_scale(); + let entity = surface_create_x11( + self.window.get_x11_window() as u64, + self.glfw.get_x11_display() as u64, + width, + height, + scale_factor, + )?; + self.surface = Some(entity); + Ok(entity) + } + + pub fn poll_events(&mut self) -> bool { + self.glfw.poll_events(); + + let surface = match self.surface { + Some(s) => s, + None => { + for (_, event) in glfw::flush_messages(&self.events) { + if event == WindowEvent::Close { + self.window.hide(); + return false; + } + } + if self.window.should_close() { + self.window.hide(); + return false; + } + return true; + } + }; + + for (_, event) in glfw::flush_messages(&self.events) { + match event { + WindowEvent::Close => { + self.window.hide(); + return false; + } + WindowEvent::CursorPos(x, y) => { + let s = self.scale_factor; + input_set_mouse_move(surface, x as f32 / s, y as f32 / s).unwrap(); + } + WindowEvent::MouseButton(button, action, _mods) => { + if let Some(btn) = glfw_button_to_bevy(button) { + input_set_mouse_button(surface, btn, action == Action::Press).unwrap(); + } + } + WindowEvent::Scroll(x, y) => { + input_set_scroll(surface, x as f32, y as f32).unwrap(); + } + WindowEvent::Key(key, _scancode, action, _mods) => { + if let Some(kc) = glfw_key_to_bevy(key) { + input_set_key( + surface, + kc, + action == Action::Press || action == Action::Repeat, + ) + .unwrap(); + } + } + WindowEvent::Char(ch) => { + input_set_char( + surface, + KeyCode::Unidentified(NativeKeyCode::Unidentified), + ch, + ) + .unwrap(); + } + WindowEvent::CursorEnter(true) => { + input_set_cursor_enter(surface).unwrap(); + } + WindowEvent::CursorEnter(false) => { + input_set_cursor_leave(surface).unwrap(); + } + WindowEvent::Focus(focused) => { + input_set_focus(surface, focused).unwrap(); + } + _ => {} + } + } + + if self.window.should_close() { + self.window.hide(); + return false; + } + + input_flush().unwrap(); + + true + } +} + +fn glfw_button_to_bevy(button: glfw::MouseButton) -> Option { + match button { + glfw::MouseButtonLeft => Some(MouseButton::Left), + glfw::MouseButtonRight => Some(MouseButton::Right), + glfw::MouseButtonMiddle => Some(MouseButton::Middle), + _ => None, + } +} + +fn glfw_key_to_bevy(key: glfw::Key) -> Option { + match key { + glfw::Key::Space => Some(KeyCode::Space), + glfw::Key::Apostrophe => Some(KeyCode::Quote), + glfw::Key::Comma => Some(KeyCode::Comma), + glfw::Key::Minus => Some(KeyCode::Minus), + glfw::Key::Period => Some(KeyCode::Period), + glfw::Key::Slash => Some(KeyCode::Slash), + glfw::Key::Num0 => Some(KeyCode::Digit0), + glfw::Key::Num1 => Some(KeyCode::Digit1), + glfw::Key::Num2 => Some(KeyCode::Digit2), + glfw::Key::Num3 => Some(KeyCode::Digit3), + glfw::Key::Num4 => Some(KeyCode::Digit4), + glfw::Key::Num5 => Some(KeyCode::Digit5), + glfw::Key::Num6 => Some(KeyCode::Digit6), + glfw::Key::Num7 => Some(KeyCode::Digit7), + glfw::Key::Num8 => Some(KeyCode::Digit8), + glfw::Key::Num9 => Some(KeyCode::Digit9), + glfw::Key::Semicolon => Some(KeyCode::Semicolon), + glfw::Key::Equal => Some(KeyCode::Equal), + glfw::Key::A => Some(KeyCode::KeyA), + glfw::Key::B => Some(KeyCode::KeyB), + glfw::Key::C => Some(KeyCode::KeyC), + glfw::Key::D => Some(KeyCode::KeyD), + glfw::Key::E => Some(KeyCode::KeyE), + glfw::Key::F => Some(KeyCode::KeyF), + glfw::Key::G => Some(KeyCode::KeyG), + glfw::Key::H => Some(KeyCode::KeyH), + glfw::Key::I => Some(KeyCode::KeyI), + glfw::Key::J => Some(KeyCode::KeyJ), + glfw::Key::K => Some(KeyCode::KeyK), + glfw::Key::L => Some(KeyCode::KeyL), + glfw::Key::M => Some(KeyCode::KeyM), + glfw::Key::N => Some(KeyCode::KeyN), + glfw::Key::O => Some(KeyCode::KeyO), + glfw::Key::P => Some(KeyCode::KeyP), + glfw::Key::Q => Some(KeyCode::KeyQ), + glfw::Key::R => Some(KeyCode::KeyR), + glfw::Key::S => Some(KeyCode::KeyS), + glfw::Key::T => Some(KeyCode::KeyT), + glfw::Key::U => Some(KeyCode::KeyU), + glfw::Key::V => Some(KeyCode::KeyV), + glfw::Key::W => Some(KeyCode::KeyW), + glfw::Key::X => Some(KeyCode::KeyX), + glfw::Key::Y => Some(KeyCode::KeyY), + glfw::Key::Z => Some(KeyCode::KeyZ), + glfw::Key::LeftBracket => Some(KeyCode::BracketLeft), + glfw::Key::Backslash => Some(KeyCode::Backslash), + glfw::Key::RightBracket => Some(KeyCode::BracketRight), + glfw::Key::GraveAccent => Some(KeyCode::Backquote), + glfw::Key::Escape => Some(KeyCode::Escape), + glfw::Key::Enter => Some(KeyCode::Enter), + glfw::Key::Tab => Some(KeyCode::Tab), + glfw::Key::Backspace => Some(KeyCode::Backspace), + glfw::Key::Insert => Some(KeyCode::Insert), + glfw::Key::Delete => Some(KeyCode::Delete), + glfw::Key::Right => Some(KeyCode::ArrowRight), + glfw::Key::Left => Some(KeyCode::ArrowLeft), + glfw::Key::Down => Some(KeyCode::ArrowDown), + glfw::Key::Up => Some(KeyCode::ArrowUp), + glfw::Key::PageUp => Some(KeyCode::PageUp), + glfw::Key::PageDown => Some(KeyCode::PageDown), + glfw::Key::Home => Some(KeyCode::Home), + glfw::Key::End => Some(KeyCode::End), + glfw::Key::CapsLock => Some(KeyCode::CapsLock), + glfw::Key::ScrollLock => Some(KeyCode::ScrollLock), + glfw::Key::NumLock => Some(KeyCode::NumLock), + glfw::Key::PrintScreen => Some(KeyCode::PrintScreen), + glfw::Key::Pause => Some(KeyCode::Pause), + glfw::Key::F1 => Some(KeyCode::F1), + glfw::Key::F2 => Some(KeyCode::F2), + glfw::Key::F3 => Some(KeyCode::F3), + glfw::Key::F4 => Some(KeyCode::F4), + glfw::Key::F5 => Some(KeyCode::F5), + glfw::Key::F6 => Some(KeyCode::F6), + glfw::Key::F7 => Some(KeyCode::F7), + glfw::Key::F8 => Some(KeyCode::F8), + glfw::Key::F9 => Some(KeyCode::F9), + glfw::Key::F10 => Some(KeyCode::F10), + glfw::Key::F11 => Some(KeyCode::F11), + glfw::Key::F12 => Some(KeyCode::F12), + glfw::Key::F13 => Some(KeyCode::F13), + glfw::Key::F14 => Some(KeyCode::F14), + glfw::Key::F15 => Some(KeyCode::F15), + glfw::Key::F16 => Some(KeyCode::F16), + glfw::Key::F17 => Some(KeyCode::F17), + glfw::Key::F18 => Some(KeyCode::F18), + glfw::Key::F19 => Some(KeyCode::F19), + glfw::Key::F20 => Some(KeyCode::F20), + glfw::Key::F21 => Some(KeyCode::F21), + glfw::Key::F22 => Some(KeyCode::F22), + glfw::Key::F23 => Some(KeyCode::F23), + glfw::Key::F24 => Some(KeyCode::F24), + glfw::Key::F25 => Some(KeyCode::F25), + glfw::Key::Kp0 => Some(KeyCode::Numpad0), + glfw::Key::Kp1 => Some(KeyCode::Numpad1), + glfw::Key::Kp2 => Some(KeyCode::Numpad2), + glfw::Key::Kp3 => Some(KeyCode::Numpad3), + glfw::Key::Kp4 => Some(KeyCode::Numpad4), + glfw::Key::Kp5 => Some(KeyCode::Numpad5), + glfw::Key::Kp6 => Some(KeyCode::Numpad6), + glfw::Key::Kp7 => Some(KeyCode::Numpad7), + glfw::Key::Kp8 => Some(KeyCode::Numpad8), + glfw::Key::Kp9 => Some(KeyCode::Numpad9), + glfw::Key::KpDecimal => Some(KeyCode::NumpadDecimal), + glfw::Key::KpDivide => Some(KeyCode::NumpadDivide), + glfw::Key::KpMultiply => Some(KeyCode::NumpadMultiply), + glfw::Key::KpSubtract => Some(KeyCode::NumpadSubtract), + glfw::Key::KpAdd => Some(KeyCode::NumpadAdd), + glfw::Key::KpEnter => Some(KeyCode::NumpadEnter), + glfw::Key::KpEqual => Some(KeyCode::NumpadEqual), + glfw::Key::LeftShift => Some(KeyCode::ShiftLeft), + glfw::Key::LeftControl => Some(KeyCode::ControlLeft), + glfw::Key::LeftAlt => Some(KeyCode::AltLeft), + glfw::Key::LeftSuper => Some(KeyCode::SuperLeft), + glfw::Key::RightShift => Some(KeyCode::ShiftRight), + glfw::Key::RightControl => Some(KeyCode::ControlRight), + glfw::Key::RightAlt => Some(KeyCode::AltRight), + glfw::Key::RightSuper => Some(KeyCode::SuperRight), + glfw::Key::Menu => Some(KeyCode::ContextMenu), + _ => None, + } +} diff --git a/crates/processing_input/Cargo.toml b/crates/processing_input/Cargo.toml new file mode 100644 index 0000000..f06e6e2 --- /dev/null +++ b/crates/processing_input/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "processing_input" +version = "0.0.1" +edition = "2024" + +[lints] +workspace = true + +[dependencies] +bevy = { workspace = true } +processing_core = { workspace = true } diff --git a/crates/processing_input/src/lib.rs b/crates/processing_input/src/lib.rs new file mode 100644 index 0000000..1148d4a --- /dev/null +++ b/crates/processing_input/src/lib.rs @@ -0,0 +1,259 @@ +pub mod state; + +use bevy::input::ButtonState; +use bevy::input::keyboard::{Key, KeyCode, KeyboardInput, NativeKey}; +use bevy::input::mouse::{ + AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion, + MouseScrollUnit, MouseWheel, +}; +use bevy::prelude::*; +use bevy::window::CursorMoved; + +use processing_core::app_mut; +use processing_core::error; + +pub use state::{CursorPosition, LastKey, LastMouseButton}; + +pub struct InputPlugin; + +impl Plugin for InputPlugin { + fn build(&self, app: &mut App) { + app.init_resource::() + .init_resource::() + .add_systems( + PreUpdate, + ( + state::snapshot_cursor, + ( + state::track_cursor_position, + state::track_last_key, + state::track_last_mouse_button, + ) + .after(state::snapshot_cursor), + ), + ); + } +} + +pub fn input_set_mouse_move(surface: Entity, x: f32, y: f32) -> error::Result<()> { + app_mut(|app| { + let world = app.world_mut(); + let new_pos = Vec2::new(x, y); + + let delta = if let Some(cursor) = world.get::(surface) { + let d = new_pos - cursor.current(); + Some(d) + } else { + world.entity_mut(surface).insert(CursorPosition::default()); + None + }; + + world.write_message(CursorMoved { + window: surface, + position: new_pos, + delta, + }); + + if let Some(d) = delta { + world.write_message(MouseMotion { delta: d }); + } + + Ok(()) + }) +} + +pub fn input_set_mouse_button( + surface: Entity, + button: MouseButton, + pressed: bool, +) -> error::Result<()> { + app_mut(|app| { + app.world_mut().write_message(MouseButtonInput { + button, + state: if pressed { + ButtonState::Pressed + } else { + ButtonState::Released + }, + window: surface, + }); + Ok(()) + }) +} + +pub fn input_set_scroll(surface: Entity, x: f32, y: f32) -> error::Result<()> { + app_mut(|app| { + app.world_mut().write_message(MouseWheel { + unit: MouseScrollUnit::Pixel, + x, + y, + window: surface, + }); + Ok(()) + }) +} + +pub fn input_set_key(surface: Entity, key_code: KeyCode, pressed: bool) -> error::Result<()> { + app_mut(|app| { + app.world_mut().write_message(KeyboardInput { + key_code, + logical_key: Key::Unidentified(NativeKey::Unidentified), + state: if pressed { + ButtonState::Pressed + } else { + ButtonState::Released + }, + text: None, + repeat: false, + window: surface, + }); + Ok(()) + }) +} + +pub fn input_set_char(surface: Entity, key_code: KeyCode, character: char) -> error::Result<()> { + app_mut(|app| { + let text = String::from(character); + app.world_mut().write_message(KeyboardInput { + key_code, + logical_key: Key::Character(text.clone().into()), + state: ButtonState::Pressed, + text: Some(text.into()), + repeat: false, + window: surface, + }); + Ok(()) + }) +} + +pub fn input_set_cursor_enter(surface: Entity) -> error::Result<()> { + app_mut(|app| { + app.world_mut() + .write_message(bevy::window::CursorEntered { window: surface }); + Ok(()) + }) +} + +pub fn input_set_cursor_leave(surface: Entity) -> error::Result<()> { + app_mut(|app| { + app.world_mut() + .write_message(bevy::window::CursorLeft { window: surface }); + Ok(()) + }) +} + +pub fn input_set_focus(surface: Entity, focused: bool) -> error::Result<()> { + app_mut(|app| { + app.world_mut().write_message(bevy::window::WindowFocused { + window: surface, + focused, + }); + Ok(()) + }) +} + +pub fn input_flush() -> error::Result<()> { + app_mut(|app| { + app.world_mut().run_schedule(PreUpdate); + Ok(()) + }) +} + +pub fn input_mouse_x(surface: Entity) -> error::Result { + app_mut(|app| { + let pos = app + .world() + .get::(surface) + .map(|c| c.current()) + .unwrap_or(Vec2::ZERO); + Ok(pos.x) + }) +} + +pub fn input_mouse_y(surface: Entity) -> error::Result { + app_mut(|app| { + let pos = app + .world() + .get::(surface) + .map(|c| c.current()) + .unwrap_or(Vec2::ZERO); + Ok(pos.y) + }) +} + +pub fn input_pmouse_x(surface: Entity) -> error::Result { + app_mut(|app| { + let pos = app + .world() + .get::(surface) + .map(|c| c.previous()) + .unwrap_or(Vec2::ZERO); + Ok(pos.x) + }) +} + +pub fn input_pmouse_y(surface: Entity) -> error::Result { + app_mut(|app| { + let pos = app + .world() + .get::(surface) + .map(|c| c.previous()) + .unwrap_or(Vec2::ZERO); + Ok(pos.y) + }) +} + +pub fn input_mouse_is_pressed() -> error::Result { + app_mut(|app| { + Ok(app + .world() + .resource::>() + .get_pressed() + .next() + .is_some()) + }) +} + +pub fn input_mouse_button() -> error::Result> { + app_mut(|app| Ok(app.world().resource::().button)) +} + +pub fn input_moved_x() -> error::Result { + app_mut(|app| Ok(app.world().resource::().delta.x)) +} + +pub fn input_moved_y() -> error::Result { + app_mut(|app| Ok(app.world().resource::().delta.y)) +} + +pub fn input_mouse_wheel() -> error::Result { + app_mut(|app| Ok(app.world().resource::().delta.y)) +} + +pub fn input_key_is_pressed() -> error::Result { + app_mut(|app| { + Ok(app + .world() + .resource::>() + .get_pressed() + .next() + .is_some()) + }) +} + +pub fn input_key_is_down(key_code: KeyCode) -> error::Result { + app_mut(|app| { + Ok(app + .world() + .resource::>() + .pressed(key_code)) + }) +} + +pub fn input_key() -> error::Result> { + app_mut(|app| Ok(app.world().resource::().character)) +} + +pub fn input_key_code() -> error::Result> { + app_mut(|app| Ok(app.world().resource::().code)) +} diff --git a/crates/processing_input/src/state.rs b/crates/processing_input/src/state.rs new file mode 100644 index 0000000..1db8f3d --- /dev/null +++ b/crates/processing_input/src/state.rs @@ -0,0 +1,75 @@ +use bevy::input::ButtonState; +use bevy::input::keyboard::KeyboardInput; +use bevy::input::mouse::{MouseButton, MouseButtonInput}; +use bevy::prelude::*; +use bevy::window::CursorMoved; + +use bevy::input::keyboard::KeyCode; + +#[derive(Component, Default)] +pub struct CursorPosition { + current: Vec2, + previous: Vec2, +} + +impl CursorPosition { + pub fn current(&self) -> Vec2 { + self.current + } + + pub fn previous(&self) -> Vec2 { + self.previous + } +} + +#[derive(Resource, Default)] +pub struct LastKey { + pub code: Option, + pub character: Option, +} + +#[derive(Resource, Default)] +pub struct LastMouseButton { + pub button: Option, +} + +pub fn snapshot_cursor(mut query: Query<&mut CursorPosition>) { + for mut cursor in &mut query { + cursor.previous = cursor.current; + } +} + +pub fn track_cursor_position( + mut reader: MessageReader, + mut query: Query<&mut CursorPosition>, +) { + for event in reader.read() { + if let Ok(mut cursor) = query.get_mut(event.window) { + cursor.current = event.position; + } + } +} + +pub fn track_last_key(mut reader: MessageReader, mut last: ResMut) { + if let Some(event) = reader + .read() + .filter(|e| e.state == ButtonState::Pressed) + .last() + { + last.code = Some(event.key_code); + last.character = event.text.as_ref().and_then(|t| t.chars().next()); + } +} + +pub fn track_last_mouse_button( + mut reader: MessageReader, + mut last: ResMut, +) { + if let Some(event) = reader + .read() + .filter(|e| e.state == ButtonState::Pressed) + .last() + { + last.button = Some(event.button); + } +} diff --git a/crates/processing_pyo3/Cargo.toml b/crates/processing_pyo3/Cargo.toml index 6bb468b..f190b38 100644 --- a/crates/processing_pyo3/Cargo.toml +++ b/crates/processing_pyo3/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib"] [features] default = ["wayland"] -wayland = ["processing/wayland", "glfw/wayland"] +wayland = ["processing/wayland", "processing_glfw/wayland"] x11 = ["processing/x11"] webcam = ["processing/webcam", "dep:processing_webcam"] @@ -20,6 +20,6 @@ webcam = ["processing/webcam", "dep:processing_webcam"] pyo3 = { workspace = true, features = ["experimental-inspect"] } processing = { workspace = true } processing_webcam = { workspace = true, optional = true } +processing_glfw = { workspace = true } bevy = { workspace = true, features = ["file_watcher"] } -glfw = { version = "0.60.0", features = ["static-link"] } png = "0.18" diff --git a/crates/processing_pyo3/examples/input.py b/crates/processing_pyo3/examples/input.py new file mode 100644 index 0000000..a1776fe --- /dev/null +++ b/crates/processing_pyo3/examples/input.py @@ -0,0 +1,21 @@ +from mewnala import * + +def setup(): + size(400, 400) + +def draw(): + background(220) + no_stroke() + + if mouse_is_pressed: + fill(200, 50, 50) + else: + fill(50, 130, 200) + + rect(mouse_x - 25, mouse_y - 25, 50, 50) + + if key_is_pressed: + if key_is_down(ESCAPE): + return + +run() diff --git a/crates/processing_pyo3/src/glfw.rs b/crates/processing_pyo3/src/glfw.rs index 37a15d2..a758858 100644 --- a/crates/processing_pyo3/src/glfw.rs +++ b/crates/processing_pyo3/src/glfw.rs @@ -1,100 +1 @@ -use bevy::prelude::Entity; -/// Minimal GLFW helper for Processing examples -use glfw::{Glfw, GlfwReceiver, PWindow, WindowEvent, WindowMode}; -use processing::prelude::error::Result; - -pub struct GlfwContext { - glfw: Glfw, - window: PWindow, - events: GlfwReceiver<(f64, WindowEvent)>, -} - -impl GlfwContext { - pub fn new(width: u32, height: u32) -> Result { - let mut glfw = glfw::init(glfw::fail_on_errors).unwrap(); - - glfw.window_hint(glfw::WindowHint::ClientApi(glfw::ClientApiHint::NoApi)); - glfw.window_hint(glfw::WindowHint::Visible(false)); - - let (mut window, events) = glfw - .create_window(width, height, "Processing", WindowMode::Windowed) - .unwrap(); - - window.set_all_polling(true); - window.show(); - - Ok(Self { - glfw, - window, - events, - }) - } - - #[cfg(target_os = "macos")] - pub fn create_surface(&self, width: u32, height: u32) -> Result { - use processing::prelude::surface_create_macos; - let (scale_factor, _) = self.window.get_content_scale(); - surface_create_macos( - self.window.get_cocoa_window() as u64, - width, - height, - scale_factor, - ) - } - - #[cfg(target_os = "windows")] - pub fn create_surface(&self, width: u32, height: u32) -> Result { - use processing::prelude::surface_create_windows; - let (scale_factor, _) = self.window.get_content_scale(); - surface_create_windows( - self.window.get_win32_window() as u64, - width, - height, - scale_factor, - ) - } - - #[cfg(all(target_os = "linux", feature = "wayland"))] - pub fn create_surface(&self, width: u32, height: u32) -> Result { - use processing::prelude::surface_create_wayland; - let (scale_factor, _) = self.window.get_content_scale(); - surface_create_wayland( - self.window.get_wayland_window() as u64, - self.glfw.get_wayland_display() as u64, - width, - height, - scale_factor, - ) - } - - #[cfg(all(target_os = "linux", feature = "x11"))] - pub fn create_surface(&self, width: u32, height: u32) -> Result { - use processing::prelude::surface_create_x11; - let (scale_factor, _) = self.window.get_content_scale(); - surface_create_x11( - self.window.get_x11_window() as u64, - self.glfw.get_x11_display() as u64, - width, - height, - scale_factor, - ) - } - - pub fn poll_events(&mut self) -> bool { - self.glfw.poll_events(); - - for (_, event) in glfw::flush_messages(&self.events) { - if event == WindowEvent::Close { - self.window.hide(); - return false; - } - } - - if self.window.should_close() { - self.window.hide(); - return false; - } - - true - } -} +pub use processing_glfw::GlfwContext; diff --git a/crates/processing_pyo3/src/graphics.rs b/crates/processing_pyo3/src/graphics.rs index d8d8929..4cde880 100644 --- a/crates/processing_pyo3/src/graphics.rs +++ b/crates/processing_pyo3/src/graphics.rs @@ -12,11 +12,12 @@ use pyo3::{ }; use crate::glfw::GlfwContext; +use crate::input; use crate::math::{extract_vec2, extract_vec3, extract_vec4}; #[pyclass(unsendable)] pub struct Surface { - entity: Entity, + pub(crate) entity: Entity, glfw_ctx: Option, } @@ -184,7 +185,7 @@ impl Graphics { sketch_file_name: &str, log_level: Option<&str>, ) -> PyResult { - let glfw_ctx = + let mut glfw_ctx = GlfwContext::new(width, height).map_err(|e| PyRuntimeError::new_err(format!("{e}")))?; let mut config = Config::new(); @@ -574,6 +575,66 @@ impl Graphics { } } + #[getter] + fn mouse_x(&self) -> PyResult { + input::mouse_x(self.surface.entity) + } + + #[getter] + fn mouse_y(&self) -> PyResult { + input::mouse_y(self.surface.entity) + } + + #[getter] + fn pmouse_x(&self) -> PyResult { + input::pmouse_x(self.surface.entity) + } + + #[getter] + fn pmouse_y(&self) -> PyResult { + input::pmouse_y(self.surface.entity) + } + + #[getter] + fn mouse_is_pressed(&self) -> PyResult { + input::mouse_is_pressed() + } + + #[getter] + fn mouse_button(&self) -> PyResult> { + input::mouse_button() + } + + #[getter] + fn moved_x(&self) -> PyResult { + input::moved_x() + } + + #[getter] + fn moved_y(&self) -> PyResult { + input::moved_y() + } + + #[getter] + fn mouse_wheel(&self) -> PyResult { + input::mouse_wheel() + } + + #[getter] + fn key(&self) -> PyResult> { + input::key() + } + + #[getter] + fn key_code(&self) -> PyResult> { + input::key_code() + } + + #[getter] + fn key_is_pressed(&self) -> PyResult { + input::key_is_pressed() + } + pub fn light_spot( &self, r: f32, diff --git a/crates/processing_pyo3/src/input.rs b/crates/processing_pyo3/src/input.rs new file mode 100644 index 0000000..ab51669 --- /dev/null +++ b/crates/processing_pyo3/src/input.rs @@ -0,0 +1,214 @@ +use Entity; +use processing::prelude::*; +use pyo3::{ + exceptions::{PyRuntimeError, PyValueError}, + prelude::*, +}; + +pub fn mouse_x(surface: Entity) -> PyResult { + processing::prelude::input_mouse_x(surface).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn mouse_y(surface: Entity) -> PyResult { + processing::prelude::input_mouse_y(surface).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn pmouse_x(surface: Entity) -> PyResult { + processing::prelude::input_pmouse_x(surface) + .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn pmouse_y(surface: Entity) -> PyResult { + processing::prelude::input_pmouse_y(surface) + .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn mouse_is_pressed() -> PyResult { + processing::prelude::input_mouse_is_pressed() + .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn mouse_button() -> PyResult> { + processing::prelude::input_mouse_button() + .map(|opt| { + opt.map(|b| match b { + MouseButton::Left => "LEFT".to_string(), + MouseButton::Right => "RIGHT".to_string(), + MouseButton::Middle => "CENTER".to_string(), + _ => format!("{b:?}"), + }) + }) + .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn moved_x() -> PyResult { + processing::prelude::input_moved_x().map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn moved_y() -> PyResult { + processing::prelude::input_moved_y().map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn mouse_wheel() -> PyResult { + processing::prelude::input_mouse_wheel().map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn key_is_pressed() -> PyResult { + processing::prelude::input_key_is_pressed().map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn key_is_down(key_code: u32) -> PyResult { + let kc = u32_to_key_code(key_code)?; + processing::prelude::input_key_is_down(kc).map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn key() -> PyResult> { + processing::prelude::input_key() + .map(|opt| opt.map(String::from)) + .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn key_code() -> PyResult> { + processing::prelude::input_key_code() + .map(|opt| opt.map(key_code_to_u32)) + .map_err(|e| PyRuntimeError::new_err(format!("{e}"))) +} + +pub fn sync_globals(func: &Bound<'_, PyAny>, surface: Entity) -> PyResult<()> { + let globals = func.getattr("__globals__")?; + globals.set_item("mouse_x", mouse_x(surface)?)?; + globals.set_item("mouse_y", mouse_y(surface)?)?; + globals.set_item("pmouse_x", pmouse_x(surface)?)?; + globals.set_item("pmouse_y", pmouse_y(surface)?)?; + globals.set_item("mouse_is_pressed", mouse_is_pressed()?)?; + globals.set_item("mouse_button", mouse_button()?)?; + globals.set_item("moved_x", moved_x()?)?; + globals.set_item("moved_y", moved_y()?)?; + globals.set_item("mouse_wheel", mouse_wheel()?)?; + globals.set_item("key", key()?)?; + globals.set_item("key_code", key_code()?)?; + globals.set_item("key_is_pressed", key_is_pressed()?)?; + Ok(()) +} + +fn u32_to_key_code(val: u32) -> PyResult { + use bevy::input::keyboard::KeyCode; + match val { + 32 => Ok(KeyCode::Space), + 256 => Ok(KeyCode::Escape), + 257 => Ok(KeyCode::Enter), + 258 => Ok(KeyCode::Tab), + 259 => Ok(KeyCode::Backspace), + 261 => Ok(KeyCode::Delete), + 262 => Ok(KeyCode::ArrowRight), + 263 => Ok(KeyCode::ArrowLeft), + 264 => Ok(KeyCode::ArrowDown), + 265 => Ok(KeyCode::ArrowUp), + 340 => Ok(KeyCode::ShiftLeft), + 341 => Ok(KeyCode::ControlLeft), + 342 => Ok(KeyCode::AltLeft), + 343 => Ok(KeyCode::SuperLeft), + 65..=90 => Ok(match val { + 65 => KeyCode::KeyA, + 66 => KeyCode::KeyB, + 67 => KeyCode::KeyC, + 68 => KeyCode::KeyD, + 69 => KeyCode::KeyE, + 70 => KeyCode::KeyF, + 71 => KeyCode::KeyG, + 72 => KeyCode::KeyH, + 73 => KeyCode::KeyI, + 74 => KeyCode::KeyJ, + 75 => KeyCode::KeyK, + 76 => KeyCode::KeyL, + 77 => KeyCode::KeyM, + 78 => KeyCode::KeyN, + 79 => KeyCode::KeyO, + 80 => KeyCode::KeyP, + 81 => KeyCode::KeyQ, + 82 => KeyCode::KeyR, + 83 => KeyCode::KeyS, + 84 => KeyCode::KeyT, + 85 => KeyCode::KeyU, + 86 => KeyCode::KeyV, + 87 => KeyCode::KeyW, + 88 => KeyCode::KeyX, + 89 => KeyCode::KeyY, + 90 => KeyCode::KeyZ, + _ => unreachable!(), + }), + 48..=57 => Ok(match val { + 48 => KeyCode::Digit0, + 49 => KeyCode::Digit1, + 50 => KeyCode::Digit2, + 51 => KeyCode::Digit3, + 52 => KeyCode::Digit4, + 53 => KeyCode::Digit5, + 54 => KeyCode::Digit6, + 55 => KeyCode::Digit7, + 56 => KeyCode::Digit8, + 57 => KeyCode::Digit9, + _ => unreachable!(), + }), + _ => Err(PyValueError::new_err(format!( + "unknown key code: {val}" + ))), + } +} + +fn key_code_to_u32(kc: bevy::input::keyboard::KeyCode) -> u32 { + use bevy::input::keyboard::KeyCode; + match kc { + KeyCode::Space => 32, + KeyCode::Escape => 256, + KeyCode::Enter => 257, + KeyCode::Tab => 258, + KeyCode::Backspace => 259, + KeyCode::Delete => 261, + KeyCode::ArrowRight => 262, + KeyCode::ArrowLeft => 263, + KeyCode::ArrowDown => 264, + KeyCode::ArrowUp => 265, + KeyCode::ShiftLeft => 340, + KeyCode::ControlLeft => 341, + KeyCode::AltLeft => 342, + KeyCode::SuperLeft => 343, + KeyCode::KeyA => 65, + KeyCode::KeyB => 66, + KeyCode::KeyC => 67, + KeyCode::KeyD => 68, + KeyCode::KeyE => 69, + KeyCode::KeyF => 70, + KeyCode::KeyG => 71, + KeyCode::KeyH => 72, + KeyCode::KeyI => 73, + KeyCode::KeyJ => 74, + KeyCode::KeyK => 75, + KeyCode::KeyL => 76, + KeyCode::KeyM => 77, + KeyCode::KeyN => 78, + KeyCode::KeyO => 79, + KeyCode::KeyP => 80, + KeyCode::KeyQ => 81, + KeyCode::KeyR => 82, + KeyCode::KeyS => 83, + KeyCode::KeyT => 84, + KeyCode::KeyU => 85, + KeyCode::KeyV => 86, + KeyCode::KeyW => 87, + KeyCode::KeyX => 88, + KeyCode::KeyY => 89, + KeyCode::KeyZ => 90, + KeyCode::Digit0 => 48, + KeyCode::Digit1 => 49, + KeyCode::Digit2 => 50, + KeyCode::Digit3 => 51, + KeyCode::Digit4 => 52, + KeyCode::Digit5 => 53, + KeyCode::Digit6 => 54, + KeyCode::Digit7 => 55, + KeyCode::Digit8 => 56, + KeyCode::Digit9 => 57, + _ => 0, + } +} diff --git a/crates/processing_pyo3/src/lib.rs b/crates/processing_pyo3/src/lib.rs index 29a0e10..4bcb8e1 100644 --- a/crates/processing_pyo3/src/lib.rs +++ b/crates/processing_pyo3/src/lib.rs @@ -11,6 +11,7 @@ mod glfw; mod gltf; mod graphics; +mod input; pub(crate) mod material; pub(crate) mod math; mod midi; @@ -129,6 +130,7 @@ mod mewnala { #[pymodule_export] use super::Topology; + // Stroke cap/join #[pymodule_export] const ROUND: u8 = 0; #[pymodule_export] @@ -140,6 +142,182 @@ mod mewnala { #[pymodule_export] const BEVEL: u8 = 2; + // Mouse buttons + #[pymodule_export] + const LEFT: u8 = 0; + #[pymodule_export] + const CENTER: u8 = 1; + #[pymodule_export] + const RIGHT: u8 = 2; + + // Letters + #[pymodule_export] + const KEY_A: u32 = 65; + #[pymodule_export] + const KEY_B: u32 = 66; + #[pymodule_export] + const KEY_C: u32 = 67; + #[pymodule_export] + const KEY_D: u32 = 68; + #[pymodule_export] + const KEY_E: u32 = 69; + #[pymodule_export] + const KEY_F: u32 = 70; + #[pymodule_export] + const KEY_G: u32 = 71; + #[pymodule_export] + const KEY_H: u32 = 72; + #[pymodule_export] + const KEY_I: u32 = 73; + #[pymodule_export] + const KEY_J: u32 = 74; + #[pymodule_export] + const KEY_K: u32 = 75; + #[pymodule_export] + const KEY_L: u32 = 76; + #[pymodule_export] + const KEY_M: u32 = 77; + #[pymodule_export] + const KEY_N: u32 = 78; + #[pymodule_export] + const KEY_O: u32 = 79; + #[pymodule_export] + const KEY_P: u32 = 80; + #[pymodule_export] + const KEY_Q: u32 = 81; + #[pymodule_export] + const KEY_R: u32 = 82; + #[pymodule_export] + const KEY_S: u32 = 83; + #[pymodule_export] + const KEY_T: u32 = 84; + #[pymodule_export] + const KEY_U: u32 = 85; + #[pymodule_export] + const KEY_V: u32 = 86; + #[pymodule_export] + const KEY_W: u32 = 87; + #[pymodule_export] + const KEY_X: u32 = 88; + #[pymodule_export] + const KEY_Y: u32 = 89; + #[pymodule_export] + const KEY_Z: u32 = 90; + + // Digits + #[pymodule_export] + const KEY_0: u32 = 48; + #[pymodule_export] + const KEY_1: u32 = 49; + #[pymodule_export] + const KEY_2: u32 = 50; + #[pymodule_export] + const KEY_3: u32 = 51; + #[pymodule_export] + const KEY_4: u32 = 52; + #[pymodule_export] + const KEY_5: u32 = 53; + #[pymodule_export] + const KEY_6: u32 = 54; + #[pymodule_export] + const KEY_7: u32 = 55; + #[pymodule_export] + const KEY_8: u32 = 56; + #[pymodule_export] + const KEY_9: u32 = 57; + + // Punctuation/symbols + #[pymodule_export] + const SPACE: u32 = 32; + #[pymodule_export] + const QUOTE: u32 = 39; + #[pymodule_export] + const COMMA: u32 = 44; + #[pymodule_export] + const MINUS: u32 = 45; + #[pymodule_export] + const PERIOD: u32 = 46; + #[pymodule_export] + const SLASH: u32 = 47; + #[pymodule_export] + const SEMICOLON: u32 = 59; + #[pymodule_export] + const EQUAL: u32 = 61; + #[pymodule_export] + const BRACKET_LEFT: u32 = 91; + #[pymodule_export] + const BACKSLASH: u32 = 92; + #[pymodule_export] + const BRACKET_RIGHT: u32 = 93; + #[pymodule_export] + const BACKQUOTE: u32 = 96; + + // Navigation/editing + #[pymodule_export] + const ESCAPE: u32 = 256; + #[pymodule_export] + const ENTER: u32 = 257; + #[pymodule_export] + const TAB: u32 = 258; + #[pymodule_export] + const BACKSPACE: u32 = 259; + #[pymodule_export] + const INSERT: u32 = 260; + #[pymodule_export] + const DELETE: u32 = 261; + #[pymodule_export] + const UP: u32 = 265; + #[pymodule_export] + const DOWN: u32 = 264; + #[pymodule_export] + const LEFT_ARROW: u32 = 263; + #[pymodule_export] + const RIGHT_ARROW: u32 = 262; + #[pymodule_export] + const PAGE_UP: u32 = 266; + #[pymodule_export] + const PAGE_DOWN: u32 = 267; + #[pymodule_export] + const HOME: u32 = 268; + #[pymodule_export] + const END: u32 = 269; + + // Modifiers + #[pymodule_export] + const SHIFT: u32 = 340; + #[pymodule_export] + const CONTROL: u32 = 341; + #[pymodule_export] + const ALT: u32 = 342; + #[pymodule_export] + const SUPER: u32 = 343; + + // Function keys + #[pymodule_export] + const F1: u32 = 290; + #[pymodule_export] + const F2: u32 = 291; + #[pymodule_export] + const F3: u32 = 292; + #[pymodule_export] + const F4: u32 = 293; + #[pymodule_export] + const F5: u32 = 294; + #[pymodule_export] + const F6: u32 = 295; + #[pymodule_export] + const F7: u32 = 296; + #[pymodule_export] + const F8: u32 = 297; + #[pymodule_export] + const F9: u32 = 298; + #[pymodule_export] + const F10: u32 = 299; + #[pymodule_export] + const F11: u32 = 300; + #[pymodule_export] + const F12: u32 = 301; + #[pymodule] mod math { use super::*; @@ -338,6 +516,12 @@ mod mewnala { // call setup setup_fn.call0()?; + { + let graphics = get_graphics(module)? + .ok_or_else(|| PyRuntimeError::new_err("call size() first"))?; + input::sync_globals(&draw_fn, graphics.surface.entity)?; + } + // start draw loop loop { { @@ -373,6 +557,12 @@ mod mewnala { graphics.begin_draw()?; } + { + let graphics = get_graphics(module)? + .ok_or_else(|| PyRuntimeError::new_err("call size() first"))?; + input::sync_globals(&draw_fn, graphics.surface.entity)?; + } + draw_fn .call0() .map_err(|e| PyRuntimeError::new_err(format!("{e}")))?; @@ -633,4 +823,41 @@ mod mewnala { fn midi_play_notes(note: u8, duration: u64) -> PyResult<()> { midi::play_notes(note, duration) } + + #[pyfunction] + #[pyo3(pass_module)] + fn mouse_x(module: &Bound<'_, PyModule>) -> PyResult { + let graphics = + get_graphics(module)?.ok_or_else(|| PyRuntimeError::new_err("call size() first"))?; + input::mouse_x(graphics.surface.entity) + } + + #[pyfunction] + #[pyo3(pass_module)] + fn mouse_y(module: &Bound<'_, PyModule>) -> PyResult { + let graphics = + get_graphics(module)?.ok_or_else(|| PyRuntimeError::new_err("call size() first"))?; + input::mouse_y(graphics.surface.entity) + } + + #[pyfunction] + #[pyo3(pass_module)] + fn pmouse_x(module: &Bound<'_, PyModule>) -> PyResult { + let graphics = + get_graphics(module)?.ok_or_else(|| PyRuntimeError::new_err("call size() first"))?; + input::pmouse_x(graphics.surface.entity) + } + + #[pyfunction] + #[pyo3(pass_module)] + fn pmouse_y(module: &Bound<'_, PyModule>) -> PyResult { + let graphics = + get_graphics(module)?.ok_or_else(|| PyRuntimeError::new_err("call size() first"))?; + input::pmouse_y(graphics.surface.entity) + } + + #[pyfunction] + fn key_is_down(key_code: u32) -> PyResult { + input::key_is_down(key_code) + } } diff --git a/crates/processing_render/src/render/mod.rs b/crates/processing_render/src/render/mod.rs index 239356c..dd049d2 100644 --- a/crates/processing_render/src/render/mod.rs +++ b/crates/processing_render/src/render/mod.rs @@ -653,7 +653,7 @@ fn create_ndc_background_quad(world_from_clip: Mat4, color: Color, with_uvs: boo use bevy::asset::RenderAssetUsages; use bevy::mesh::{Indices, PrimitiveTopology}; - let ndc_z = 0.001; // near far plane (bevy uses reverse-z) + let ndc_z = 0.0; let ndc_corners = [ Vec4::new(-1.0, -1.0, ndc_z, 1.0), // bl Vec4::new(1.0, -1.0, ndc_z, 1.0), // br diff --git a/examples/animated_mesh.rs b/examples/animated_mesh.rs index eacf240..dd85212 100644 --- a/examples/animated_mesh.rs +++ b/examples/animated_mesh.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::{Vec3, Vec4}; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::geometry::Topology; use processing_render::render::command::DrawCommand; diff --git a/examples/background_image.rs b/examples/background_image.rs index 3d76775..dfc0d31 100644 --- a/examples/background_image.rs +++ b/examples/background_image.rs @@ -1,6 +1,5 @@ -mod glfw; +use processing_glfw::GlfwContext; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/box.rs b/examples/box.rs index 916f5d0..a07fba0 100644 --- a/examples/box.rs +++ b/examples/box.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::Vec3; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/custom_attribute.rs b/examples/custom_attribute.rs index 7493f82..a223ad9 100644 --- a/examples/custom_attribute.rs +++ b/examples/custom_attribute.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::{Vec3, Vec4}; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::geometry::{AttributeFormat, Topology}; use processing_render::render::command::DrawCommand; diff --git a/examples/custom_material.rs b/examples/custom_material.rs index d6e9fe2..b29feed 100644 --- a/examples/custom_material.rs +++ b/examples/custom_material.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::Vec3; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/glfw.rs b/examples/glfw.rs deleted file mode 100644 index 5478e08..0000000 --- a/examples/glfw.rs +++ /dev/null @@ -1,94 +0,0 @@ -/// Minimal GLFW helper for Processing examples -use bevy::prelude::Entity; -use glfw::{Glfw, GlfwReceiver, PWindow, WindowEvent, WindowMode}; -use processing_core::error::Result; - -pub struct GlfwContext { - glfw: Glfw, - window: PWindow, - events: GlfwReceiver<(f64, WindowEvent)>, -} - -impl GlfwContext { - pub fn new(width: u32, height: u32) -> Result { - let mut glfw = glfw::init(glfw::fail_on_errors).unwrap(); - - glfw.window_hint(glfw::WindowHint::ClientApi(glfw::ClientApiHint::NoApi)); - glfw.window_hint(glfw::WindowHint::Visible(false)); - - let (mut window, events) = glfw - .create_window(width, height, "Processing", WindowMode::Windowed) - .unwrap(); - - window.set_all_polling(true); - window.show(); - - Ok(Self { - glfw, - window, - events, - }) - } - - #[cfg(target_os = "macos")] - pub fn create_surface(&self, width: u32, height: u32) -> Result { - use processing_render::surface_create_macos; - let (scale_factor, _) = self.window.get_content_scale(); - surface_create_macos( - self.window.get_cocoa_window() as u64, - width, - height, - scale_factor, - ) - } - - #[cfg(target_os = "windows")] - pub fn create_surface(&self, width: u32, height: u32) -> Result { - use processing_render::surface_create_windows; - let (scale_factor, _) = self.window.get_content_scale(); - surface_create_windows( - self.window.get_win32_window() as u64, - width, - height, - scale_factor, - ) - } - - #[cfg(all(target_os = "linux", feature = "wayland"))] - pub fn create_surface(&self, width: u32, height: u32) -> Result { - use processing_render::surface_create_wayland; - let (scale_factor, _) = self.window.get_content_scale(); - surface_create_wayland( - self.window.get_wayland_window() as u64, - self.glfw.get_wayland_display() as u64, - width, - height, - scale_factor, - ) - } - - #[cfg(all(target_os = "linux", feature = "x11"))] - pub fn create_surface(&self, width: u32, height: u32) -> Result { - use processing_render::surface_create_x11; - let (scale_factor, _) = self.window.get_content_scale(); - surface_create_x11( - self.window.get_x11_window() as u64, - self.glfw.get_x11_display() as u64, - width, - height, - scale_factor, - ) - } - - pub fn poll_events(&mut self) -> bool { - self.glfw.poll_events(); - - for (_, event) in glfw::flush_messages(&self.events) { - if event == WindowEvent::Close { - return false; - } - } - - !self.window.should_close() - } -} diff --git a/examples/gltf_load.rs b/examples/gltf_load.rs index db3b4e7..b261539 100644 --- a/examples/gltf_load.rs +++ b/examples/gltf_load.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::Vec3; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::material::MaterialValue; use processing_render::render::command::DrawCommand; diff --git a/examples/input.rs b/examples/input.rs new file mode 100644 index 0000000..06cb528 --- /dev/null +++ b/examples/input.rs @@ -0,0 +1,57 @@ +use processing_glfw::GlfwContext; + +use processing::prelude::*; +use processing_render::render::command::DrawCommand; + +fn main() { + match sketch() { + Ok(_) => { + eprintln!("Sketch completed successfully"); + exit(0).unwrap(); + } + Err(e) => { + eprintln!("Sketch error: {:?}", e); + exit(1).unwrap(); + } + }; +} + +fn sketch() -> error::Result<()> { + let width = 400; + let height = 400; + let mut glfw_ctx = GlfwContext::new(width, height)?; + init(Config::default())?; + + let surface = glfw_ctx.create_surface(width, height)?; + let graphics = graphics_create(surface, width, height, TextureFormat::Rgba16Float)?; + + while glfw_ctx.poll_events() { + graphics_begin_draw(graphics)?; + + let mx = input_mouse_x(surface)?; + let my = input_mouse_y(surface)?; + + graphics_record_command( + graphics, + DrawCommand::BackgroundColor(bevy::color::Color::srgb(0.15, 0.15, 0.2)), + )?; + + graphics_record_command( + graphics, + DrawCommand::Rect { + x: mx - 25.0, + y: my - 25.0, + w: 50.0, + h: 50.0, + radii: [0.0; 4], + }, + )?; + + if input_key_is_pressed()? && input_key_is_down(KeyCode::Escape)? { + break; + } + + graphics_end_draw(graphics)?; + } + Ok(()) +} diff --git a/examples/lights.rs b/examples/lights.rs index 32a2308..b2d0f21 100644 --- a/examples/lights.rs +++ b/examples/lights.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::Vec3; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/materials.rs b/examples/materials.rs index 1ee3119..5ec92d8 100644 --- a/examples/materials.rs +++ b/examples/materials.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::{Vec2, Vec3}; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/midi.rs b/examples/midi.rs index b1317f9..3a2a956 100644 --- a/examples/midi.rs +++ b/examples/midi.rs @@ -1,6 +1,5 @@ -mod glfw; +use processing_glfw::GlfwContext; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/pbr.rs b/examples/pbr.rs index 6adb4bf..847bc1d 100644 --- a/examples/pbr.rs +++ b/examples/pbr.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::{Vec2, Vec3}; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/rectangle.rs b/examples/rectangle.rs index 3ea755b..7ea0093 100644 --- a/examples/rectangle.rs +++ b/examples/rectangle.rs @@ -1,6 +1,5 @@ -mod glfw; +use processing_glfw::GlfwContext; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/stroke_2d.rs b/examples/stroke_2d.rs index 51d83c9..d666e3f 100644 --- a/examples/stroke_2d.rs +++ b/examples/stroke_2d.rs @@ -1,6 +1,5 @@ -mod glfw; +use processing_glfw::GlfwContext; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/stroke_3d.rs b/examples/stroke_3d.rs index 1e5687b..c01cb21 100644 --- a/examples/stroke_3d.rs +++ b/examples/stroke_3d.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::{Vec2, Vec3}; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; diff --git a/examples/transforms.rs b/examples/transforms.rs index 1ca2fe6..5f1801f 100644 --- a/examples/transforms.rs +++ b/examples/transforms.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::math::Vec2; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; use std::f32::consts::PI; diff --git a/examples/update_pixels.rs b/examples/update_pixels.rs index a1a187e..e2eb728 100644 --- a/examples/update_pixels.rs +++ b/examples/update_pixels.rs @@ -1,7 +1,6 @@ -mod glfw; +use processing_glfw::GlfwContext; use bevy::{color::Color, prelude::LinearRgba}; -use glfw::GlfwContext; use processing::prelude::*; fn main() { diff --git a/examples/webcam.rs b/examples/webcam.rs index b189275..211ba25 100644 --- a/examples/webcam.rs +++ b/examples/webcam.rs @@ -1,6 +1,5 @@ -mod glfw; +use processing_glfw::GlfwContext; -use glfw::GlfwContext; use processing::prelude::*; use processing_render::render::command::DrawCommand; use processing_webcam::{webcam_create, webcam_destroy, webcam_image, webcam_is_connected}; diff --git a/src/lib.rs b/src/lib.rs index d7281e3..b71988f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,7 @@ fn create_app(config: Config) -> App { app.add_plugins(plugins); app.add_plugins(processing_midi::MidiPlugin); + app.add_plugins(processing_input::InputPlugin); app.add_plugins(processing_render::ProcessingRenderPlugin); #[cfg(feature = "webcam")] diff --git a/src/prelude.rs b/src/prelude.rs index ec86599..6f327b4 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,6 +1,9 @@ +pub use bevy::input::keyboard::KeyCode; +pub use bevy::input::mouse::MouseButton; pub use bevy::prelude::default; pub use bevy::render::render_resource::TextureFormat; pub use processing_core::{config::*, error}; +pub use processing_input::*; pub use processing_midi::{ midi_connect, midi_disconnect, midi_list_ports, midi_play_notes, midi_refresh_ports, }; From 06641ef71705ae441921c7b2aa2d6ce95786d493 Mon Sep 17 00:00:00 2001 From: charlotte Date: Tue, 24 Mar 2026 13:18:40 -0700 Subject: [PATCH 2/4] Fixes. --- crates/processing_glfw/Cargo.toml | 2 ++ crates/processing_pyo3/Cargo.toml | 3 ++- crates/processing_pyo3/src/input.rs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/processing_glfw/Cargo.toml b/crates/processing_glfw/Cargo.toml index b2e788b..2730ba0 100644 --- a/crates/processing_glfw/Cargo.toml +++ b/crates/processing_glfw/Cargo.toml @@ -8,6 +8,8 @@ workspace = true [features] wayland = ["glfw/wayland"] +x11 = [] +static-link = ["glfw/static-link"] [dependencies] bevy = { workspace = true } diff --git a/crates/processing_pyo3/Cargo.toml b/crates/processing_pyo3/Cargo.toml index f190b38..9660e34 100644 --- a/crates/processing_pyo3/Cargo.toml +++ b/crates/processing_pyo3/Cargo.toml @@ -11,8 +11,9 @@ name = "mewnala" crate-type = ["cdylib"] [features] -default = ["wayland"] +default = ["wayland", "static-link"] wayland = ["processing/wayland", "processing_glfw/wayland"] +static-link = ["processing_glfw/static-link"] x11 = ["processing/x11"] webcam = ["processing/webcam", "dep:processing_webcam"] diff --git a/crates/processing_pyo3/src/input.rs b/crates/processing_pyo3/src/input.rs index ab51669..8f0aac1 100644 --- a/crates/processing_pyo3/src/input.rs +++ b/crates/processing_pyo3/src/input.rs @@ -1,4 +1,4 @@ -use Entity; +use bevy::prelude::Entity; use processing::prelude::*; use pyo3::{ exceptions::{PyRuntimeError, PyValueError}, From 2715c70d76b3ec722f0f21ea6a19790af081e571 Mon Sep 17 00:00:00 2001 From: charlotte Date: Tue, 24 Mar 2026 13:22:12 -0700 Subject: [PATCH 3/4] Fmt. --- crates/processing_ffi/src/lib.rs | 21 ++++++++------------- crates/processing_pyo3/src/input.rs | 4 +--- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/crates/processing_ffi/src/lib.rs b/crates/processing_ffi/src/lib.rs index ee30d4f..2de215c 100644 --- a/crates/processing_ffi/src/lib.rs +++ b/crates/processing_ffi/src/lib.rs @@ -1395,11 +1395,9 @@ pub extern "C" fn processing_input_mouse_button(surface_id: u64, button: u8, pre PROCESSING_MOUSE_MIDDLE => MouseButton::Middle, PROCESSING_MOUSE_RIGHT => MouseButton::Right, _ => { - return Err( - ProcessingError::InvalidArgument(format!( - "invalid mouse button: {button}" - )), - ); + return Err(ProcessingError::InvalidArgument(format!( + "invalid mouse button: {button}" + ))); } }; input_set_mouse_button(Entity::from_bits(surface_id), btn, pressed) @@ -1427,9 +1425,7 @@ pub extern "C" fn processing_input_char(surface_id: u64, key_code: u32, codepoin error::check(|| { let kc = key_code_from_u32(key_code)?; let ch = char::from_u32(codepoint).ok_or_else(|| { - ProcessingError::InvalidArgument(format!( - "invalid codepoint: {codepoint}" - )) + ProcessingError::InvalidArgument(format!("invalid codepoint: {codepoint}")) })?; input_set_char(Entity::from_bits(surface_id), kc, ch) }); @@ -1528,8 +1524,7 @@ pub extern "C" fn processing_key() -> u32 { #[unsafe(no_mangle)] pub extern "C" fn processing_key_code() -> u32 { error::clear_error(); - error::check(|| input_key_code().map(|opt| opt.map(key_code_to_u32).unwrap_or(0))) - .unwrap_or(0) + error::check(|| input_key_code().map(|opt| opt.map(key_code_to_u32).unwrap_or(0))).unwrap_or(0) } #[unsafe(no_mangle)] @@ -1657,9 +1652,9 @@ fn key_code_from_u32(val: u32) -> processing::prelude::error::Result { PROCESSING_KEY_ALT_RIGHT => Ok(KeyCode::AltRight), PROCESSING_KEY_SUPER_RIGHT => Ok(KeyCode::SuperRight), PROCESSING_KEY_CONTEXT_MENU => Ok(KeyCode::ContextMenu), - _ => Err(ProcessingError::InvalidArgument( - format!("unknown key code: {val}"), - )), + _ => Err(ProcessingError::InvalidArgument(format!( + "unknown key code: {val}" + ))), } } diff --git a/crates/processing_pyo3/src/input.rs b/crates/processing_pyo3/src/input.rs index 8f0aac1..2b9f258 100644 --- a/crates/processing_pyo3/src/input.rs +++ b/crates/processing_pyo3/src/input.rs @@ -150,9 +150,7 @@ fn u32_to_key_code(val: u32) -> PyResult { 57 => KeyCode::Digit9, _ => unreachable!(), }), - _ => Err(PyValueError::new_err(format!( - "unknown key code: {val}" - ))), + _ => Err(PyValueError::new_err(format!("unknown key code: {val}"))), } } From 19e0c3fcbe6791683efc5c618f45679f39253bd0 Mon Sep 17 00:00:00 2001 From: charlotte Date: Tue, 24 Mar 2026 21:42:39 -0700 Subject: [PATCH 4/4] Clippy. --- crates/processing_ffi/src/lib.rs | 12 ++++++------ crates/processing_pyo3/src/math.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/processing_ffi/src/lib.rs b/crates/processing_ffi/src/lib.rs index 2de215c..4e78959 100644 --- a/crates/processing_ffi/src/lib.rs +++ b/crates/processing_ffi/src/lib.rs @@ -1452,7 +1452,7 @@ pub extern "C" fn processing_input_focus(surface_id: u64, focused: bool) { #[unsafe(no_mangle)] pub extern "C" fn processing_input_flush() { error::clear_error(); - error::check(|| input_flush()); + error::check(input_flush); } #[unsafe(no_mangle)] @@ -1482,7 +1482,7 @@ pub extern "C" fn processing_pmouse_y(surface_id: u64) -> f32 { #[unsafe(no_mangle)] pub extern "C" fn processing_mouse_is_pressed() -> bool { error::clear_error(); - error::check(|| input_mouse_is_pressed()).unwrap_or(false) + error::check(input_mouse_is_pressed).unwrap_or(false) } #[unsafe(no_mangle)] @@ -1502,7 +1502,7 @@ pub extern "C" fn processing_mouse_button() -> i8 { #[unsafe(no_mangle)] pub extern "C" fn processing_key_is_pressed() -> bool { error::clear_error(); - error::check(|| input_key_is_pressed()).unwrap_or(false) + error::check(input_key_is_pressed).unwrap_or(false) } #[unsafe(no_mangle)] @@ -1530,19 +1530,19 @@ pub extern "C" fn processing_key_code() -> u32 { #[unsafe(no_mangle)] pub extern "C" fn processing_moved_x() -> f32 { error::clear_error(); - error::check(|| input_moved_x()).unwrap_or(0.0) + error::check(input_moved_x).unwrap_or(0.0) } #[unsafe(no_mangle)] pub extern "C" fn processing_moved_y() -> f32 { error::clear_error(); - error::check(|| input_moved_y()).unwrap_or(0.0) + error::check(input_moved_y).unwrap_or(0.0) } #[unsafe(no_mangle)] pub extern "C" fn processing_mouse_wheel() -> f32 { error::clear_error(); - error::check(|| input_mouse_wheel()).unwrap_or(0.0) + error::check(input_mouse_wheel).unwrap_or(0.0) } fn key_code_from_u32(val: u32) -> processing::prelude::error::Result { diff --git a/crates/processing_pyo3/src/math.rs b/crates/processing_pyo3/src/math.rs index 0239e01..f375543 100644 --- a/crates/processing_pyo3/src/math.rs +++ b/crates/processing_pyo3/src/math.rs @@ -269,7 +269,7 @@ macro_rules! impl_py_vec { fn __getitem__(&self, idx: isize) -> PyResult { let idx = if idx < 0 { $n as isize + idx } else { idx }; - if idx < 0 || idx >= $n { + if !(0..$n as isize).contains(&idx) { return Err(PyTypeError::new_err("index out of range")); } Ok(self.0[idx as usize]) @@ -277,7 +277,7 @@ macro_rules! impl_py_vec { fn __setitem__(&mut self, idx: isize, val: f32) -> PyResult<()> { let idx = if idx < 0 { $n as isize + idx } else { idx }; - if idx < 0 || idx >= $n { + if !(0..$n as isize).contains(&idx) { return Err(PyTypeError::new_err("index out of range")); } self.0[idx as usize] = val;