use crate::ecs::world::Vec2;
use crate::ecs::world::World;
use crate::ecs::world::resources::MouseState;
use crate::render::wgpu::WgpuRenderer;
use crate::state::State;
use winit::event::{ElementState, MouseButton};
use winit::keyboard::KeyCode;
pub fn initialize_offscreen(
world: &mut World,
state: &mut dyn State,
renderer: &mut WgpuRenderer,
viewport: (u32, u32),
scale_factor: f32,
) {
world.resources.window.cached_viewport_size = Some(viewport);
world.resources.window.cached_scale_factor = scale_factor;
if let Err(error) = renderer.configure_with_state(state) {
tracing::error!("Failed to configure renderer with state: {error}");
}
world.resources.schedules.frame = crate::schedule::build_default_frame_schedule();
world.resources.schedules.retained_ui = crate::schedule::build_default_retained_ui_schedule();
state.initialize(world);
}
pub fn resize_offscreen(world: &mut World, renderer: &mut WgpuRenderer, width: u32, height: u32) {
if let Err(error) = renderer.resize_surface(width, height) {
tracing::error!("Failed to resize surface: {error}");
}
world.resources.window.cached_viewport_size = Some((width, height));
}
pub fn tick_offscreen(
world: &mut World,
state: &mut dyn State,
renderer: &mut WgpuRenderer,
) -> Option<Box<dyn State>> {
super::advance_timing(world);
super::run_frame_body(world, state, renderer)
}
pub fn input_inject_cursor_moved(world: &mut World, position: Vec2) {
let mouse = &mut world.resources.input.mouse;
if mouse.position_initialized {
mouse.position_delta += position - mouse.position;
} else {
mouse.position_initialized = true;
}
mouse.position = position;
mouse.state.set(MouseState::MOVED, true);
}
pub fn input_inject_mouse_motion(world: &mut World, delta: Vec2) {
world.resources.input.mouse.raw_mouse_delta += delta;
}
pub fn input_inject_mouse_button(world: &mut World, button: MouseButton, state: ElementState) {
let pressed = state == ElementState::Pressed;
let released = state == ElementState::Released;
let mouse = &mut world.resources.input.mouse;
match button {
MouseButton::Left => {
let was_clicked = mouse.state.contains(MouseState::LEFT_CLICKED);
mouse.state.set(MouseState::LEFT_CLICKED, pressed);
if pressed && !was_clicked {
mouse.state.set(MouseState::LEFT_JUST_PRESSED, true);
}
if released && was_clicked {
mouse.state.set(MouseState::LEFT_JUST_RELEASED, true);
}
}
MouseButton::Middle => {
let was_clicked = mouse.state.contains(MouseState::MIDDLE_CLICKED);
mouse.state.set(MouseState::MIDDLE_CLICKED, pressed);
if pressed && !was_clicked {
mouse.state.set(MouseState::MIDDLE_JUST_PRESSED, true);
}
if released && was_clicked {
mouse.state.set(MouseState::MIDDLE_JUST_RELEASED, true);
}
}
MouseButton::Right => {
let was_clicked = mouse.state.contains(MouseState::RIGHT_CLICKED);
mouse.state.set(MouseState::RIGHT_CLICKED, pressed);
if pressed && !was_clicked {
mouse.state.set(MouseState::RIGHT_JUST_PRESSED, true);
}
if released && was_clicked {
mouse.state.set(MouseState::RIGHT_JUST_RELEASED, true);
}
}
_ => {}
}
}
pub fn input_inject_mouse_wheel(world: &mut World, delta: Vec2) {
let mouse = &mut world.resources.input.mouse;
mouse.wheel_delta = delta;
mouse.state.set(MouseState::SCROLLED, true);
}
pub fn input_inject_keyboard(
world: &mut World,
key_code: KeyCode,
state: ElementState,
text: Option<&str>,
) {
use crate::ecs::event_bus::commands::publish_event;
use crate::ecs::world::events::{InputEvent, KeyState, Message};
let was_pressed = world
.resources
.input
.keyboard
.keystates
.get(&key_code)
.is_some_and(|previous| *previous == ElementState::Pressed);
*world
.resources
.input
.keyboard
.keystates
.entry(key_code)
.or_insert(state) = state;
let pressed = state == ElementState::Pressed;
world
.resources
.input
.keyboard
.frame_keys
.push((key_code, pressed));
if pressed && !was_pressed {
world
.resources
.input
.keyboard
.just_pressed_keys
.insert(key_code);
} else if !pressed && was_pressed {
world
.resources
.input
.keyboard
.just_released_keys
.insert(key_code);
}
if pressed && let Some(text) = text {
for character in text.chars() {
world.resources.input.keyboard.frame_chars.push(character);
}
}
let event = match state {
ElementState::Pressed => InputEvent::KeyboardInput {
key_code: key_code as u32,
state: KeyState::Pressed,
},
ElementState::Released => InputEvent::KeyboardInput {
key_code: key_code as u32,
state: KeyState::Released,
},
};
publish_event(world, Message::Input { event });
}