kitgit

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

Thribhu K <4tkbytes@pm.me> · 2026-01-14 13:57

view full diff

diff --git a/Cargo.toml b/Cargo.toml
index d59eeca..c257e24 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"] }
diff --git a/README.md b/README.md
index b5c3cfc..24a1d4e 100644
--- a/README.md
+++ b/README.md
@@ -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.
 
diff --git a/dropbear-engine/Cargo.toml b/dropbear-engine/Cargo.toml
index 2c060fa..887b142 100644
--- a/dropbear-engine/Cargo.toml
+++ b/dropbear-engine/Cargo.toml
@@ -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"
diff --git a/dropbear-engine/src/lib.rs b/dropbear-engine/src/lib.rs
index f9f6107..8a85c3e 100644
--- a/dropbear-engine/src/lib.rs
+++ b/dropbear-engine/src/lib.rs
@@ -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);
                         }
diff --git a/dropbear-engine/src/panic.rs b/dropbear-engine/src/panic.rs
index ca7ebc5..28f1b55 100644
--- a/dropbear-engine/src/panic.rs
+++ b/dropbear-engine/src/panic.rs
@@ -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!")
diff --git a/dropbear-engine/src/scene.rs b/dropbear-engine/src/scene.rs
index 9058d3e..141f08c 100644
--- a/dropbear-engine/src/scene.rs
+++ b/dropbear-engine/src/scene.rs
@@ -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),
diff --git a/eucalyptus-editor/src/editor/input.rs b/eucalyptus-editor/src/editor/input.rs
index 81b8ab7..925bf63 100644
--- a/eucalyptus-editor/src/editor/input.rs
+++ b/eucalyptus-editor/src/editor/input.rs
@@ -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"
diff --git a/eucalyptus-editor/src/editor/mod.rs b/eucalyptus-editor/src/editor/mod.rs
index ac1b977..912206c 100644
--- a/eucalyptus-editor/src/editor/mod.rs
+++ b/eucalyptus-editor/src/editor/mod.rs
@@ -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);
diff --git a/eucalyptus-editor/src/editor/scene.rs b/eucalyptus-editor/src/editor/scene.rs
index ff6d8b7..ff26b5c 100644
--- a/eucalyptus-editor/src/editor/scene.rs
+++ b/eucalyptus-editor/src/editor/scene.rs
@@ -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);
+                }
             }
         }
 
diff --git a/eucalyptus-editor/src/main.rs b/eucalyptus-editor/src/main.rs
index 7d18fd8..f7de1fd 100644
--- a/eucalyptus-editor/src/main.rs
+++ b/eucalyptus-editor/src/main.rs
@@ -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")
diff --git a/eucalyptus-editor/src/menu.rs b/eucalyptus-editor/src/menu.rs
index c07b2f1..a99a983 100644
--- a/eucalyptus-editor/src/menu.rs
+++ b/eucalyptus-editor/src/menu.rs
@@ -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);
         }
     }
 
diff --git a/eucalyptus-editor/src/runtime/command.rs b/eucalyptus-editor/src/runtime/command.rs
index 81e7355..074bf5c 100644
--- a/eucalyptus-editor/src/runtime/command.rs
+++ b/eucalyptus-editor/src/runtime/command.rs
@@ -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);
                         }
                     }
                 }
diff --git a/eucalyptus-editor/src/runtime/scene.rs b/eucalyptus-editor/src/runtime/scene.rs
index e76902e..915e0dd 100644
--- a/eucalyptus-editor/src/runtime/scene.rs
+++ b/eucalyptus-editor/src/runtime/scene.rs
@@ -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);
+                }
             }
         }
 
diff --git a/resources/eucalyptus-editor.png b/resources/eucalyptus-editor.png
new file mode 100644
index 0000000..bdd598f
Binary files /dev/null and b/resources/eucalyptus-editor.png differ