Lightningbeam/src-tauri/src/render_window.rs

162 lines
5.4 KiB
Rust

use std::sync::Arc;
use std::thread;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy},
window::WindowBuilder,
};
#[cfg(target_os = "linux")]
use winit::platform::x11::EventLoopBuilderExtX11;
use crate::renderer::Renderer;
/// Events that can be sent to the render window thread
#[derive(Debug, Clone)]
pub enum RenderEvent {
UpdateGradient { top: [f32; 4], bottom: [f32; 4] },
SetPosition { x: i32, y: i32 },
SetSize { width: u32, height: u32 },
RequestRedraw,
Close,
}
/// Handle to control the render window from other threads
pub struct RenderWindowHandle {
proxy: EventLoopProxy<RenderEvent>,
}
impl RenderWindowHandle {
/// Update the gradient colors
pub fn update_gradient(&self, top: [f32; 4], bottom: [f32; 4]) {
let _ = self.proxy.send_event(RenderEvent::UpdateGradient { top, bottom });
}
/// Set window position
pub fn set_position(&self, x: i32, y: i32) {
let _ = self.proxy.send_event(RenderEvent::SetPosition { x, y });
}
/// Set window size
pub fn set_size(&self, width: u32, height: u32) {
let _ = self.proxy.send_event(RenderEvent::SetSize { width, height });
}
/// Request a redraw
pub fn request_redraw(&self) {
let _ = self.proxy.send_event(RenderEvent::RequestRedraw);
}
/// Close the render window
pub fn close(&self) {
let _ = self.proxy.send_event(RenderEvent::Close);
}
}
/// Spawn the render window in a separate thread
pub fn spawn_render_window(
x: i32,
y: i32,
width: u32,
height: u32,
) -> Result<RenderWindowHandle, String> {
let (tx, rx) = std::sync::mpsc::channel();
thread::spawn(move || {
let mut event_loop_builder = EventLoopBuilder::with_user_event();
// On Linux, allow event loop on any thread (not just main thread)
#[cfg(target_os = "linux")]
{
event_loop_builder.with_any_thread(true);
}
let event_loop: EventLoop<RenderEvent> = event_loop_builder.build().unwrap();
let proxy = event_loop.create_proxy();
// Send the proxy back to the main thread
tx.send(proxy.clone()).unwrap();
let window = WindowBuilder::new()
.with_title("Lightningbeam Renderer")
.with_inner_size(winit::dpi::PhysicalSize::new(width, height))
.with_position(winit::dpi::PhysicalPosition::new(x, y))
.with_decorations(false) // No title bar
.with_transparent(false) // Opaque background
.with_resizable(false)
.build(&event_loop)
.unwrap();
let window = Arc::new(window);
// Initialize renderer (async operation)
let mut renderer = pollster::block_on(Renderer::new(window.clone()));
event_loop.run(move |event, elwt| {
elwt.set_control_flow(ControlFlow::Wait);
match event {
Event::UserEvent(render_event) => match render_event {
RenderEvent::UpdateGradient { top, bottom } => {
eprintln!("[RenderWindow] Updating gradient: {:?} -> {:?}", top, bottom);
renderer.update_gradient(top, bottom);
window.request_redraw();
}
RenderEvent::SetPosition { x, y } => {
eprintln!("[RenderWindow] Setting position: ({}, {})", x, y);
let _ = window.set_outer_position(winit::dpi::PhysicalPosition::new(x, y));
}
RenderEvent::SetSize { width, height } => {
eprintln!("[RenderWindow] Setting size: {}x{}", width, height);
let _ = window.request_inner_size(winit::dpi::PhysicalSize::new(width, height));
}
RenderEvent::RequestRedraw => {
window.request_redraw();
}
RenderEvent::Close => {
eprintln!("[RenderWindow] Closing render window");
elwt.exit();
}
},
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
elwt.exit();
}
Event::WindowEvent {
event: WindowEvent::Resized(physical_size),
..
} => {
renderer.resize(physical_size);
window.request_redraw();
}
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
} => {
match renderer.render() {
Ok(_) => {}
Err(wgpu::SurfaceError::Lost) => renderer.resize(window.inner_size()),
Err(wgpu::SurfaceError::OutOfMemory) => {
eprintln!("Out of memory!");
elwt.exit();
}
Err(e) => eprintln!("Render error: {:?}", e),
}
}
_ => {}
}
}).expect("Event loop error");
});
// Wait for the proxy to be sent back
let proxy = rx.recv().map_err(|e| format!("Failed to receive proxy: {}", e))?;
Ok(RenderWindowHandle { proxy })
}