diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 0e2d9d86ca..0ad28d6c34 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -1,7 +1,6 @@ use rfd::AsyncFileDialog; use std::fs; use std::path::PathBuf; -use std::sync::Arc; use std::sync::mpsc::Receiver; use std::sync::mpsc::Sender; use std::sync::mpsc::SyncSender; @@ -12,22 +11,20 @@ use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::ActiveEventLoop; use winit::event_loop::ControlFlow; -use winit::window::Window; use winit::window::WindowId; use crate::cef; use crate::consts::CEF_MESSAGE_LOOP_MAX_ITERATIONS; use crate::event::{AppEvent, AppEventScheduler}; -use crate::native_window; use crate::persist::PersistentData; use crate::render::GraphicsState; +use crate::window::Window; use graphite_desktop_wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, Platform}; use graphite_desktop_wrapper::{DesktopWrapper, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages}; pub(crate) struct App { cef_context: Box, - window: Option>, - native_window: native_window::NativeWindowHandle, + window: Option, cef_schedule: Option, cef_window_size_sender: Sender, graphics_state: Option, @@ -82,7 +79,6 @@ impl App { web_communication_initialized: false, web_communication_startup_buffer: Vec::new(), persistent_data, - native_window: Default::default(), launch_documents, } } @@ -173,18 +169,17 @@ impl App { } DesktopFrontendMessage::MinimizeWindow => { if let Some(window) = &self.window { - window.set_minimized(true); + window.minimize(); } } DesktopFrontendMessage::MaximizeWindow => { if let Some(window) = &self.window { - let maximized = !window.is_maximized(); - window.set_maximized(maximized); + window.toggle_maximize(); } } DesktopFrontendMessage::DragWindow => { if let Some(window) = &self.window { - let _ = window.drag_window(); + let _ = window.start_drag(); } } DesktopFrontendMessage::CloseWindow => { @@ -348,15 +343,11 @@ impl App { } impl ApplicationHandler for App { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - let window_attributes = self.native_window.build(event_loop); - - let window: Arc = Arc::from(event_loop.create_window(window_attributes).unwrap()); - - self.native_window.setup(window.as_ref()); + let window = Window::new(event_loop); + self.window = Some(window); - let graphics_state = GraphicsState::new(window.clone(), self.wgpu_context.clone()); + let graphics_state = GraphicsState::new(self.window.as_ref().unwrap(), self.wgpu_context.clone()); - self.window = Some(window); self.graphics_state = Some(graphics_state); tracing::info!("Winit window created and ready"); @@ -397,7 +388,7 @@ impl ApplicationHandler for App { let Some(ref mut graphics_state) = self.graphics_state else { return }; // Only rerender once we have a new UI texture to display if let Some(window) = &self.window { - match graphics_state.render(window.as_ref()) { + match graphics_state.render(window) { Ok(_) => {} Err(wgpu::SurfaceError::Lost) => { tracing::warn!("lost surface"); diff --git a/desktop/src/lib.rs b/desktop/src/lib.rs index 04a99182af..9f1591675b 100644 --- a/desktop/src/lib.rs +++ b/desktop/src/lib.rs @@ -10,9 +10,9 @@ mod cef; mod cli; mod dirs; mod event; -mod native_window; mod persist; mod render; +mod window; mod gpu_context; diff --git a/desktop/src/native_window.rs b/desktop/src/native_window.rs deleted file mode 100644 index 154ffcb316..0000000000 --- a/desktop/src/native_window.rs +++ /dev/null @@ -1,71 +0,0 @@ -use winit::event_loop::ActiveEventLoop; -use winit::window::{Window, WindowAttributes}; - -use crate::consts::APP_NAME; - -#[cfg(target_os = "windows")] -mod windows; - -pub(crate) enum NativeWindowHandle { - #[cfg(target_os = "windows")] - #[expect(private_interfaces, dead_code)] - Windows(windows::WindowsNativeWindowHandle), - None, -} -impl Default for NativeWindowHandle { - fn default() -> Self { - Self::None - } -} -impl NativeWindowHandle { - #[allow(unused_variables)] - pub(super) fn build(&mut self, event_loop: &dyn ActiveEventLoop) -> WindowAttributes { - let mut window = WindowAttributes::default() - .with_title(APP_NAME) - .with_min_surface_size(winit::dpi::LogicalSize::new(400, 300)) - .with_surface_size(winit::dpi::LogicalSize::new(1200, 800)) - .with_resizable(true) - .with_theme(Some(winit::window::Theme::Dark)); - - #[cfg(target_os = "linux")] - { - use crate::consts::{APP_ID, APP_NAME}; - use winit::platform::wayland::ActiveEventLoopExtWayland; - use winit::platform::wayland::WindowAttributesWayland; - use winit::platform::x11::WindowAttributesX11; - window = if event_loop.is_wayland() { - let wayland_window = WindowAttributesWayland::default().with_name(APP_ID, "").with_prefer_csd(true); - window.with_platform_attributes(Box::new(wayland_window)) - } else { - let x11_window = WindowAttributesX11::default().with_name(APP_ID, APP_NAME); - window.with_platform_attributes(Box::new(x11_window)) - }; - } - - #[cfg(target_os = "windows")] - { - if let Ok(win_icon) = winit::platform::windows::WinIcon::from_resource(1, None) { - let icon = winit::icon::Icon(std::sync::Arc::new(win_icon)); - window = window.with_window_icon(Some(icon)); - } - } - - #[cfg(target_os = "macos")] - { - let mac_window = winit::platform::macos::WindowAttributesMacOS::default() - .with_titlebar_transparent(true) - .with_fullsize_content_view(true) - .with_title_hidden(true); - window = window.with_platform_attributes(Box::new(mac_window)); - } - - window - } - #[allow(unused_variables)] - pub(crate) fn setup(&mut self, window: &dyn Window) { - #[cfg(target_os = "windows")] - { - *self = NativeWindowHandle::Windows(windows::WindowsNativeWindowHandle::new(window)); - } - } -} diff --git a/desktop/src/render/graphics_state.rs b/desktop/src/render/graphics_state.rs index 1d1d1aef00..d6797f937e 100644 --- a/desktop/src/render/graphics_state.rs +++ b/desktop/src/render/graphics_state.rs @@ -1,5 +1,4 @@ -use std::sync::Arc; -use winit::window::Window; +use crate::window::Window; use graphite_desktop_wrapper::{Color, WgpuContext, WgpuExecutor}; @@ -24,10 +23,9 @@ pub(crate) struct GraphicsState { } impl GraphicsState { - pub(crate) fn new(window: Arc, context: WgpuContext) -> Self { + pub(crate) fn new(window: &Window, context: WgpuContext) -> Self { let size = window.surface_size(); - - let surface = context.instance.create_surface(window).unwrap(); + let surface = window.create_surface(context.instance.clone()); let surface_caps = surface.get_capabilities(&context.adapter); let surface_format = surface_caps.formats.iter().find(|f| f.is_srgb()).copied().unwrap_or(surface_caps.formats[0]); @@ -232,7 +230,7 @@ impl GraphicsState { self.bind_overlays_texture(texture); } - pub(crate) fn render(&mut self, window: &dyn Window) -> Result<(), wgpu::SurfaceError> { + pub(crate) fn render(&mut self, window: &Window) -> Result<(), wgpu::SurfaceError> { if let Some(scene) = self.overlays_scene.take() { self.render_overlays(scene); } diff --git a/desktop/src/window.rs b/desktop/src/window.rs new file mode 100644 index 0000000000..8445e55f2a --- /dev/null +++ b/desktop/src/window.rs @@ -0,0 +1,87 @@ +use std::sync::Arc; +use winit::event_loop::ActiveEventLoop; +use winit::window::{Window as WinitWindow, WindowAttributes}; + +use crate::consts::APP_NAME; + +pub(crate) trait NativeWindow { + fn configure(attributes: WindowAttributes, event_loop: &dyn ActiveEventLoop) -> WindowAttributes; + fn new(window: &dyn WinitWindow) -> Self; +} + +#[cfg(target_os = "linux")] +mod linux; +#[cfg(target_os = "linux")] +use linux as native; + +#[cfg(target_os = "macos")] +mod mac; +#[cfg(target_os = "macos")] +use mac as native; + +#[cfg(target_os = "windows")] +mod win; +#[cfg(target_os = "windows")] +use win as native; + +pub(crate) struct Window { + winit_window: Arc, + #[allow(dead_code)] + native_handle: native::NativeWindowImpl, +} + +impl Window { + pub(crate) fn new(event_loop: &dyn ActiveEventLoop) -> Self { + let mut attributes = WindowAttributes::default() + .with_title(APP_NAME) + .with_min_surface_size(winit::dpi::LogicalSize::new(400, 300)) + .with_surface_size(winit::dpi::LogicalSize::new(1200, 800)) + .with_resizable(true) + .with_theme(Some(winit::window::Theme::Dark)); + + attributes = native::NativeWindowImpl::configure(attributes, event_loop); + + let winit_window = event_loop.create_window(attributes).unwrap(); + let native_handle = native::NativeWindowImpl::new(winit_window.as_ref()); + Self { + winit_window: winit_window.into(), + native_handle, + } + } + + pub(crate) fn request_redraw(&self) { + self.winit_window.request_redraw(); + } + + pub(crate) fn create_surface(&self, instance: Arc) -> wgpu::Surface<'static> { + instance.create_surface(self.winit_window.clone()).unwrap() + } + + pub(crate) fn pre_present_notify(&self) { + self.winit_window.pre_present_notify(); + } + + pub(crate) fn surface_size(&self) -> winit::dpi::PhysicalSize { + self.winit_window.surface_size() + } + + pub(crate) fn minimize(&self) { + self.winit_window.set_minimized(true); + } + + pub(crate) fn toggle_maximize(&self) { + self.winit_window.set_maximized(!self.winit_window.is_maximized()); + } + + pub(crate) fn is_maximized(&self) -> bool { + self.winit_window.is_maximized() + } + + pub(crate) fn start_drag(&self) { + let _ = self.winit_window.drag_window(); + } + + pub(crate) fn set_cursor(&self, cursor: winit::cursor::Cursor) { + self.winit_window.set_cursor(cursor); + } +} diff --git a/desktop/src/window/linux.rs b/desktop/src/window/linux.rs new file mode 100644 index 0000000000..5a48385acc --- /dev/null +++ b/desktop/src/window/linux.rs @@ -0,0 +1,27 @@ +use winit::event_loop::ActiveEventLoop; +use winit::platform::wayland::ActiveEventLoopExtWayland; +use winit::platform::wayland::WindowAttributesWayland; +use winit::platform::x11::WindowAttributesX11; +use winit::window::{Window, WindowAttributes}; + +use crate::consts::{APP_ID, APP_NAME}; + +use super::NativeWindow; + +pub(super) struct NativeWindowImpl {} + +impl NativeWindow for NativeWindowImpl { + fn configure(attributes: WindowAttributes, event_loop: &dyn ActiveEventLoop) -> WindowAttributes { + if event_loop.is_wayland() { + let wayland_attributes = WindowAttributesWayland::default().with_name(APP_ID, "").with_prefer_csd(true); + attributes.with_platform_attributes(Box::new(wayland_attributes)) + } else { + let x11_attributes = WindowAttributesX11::default().with_name(APP_ID, APP_NAME); + attributes.with_platform_attributes(Box::new(x11_attributes)) + } + } + + fn new(_window: &dyn Window) -> Self { + NativeWindowImpl {} + } +} diff --git a/desktop/src/window/mac.rs b/desktop/src/window/mac.rs new file mode 100644 index 0000000000..83386909b5 --- /dev/null +++ b/desktop/src/window/mac.rs @@ -0,0 +1,20 @@ +use winit::event_loop::ActiveEventLoop; +use winit::window::{Window, WindowAttributes}; + +use super::NativeWindow; + +pub(super) struct NativeWindowImpl {} + +impl NativeWindow for NativeWindowImpl { + fn configure(attributes: WindowAttributes, _event_loop: &dyn ActiveEventLoop) -> WindowAttributes { + let mac_window = winit::platform::macos::WindowAttributesMacOS::default() + .with_titlebar_transparent(true) + .with_fullsize_content_view(true) + .with_title_hidden(true); + attributes.with_platform_attributes(Box::new(mac_window)) + } + + fn new(_window: &dyn Window) -> Self { + NativeWindowImpl {} + } +} diff --git a/desktop/src/window/win.rs b/desktop/src/window/win.rs new file mode 100644 index 0000000000..8f23866f08 --- /dev/null +++ b/desktop/src/window/win.rs @@ -0,0 +1,32 @@ +use winit::event_loop::ActiveEventLoop; +use winit::window::{Window, WindowAttributes}; + +use super::NativeWindow; + +pub(super) struct NativeWindowImpl { + native_handle: native_handle::NativeWindowHandle, +} + +impl NativeWindow for NativeWindowImpl { + fn configure(attributes: WindowAttributes, _event_loop: &dyn ActiveEventLoop) -> WindowAttributes { + if let Ok(win_icon) = winit::platform::windows::WinIcon::from_resource(1, None) { + let icon = winit::icon::Icon(std::sync::Arc::new(win_icon)); + attributes.with_window_icon(Some(icon)) + } else { + attributes + } + } + + fn new(window: &dyn Window) -> Self { + let native_handle = native_handle::NativeWindowHandle::new(window); + NativeWindowImpl { native_handle } + } +} + +impl Drop for NativeWindowImpl { + fn drop(&mut self) { + self.native_handle.destroy(); + } +} + +mod native_handle; diff --git a/desktop/src/native_window/windows.rs b/desktop/src/window/win/native_handle.rs similarity index 92% rename from desktop/src/native_window/windows.rs rename to desktop/src/window/win/native_handle.rs index 849de302fe..17ff1f19fa 100644 --- a/desktop/src/native_window/windows.rs +++ b/desktop/src/window/win/native_handle.rs @@ -21,29 +21,14 @@ use windows::Win32::UI::WindowsAndMessaging::*; use windows::core::PCWSTR; use winit::window::Window; -pub(super) struct WindowsNativeWindowHandle { - inner: WindowsNativeWindowHandleInner, -} -impl WindowsNativeWindowHandle { - pub(super) fn new(window: &dyn Window) -> Self { - let inner = WindowsNativeWindowHandleInner::new(window); - WindowsNativeWindowHandle { inner } - } -} -impl Drop for WindowsNativeWindowHandle { - fn drop(&mut self) { - self.inner.destroy(); - } -} - #[derive(Clone)] -struct WindowsNativeWindowHandleInner { +pub(super) struct NativeWindowHandle { main: HWND, helper: HWND, prev_window_message_handler: isize, } -impl WindowsNativeWindowHandleInner { - fn new(window: &dyn Window) -> WindowsNativeWindowHandleInner { +impl NativeWindowHandle { + pub(super) fn new(window: &dyn Window) -> NativeWindowHandle { // Extract Win32 HWND from winit. let hwnd = match window.window_handle().expect("No window handle").as_raw() { RawWindowHandle::Win32(h) => HWND(h.hwnd.get() as *mut std::ffi::c_void), @@ -85,7 +70,7 @@ impl WindowsNativeWindowHandleInner { panic!("SetWindowLongPtrW failed"); } - let inner = WindowsNativeWindowHandleInner { + let inner = NativeWindowHandle { main: hwnd, helper, prev_window_message_handler, @@ -115,7 +100,7 @@ impl WindowsNativeWindowHandleInner { inner } - fn destroy(&self) { + pub(super) fn destroy(&self) { registry::remove_by_main(self.main); // Undo subclassing and destroy the helper window. @@ -130,13 +115,13 @@ mod registry { use std::cell::RefCell; use windows::Win32::Foundation::HWND; - use crate::native_window::windows::WindowsNativeWindowHandleInner; + use super::NativeWindowHandle; thread_local! { - static STORE: RefCell> = RefCell::new(Vec::new()); + static STORE: RefCell> = RefCell::new(Vec::new()); } - pub(super) fn find_by_main(main: HWND) -> Option { + pub(super) fn find_by_main(main: HWND) -> Option { STORE.with_borrow(|vec| vec.iter().find(|h| h.main == main).cloned()) } pub(super) fn remove_by_main(main: HWND) { @@ -144,7 +129,7 @@ mod registry { vec.retain(|h| h.main != main); }); } - pub(super) fn insert(handle: &WindowsNativeWindowHandleInner) { + pub(super) fn insert(handle: &NativeWindowHandle) { STORE.with_borrow_mut(|vec| { vec.push(handle.clone()); });