tirbofish/dropbear · commit
0a6e235c6dab377f7c38a1dc2be730177e72b486
fix: linux support works better now (since i moved to linux and got rid of windows). feature: got a brand new logo for dropbear. i sketched it up quickly, but it works... jarvis, run github actions (for my linux people) Unverified
@@ -21,7 +21,6 @@ members = [ [workspace.dependencies] anyhow = { version = "1.0", features = ["backtrace"] } app_dirs2 = "2.5" -arboard = "3.6" bytemuck = { version = "1.24", features = ["derive"] } chrono = "0.4" clap = { version = "4.5", features = ["derive"] } @@ -1,4 +1,9 @@ -# dropbear +<div style="text-align: center;"> + <img src="resources/eucalyptus-editor.png" alt="dropbear engine logo, specifically a koala falling off a eucalyptus tree, looking like a 'dropbear'. looks nice, right?"> + <div style="position: relative; top: -50px; "> + <h1>dropbear</h1> + </div> +</div> dropbear is a game engine used to create games, made in Rust and scripted with the Kotlin Language. @@ -46,7 +46,6 @@ postcard.workspace = true [target.'cfg(not(target_os = "android"))'.dependencies] rfd.workspace = true -arboard.workspace = true [dependencies.image] version = "0.25" @@ -20,6 +20,8 @@ pub mod utils; pub static WGPU_BACKEND: OnceLock<String> = OnceLock::new(); pub const PHYSICS_STEP_RATE: u32 = 120; const MAX_PHYSICS_STEPS_PER_FRAME: usize = 4; +/// Note: image size is 256x256 +pub const LOGO_AS_BYTES: &[u8] = include_bytes!("../../resources/eucalyptus-editor.png"); use app_dirs2::{AppDataType, AppInfo}; use bytemuck::Contiguous; @@ -83,6 +85,17 @@ pub struct State { pub scene_manager: scene::Manager, } +/// Generates the dropbear engine logo in a form that [winit::window::Icon] can accept. +/// +/// Returns (the bytes, width, height) in resp order. +pub fn gen_logo() -> anyhow::Result<(Vec<u8>, u32, u32)> { + let image = image::load_from_memory(LOGO_AS_BYTES)?.into_rgba8(); + let (width, height) = image.dimensions(); + let rgba = image.into_raw(); + Ok((rgba, width, height)) + +} + impl State { /// Asynchronously initialised the state and sets up the backend and surface for wgpu to render to. pub async fn new(window: Arc<Window>, instance: Arc<Instance>, future_queue: Arc<FutureQueue>) -> anyhow::Result<Self> { @@ -90,6 +103,10 @@ impl State { let size = window.inner_size(); + let initial_width = size.width.max(1); + let initial_height = size.height.max(1); + let is_surface_configured = size.width > 0 && size.height > 0; + let surface = instance.create_surface(window.clone())?; let adapter = instance @@ -167,15 +184,17 @@ Hardware: let config = SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, - width: size.width, - height: size.height, + width: initial_width, + height: initial_height, present_mode: surface_caps.present_modes[0], alpha_mode: surface_caps.alpha_modes[0], view_formats: vec![], desired_maximum_frame_latency: 2, }; - surface.configure(&device, &config); + if is_surface_configured { + surface.configure(&device, &config); + } let depth_texture = Texture::create_depth_texture(&config, &device, Some("depth texture")); let viewport_texture = @@ -256,7 +275,7 @@ Hardware: device: Arc::new(device), queue: Arc::new(queue), config, - is_surface_configured: true, + is_surface_configured, depth_texture, texture_bind_layout: texture_bind_group_layout, material_tint_bind_layout: material_tint_bind_group_layout, @@ -422,6 +441,8 @@ Hardware: fn cleanup(mut self, event_loop: &ActiveEventLoop) { self.scene_manager.clear_all(event_loop); + let _ = self.device.poll(wgpu::PollType::Poll); + drop(self.egui_renderer); drop(self.depth_texture); @@ -769,6 +790,26 @@ impl App { self.windows.insert(window_id, win_state); Ok(window_id) } + + fn quit(&mut self, event_loop: &ActiveEventLoop, hook: Option<fn()>) { + if let Some(h) = hook { + log::debug!("App has a pre-exit hook, executing..."); + h(); + } + + log::info!("Exiting app!"); + + let windows = std::mem::take(&mut self.windows); + for (_, state) in windows { + state.cleanup(event_loop); + } + self.root_window_id = None; + + #[cfg(not(target_os = "linux"))] + event_loop.exit(); + #[cfg(target_os = "linux")] + std::process::exit(0); + } } impl ApplicationHandler for App { @@ -832,7 +873,7 @@ impl ApplicationHandler for App { if matches!(event, WindowEvent::CloseRequested) { if Some(window_id) == self.root_window_id { log::info!("Root window closed, exiting app"); - event_loop.exit(); + self.quit(event_loop, None); } else { log::info!("Closing non-root window: {:?}", window_id); if let Some(state) = self.windows.remove(&window_id) { @@ -926,11 +967,15 @@ impl ApplicationHandler for App { scene::SceneCommand::CloseWindow(target_window_id) => { log::info!("Scene requested closing window: {:?}", target_window_id); if Some(target_window_id) == self.root_window_id { - event_loop.exit(); + self.quit(event_loop, None); } else { self.windows.remove(&target_window_id); } } + scene::SceneCommand::Quit(hook) => { + log::debug!("Caught SceneCommand::Quit command!"); + self.quit(event_loop, hook); + } scene::SceneCommand::SetFPS(new_fps) => { self.set_target_fps(new_fps); } @@ -1,8 +1,6 @@ use std::panic; #[cfg(not(target_os = "android"))] -use arboard::Clipboard; -#[cfg(not(target_os = "android"))] use rfd::{MessageDialog, MessageLevel}; /// Creates a new panic hook for crash detection. @@ -34,9 +32,9 @@ pub fn set_hook() { #[cfg(not(target_os = "android"))] { - if let Ok(mut clipboard) = Clipboard::new() { - let _ = clipboard.set_text(full_text.clone()); - } + // if let Ok(mut clipboard) = Clipboard::new() { + // let _ = clipboard.set_text(full_text.clone()); + // } let _ = MessageDialog::new() .set_title("Panic!") @@ -137,12 +137,7 @@ impl Manager { } } SceneCommand::Quit(hook) => { - if let Some(h) = hook { - log::debug!("App has a pre-exit hook, executing..."); - h() - } - log::info!("Exiting app!"); - event_loop.exit(); + return vec![SceneCommand::Quit(hook)]; } SceneCommand::None => {} SceneCommand::DebugMessage(msg) => log::debug!("{}", msg), @@ -146,7 +146,6 @@ impl Keyboard for Editor { .status() .ok(); log::debug!("Stopping gradle threads"); - std::process::exit(0); } Err(_) => { Command::new("gradlew") @@ -177,7 +176,9 @@ impl Keyboard for Editor { } }); }; + self.scene_command = SceneCommand::Quit(Some(commands)); + log::debug!("Sent quit command"); } else if is_playing { warn!( "Unable to save-quit project, please pause your playing state, then try again" @@ -876,7 +876,7 @@ impl Editor { match self.save_project_config() { Ok(_) => { log::info!("Saved, quitting..."); - std::process::exit(0); + self.scene_command = SceneCommand::Quit(None); } Err(e) => { fatal!("Error saving project: {}", e); @@ -172,7 +172,9 @@ impl Scene for Editor { { if let Some(fps) = EDITOR_SETTINGS.read().target_fps.get() { log_once::debug_once!("setting new fps for editor: {}", fps); - self.scene_command = SceneCommand::SetFPS(*fps); + if matches!(self.scene_command, SceneCommand::None) { + self.scene_command = SceneCommand::SetFPS(*fps); + } } } @@ -8,8 +8,8 @@ use eucalyptus_editor::{build, editor, menu}; use parking_lot::RwLock; use std::sync::Arc; use std::{fs, path::{Path, PathBuf}, rc::Rc}; -use winit::window::WindowAttributes; -use dropbear_engine::DropbearWindowBuilder; +use winit::window::{Icon, WindowAttributes}; +use dropbear_engine::{DropbearWindowBuilder}; use eucalyptus_core::config::ProjectConfig; use eucalyptus_core::scripting::jni::{RuntimeMode, RUNTIME_MODE}; use eucalyptus_core::scripting::{AWAIT_JDB, JVM_ARGS}; @@ -269,6 +269,12 @@ async fn main() -> anyhow::Result<()> { panic!("Unable to initialise Eucalyptus Editor: {}", e) }))); + let img = dropbear_engine::gen_logo()?; + + let window_icon = Icon::from_rgba(img.0, img.1, img.2) + .inspect_err(|e| log::warn!("Unable to set logo: {}", e)) + .ok(); + let window = DropbearWindowBuilder::new() .with_attributes( WindowAttributes::default().with_title( @@ -279,6 +285,7 @@ async fn main() -> anyhow::Result<()> { ) ) .with_maximized(true) + .with_window_icon(window_icon) ) .add_scene_with_input(editor, "editor") .add_scene_with_input(main_menu, "main_menu") @@ -493,9 +493,9 @@ impl Scene for MainMenu { } impl Keyboard for MainMenu { - fn key_down(&mut self, key: KeyCode, event_loop: &ActiveEventLoop) { + fn key_down(&mut self, key: KeyCode, _event_loop: &ActiveEventLoop) { if key == KeyCode::Escape && !self.show_new_project && !self.is_in_file_dialogue { - event_loop.exit(); + self.scene_command = SceneCommand::Quit(None); } } @@ -1,4 +1,4 @@ -use dropbear_engine::graphics::RenderContext; +use dropbear_engine::{graphics::RenderContext, scene::SceneCommand}; use eucalyptus_core::command::{CommandBufferPoller, COMMAND_BUFFER, CommandBuffer, WindowCommand}; use winit::window::CursorGrabMode; use crate::runtime::PlayMode; @@ -12,13 +12,13 @@ impl CommandBufferPoller for PlayMode { CommandBuffer::WindowCommand(w_cmd) => match w_cmd { WindowCommand::WindowGrab(lock) => { if lock { - if let Err(e) = graphics.shared.window - .set_cursor_grab(CursorGrabMode::Confined) - .or_else(|_| graphics.shared.window.set_cursor_grab(CursorGrabMode::Locked)) - { - log_once::warn_once!("Failed to grab cursor: {:?}", e); - } else { - log_once::info_once!("Grabbed cursor"); + let window = &graphics.shared.window; + window.set_cursor_visible(false); + if let Err(e) = window.set_cursor_grab(CursorGrabMode::Locked).or_else(|_| { + log_once::warn_once!("Using cursor grab fallback: CursorGrabMode::Confined"); + window.set_cursor_grab(CursorGrabMode::Confined) + }) { + log_once::error_once!("Unable to grab mouse: {}", e); } } else if let Err(e) = graphics.shared.window.set_cursor_grab(CursorGrabMode::None) { log_once::warn_once!("Failed to release cursor: {:?}", e); @@ -35,7 +35,7 @@ impl CommandBufferPoller for PlayMode { } }, CommandBuffer::Quit => { - self.scene_command = dropbear_engine::scene::SceneCommand::Quit(None); + self.scene_command = SceneCommand::CloseWindow(graphics.shared.window.id()); }, CommandBuffer::SwitchSceneImmediate(scene_name) => { log::debug!("Immediate scene switch requested: {}", scene_name); @@ -50,7 +50,7 @@ impl CommandBufferPoller for PlayMode { CommandBuffer::SwitchToAsync(handle) => { if let Some(ref progress) = self.scene_progress { if progress.requested_scene == handle.scene_name && progress.is_everything_loaded() { - self.switch_to(progress.clone(), graphics); + self.switch_to(progress.clone(), graphics); } } } @@ -231,7 +231,9 @@ impl Scene for PlayMode { { if let Some(fps) = PROJECT.read().runtime_settings.target_fps.get() { log_once::debug_once!("setting new fps for play mode session: {}", fps); - self.scene_command = SceneCommand::SetFPS(*fps); + if matches!(self.scene_command, SceneCommand::None) { + self.scene_command = SceneCommand::SetFPS(*fps); + } } } Binary files /dev/null and b/resources/eucalyptus-editor.png differ