diff --git a/Cargo.toml b/Cargo.toml index f2a067fe..60b3a6fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ uuid = [] # Deprecated; this feature was provided by the now unneeded uuid depen [dependencies] keyboard-types = { version = "0.6.1", default-features = false } -raw-window-handle = "0.5" +raw-window-handle = "0.6.2" [target.'cfg(target_os="linux")'.dependencies] x11rb = { version = "0.13.2", features = ["cursor", "resource_manager", "allow-unsafe-code", "dl-libxcb"], default-features = false } diff --git a/examples/open_parented/Cargo.toml b/examples/open_parented/Cargo.toml index f783dd84..2569efda 100644 --- a/examples/open_parented/Cargo.toml +++ b/examples/open_parented/Cargo.toml @@ -6,5 +6,4 @@ publish = false [dependencies] baseview = { path = "../.." } -softbuffer = "0.3.4" -rtrb = "0.3.4" +softbuffer = "0.4.8" diff --git a/examples/open_parented/src/main.rs b/examples/open_parented/src/main.rs index 8f7222c8..7f12deab 100644 --- a/examples/open_parented/src/main.rs +++ b/examples/open_parented/src/main.rs @@ -1,13 +1,12 @@ use baseview::{ - Event, EventStatus, PhySize, Window, WindowEvent, WindowHandle, WindowHandler, + Event, EventStatus, PhySize, Window, WindowContext, WindowEvent, WindowHandle, WindowHandler, WindowOpenOptions, }; use std::cell::{Cell, RefCell}; use std::num::NonZeroU32; struct ParentWindowHandler { - _ctx: softbuffer::Context, - surface: RefCell, + surface: RefCell>, current_size: Cell, damaged: Cell, @@ -15,20 +14,19 @@ struct ParentWindowHandler { } impl ParentWindowHandler { - pub fn new(window: &mut Window) -> Self { - let ctx = unsafe { softbuffer::Context::new(window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&ctx, window) }.unwrap(); + pub fn new(window: WindowContext) -> Self { + let ctx = softbuffer::Context::new(window.clone()).unwrap(); + let mut surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap(); surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap(); let window_open_options = WindowOpenOptions::new().with_size(256.0, 256.0).with_title("baseview child"); let child_window = - Window::open_parented(window, window_open_options, ChildWindowHandler::new); + Window::open_parented(&window, window_open_options, ChildWindowHandler::new); // TODO: no way to query physical size initially? Self { - _ctx: ctx, surface: surface.into(), current_size: PhySize::new(512, 512).into(), damaged: true.into(), @@ -38,7 +36,7 @@ impl ParentWindowHandler { } impl WindowHandler for ParentWindowHandler { - fn on_frame(&self, _window: &mut Window) { + fn on_frame(&self) { let mut surface = self.surface.borrow_mut(); let mut buf = surface.buffer_mut().unwrap(); if self.damaged.get() { @@ -48,7 +46,7 @@ impl WindowHandler for ParentWindowHandler { buf.present().unwrap(); } - fn on_event(&self, _window: &mut Window, event: Event) -> EventStatus { + fn on_event(&self, event: Event) -> EventStatus { match event { Event::Window(WindowEvent::Resized(info)) => { println!("Parent Resized: {:?}", info); @@ -72,21 +70,19 @@ impl WindowHandler for ParentWindowHandler { } struct ChildWindowHandler { - _ctx: softbuffer::Context, - surface: RefCell, + surface: RefCell>, current_size: Cell, damaged: Cell, } impl ChildWindowHandler { - pub fn new(window: &mut Window) -> Self { - let ctx = unsafe { softbuffer::Context::new(window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&ctx, window) }.unwrap(); + pub fn new(window: WindowContext) -> Self { + let ctx = softbuffer::Context::new(window.clone()).unwrap(); + let mut surface = softbuffer::Surface::new(&ctx, window).unwrap(); surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap(); // TODO: no way to query physical size initially? Self { - _ctx: ctx, surface: surface.into(), current_size: PhySize::new(256, 256).into(), damaged: true.into(), @@ -95,7 +91,7 @@ impl ChildWindowHandler { } impl WindowHandler for ChildWindowHandler { - fn on_frame(&self, _window: &mut Window) { + fn on_frame(&self) { let mut surface = self.surface.borrow_mut(); let mut buf = surface.buffer_mut().unwrap(); if self.damaged.get() { @@ -105,7 +101,7 @@ impl WindowHandler for ChildWindowHandler { buf.present().unwrap(); } - fn on_event(&self, _window: &mut Window, event: Event) -> EventStatus { + fn on_event(&self, event: Event) -> EventStatus { match event { Event::Window(WindowEvent::Resized(info)) => { println!("Child Resized: {:?}", info); diff --git a/examples/open_window/Cargo.toml b/examples/open_window/Cargo.toml index 43fbb5a6..34ad3b83 100644 --- a/examples/open_window/Cargo.toml +++ b/examples/open_window/Cargo.toml @@ -6,5 +6,5 @@ publish = false [dependencies] baseview = { path = "../.." } -softbuffer = "0.3.4" +softbuffer = "0.4.8" rtrb = "0.3.4" diff --git a/examples/open_window/src/main.rs b/examples/open_window/src/main.rs index 84d9df63..4c97a1ab 100644 --- a/examples/open_window/src/main.rs +++ b/examples/open_window/src/main.rs @@ -7,8 +7,8 @@ use rtrb::{Consumer, RingBuffer}; #[cfg(target_os = "macos")] use baseview::copy_to_clipboard; use baseview::{ - Event, EventStatus, MouseEvent, PhyPoint, PhySize, Window, WindowEvent, WindowHandler, - WindowInfo, WindowOpenOptions, + Event, EventStatus, MouseEvent, PhyPoint, PhySize, Window, WindowContext, WindowEvent, + WindowHandler, WindowInfo, WindowOpenOptions, }; #[derive(Debug, Clone)] @@ -19,8 +19,7 @@ enum Message { struct OpenWindowExample { rx: RefCell>, - _ctx: softbuffer::Context, - surface: RefCell, + surface: RefCell>, current_size: Cell, mouse_pos: Cell, is_cursor_inside: Cell, @@ -28,7 +27,7 @@ struct OpenWindowExample { } impl WindowHandler for OpenWindowExample { - fn on_frame(&self, _window: &mut Window) { + fn on_frame(&self) { if !self.damaged.get() { return; } @@ -98,7 +97,7 @@ impl WindowHandler for OpenWindowExample { } } - fn on_event(&self, _window: &mut Window, event: Event) -> EventStatus { + fn on_event(&self, event: Event) -> EventStatus { match &event { #[cfg(target_os = "macos")] Event::Mouse(MouseEvent::ButtonPressed { .. }) => copy_to_clipboard("This is a test!"), @@ -151,12 +150,11 @@ fn main() { }); Window::open_blocking(window_open_options, |window| { - let ctx = unsafe { softbuffer::Context::new(window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&ctx, window) }.unwrap(); + let ctx = softbuffer::Context::new(window.clone()).unwrap(); + let mut surface = softbuffer::Surface::new(&ctx, window).unwrap(); surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap(); OpenWindowExample { - _ctx: ctx, surface: surface.into(), rx: rx.into(), current_size: WindowInfo::from_physical_size(PhySize::new(512, 512), 1.0).into(), diff --git a/examples/render_femtovg/Cargo.toml b/examples/render_femtovg/Cargo.toml index 7607f591..2271a344 100644 --- a/examples/render_femtovg/Cargo.toml +++ b/examples/render_femtovg/Cargo.toml @@ -6,4 +6,4 @@ publish = false [dependencies] baseview = { path = "../..", features = ["opengl"] } -femtovg = "0.9.0" \ No newline at end of file +femtovg = "0.25.1" diff --git a/examples/render_femtovg/src/main.rs b/examples/render_femtovg/src/main.rs index 67a581ed..7ab894d5 100644 --- a/examples/render_femtovg/src/main.rs +++ b/examples/render_femtovg/src/main.rs @@ -1,13 +1,15 @@ -use baseview::gl::GlConfig; +use baseview::gl::{GlConfig, GlContext}; use baseview::{ - Event, EventStatus, MouseEvent, PhyPoint, Size, Window, WindowEvent, WindowHandler, WindowInfo, - WindowOpenOptions, + Event, EventStatus, MouseEvent, PhyPoint, Size, Window, WindowContext, WindowEvent, + WindowHandler, WindowInfo, WindowOpenOptions, }; use femtovg::renderer::OpenGl; use femtovg::{Canvas, Color}; use std::cell::{Cell, RefCell}; struct FemtovgExample { + window_context: WindowContext, + gl_context: GlContext, canvas: RefCell>, current_size: Cell, current_mouse_position: Cell, @@ -15,19 +17,21 @@ struct FemtovgExample { } impl FemtovgExample { - fn new(window: &mut Window) -> Self { - let context = window.gl_context().unwrap(); - unsafe { context.make_current() }; + fn new(window_context: WindowContext) -> Self { + let gl_context = window_context.gl_context().unwrap(); + unsafe { gl_context.make_current() }; let renderer = - unsafe { OpenGl::new_from_function(|s| context.get_proc_address(s)) }.unwrap(); + unsafe { OpenGl::new_from_function(|s| gl_context.get_proc_address(s)) }.unwrap(); let mut canvas = Canvas::new(renderer).unwrap(); // TODO: get actual window width canvas.set_size(512, 512, 1.0); - unsafe { context.make_not_current() }; + unsafe { gl_context.make_not_current() }; Self { + gl_context, + window_context, canvas: canvas.into(), current_size: WindowInfo::from_logical_size(Size { width: 512.0, height: 512.0 }, 1.0) .into(), @@ -38,12 +42,12 @@ impl FemtovgExample { } impl WindowHandler for FemtovgExample { - fn on_frame(&self, window: &mut Window) { + fn on_frame(&self) { if !self.damaged.get() { return; } - let context = window.gl_context().unwrap(); + let context = &self.gl_context; unsafe { context.make_current() }; let mut canvas = self.canvas.borrow_mut(); @@ -79,7 +83,7 @@ impl WindowHandler for FemtovgExample { self.damaged.set(false); } - fn on_event(&self, _window: &mut Window, event: Event) -> EventStatus { + fn on_event(&self, event: Event) -> EventStatus { match event { Event::Window(WindowEvent::Resized(size)) => { let phy_size = size.physical_size(); @@ -98,6 +102,9 @@ impl WindowHandler for FemtovgExample { | MouseEvent::DragDropped { position, .. }, ) => { self.current_mouse_position.set(position.to_physical(&self.current_size.get())); + if self.current_mouse_position.get().y > 400 && !self.window_context.has_focus() { + self.window_context.focus() + } self.damaged.set(true); } _ => {} diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 00000000..3cd18569 --- /dev/null +++ b/src/context.rs @@ -0,0 +1,52 @@ +use crate::{platform, MouseCursor, Size}; +use raw_window_handle::{ + DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle, +}; + +#[derive(Clone)] +pub struct WindowContext { + inner: platform::WindowContext, +} + +impl WindowContext { + pub(crate) fn new(inner: platform::WindowContext) -> Self { + Self { inner } + } + + pub fn set_mouse_cursor(&self, mouse_cursor: MouseCursor) { + self.inner.set_mouse_cursor(mouse_cursor); + } + + pub fn close(&self) { + self.inner.close(); + } + + pub fn has_focus(&self) -> bool { + self.inner.has_focus() + } + + pub fn focus(&self) { + self.inner.focus(); + } + + pub fn resize(&self, size: Size) { + self.inner.resize(size); + } + + #[cfg(feature = "opengl")] + pub fn gl_context(&self) -> Option { + self.inner.gl_context() + } +} + +impl HasWindowHandle for WindowContext { + fn window_handle(&self) -> Result, HandleError> { + self.inner.window_handle().ok_or(HandleError::Unavailable) + } +} + +impl HasDisplayHandle for WindowContext { + fn display_handle(&self) -> Result, HandleError> { + Ok(self.inner.display_handle()) + } +} diff --git a/src/gl.rs b/src/gl.rs index 2e2d1b45..4b020535 100644 --- a/src/gl.rs +++ b/src/gl.rs @@ -1,7 +1,6 @@ use crate::platform::gl::*; use std::ffi::c_void; use std::marker::PhantomData; -use std::panic::AssertUnwindSafe; #[derive(Clone, Debug, PartialEq)] pub struct GlConfig { @@ -45,22 +44,22 @@ pub enum Profile { } #[derive(Debug)] +#[non_exhaustive] pub enum GlError { - InvalidWindowHandle, VersionNotSupported, CreationFailed(CreationFailedError), } +#[derive(Clone)] pub struct GlContext { - // AssertUnwindSafe should *not* be here, but this is needed for now to keep semver compatibility - // Remove this in 0.2 - pub(crate) inner: AssertUnwindSafe, - phantom: PhantomData<*mut ()>, + inner: crate::platform::gl::GlContext, + // To make sure this is !Send, !Sync, and !UnwindSafe on all platforms + phantom: PhantomData<(*mut (), &'static mut ())>, } impl GlContext { pub(crate) fn new(context: crate::platform::gl::GlContext) -> GlContext { - GlContext { inner: AssertUnwindSafe(context), phantom: PhantomData } + GlContext { inner: context, phantom: PhantomData } } pub unsafe fn make_current(&self) { diff --git a/src/handler.rs b/src/handler.rs new file mode 100644 index 00000000..21db66e8 --- /dev/null +++ b/src/handler.rs @@ -0,0 +1,6 @@ +use crate::{Event, EventStatus}; + +pub trait WindowHandler: 'static { + fn on_frame(&self); + fn on_event(&self, event: Event) -> EventStatus; +} diff --git a/src/lib.rs b/src/lib.rs index 7941c52f..15190678 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,7 @@ mod clipboard; +mod context; mod event; +mod handler; mod keyboard; mod mouse_cursor; mod window; @@ -12,7 +14,9 @@ pub(crate) mod platform; pub mod gl; pub use clipboard::*; +pub use context::WindowContext; pub use event::*; +pub use handler::WindowHandler; pub use mouse_cursor::MouseCursor; pub use window::*; pub use window_info::*; diff --git a/src/platform/macos/context.rs b/src/platform/macos/context.rs new file mode 100644 index 00000000..432f8c1a --- /dev/null +++ b/src/platform/macos/context.rs @@ -0,0 +1,78 @@ +use crate::platform::macos::cursor::Cursor; +use crate::platform::macos::view::BaseviewView; +use crate::wrappers::appkit::{View, ViewRef}; +use crate::{MouseCursor, Size}; +use objc2::rc::Weak; +use objc2::runtime::NSObjectProtocol; +use objc2::Message; +use raw_window_handle::DisplayHandle; + +#[derive(Clone)] +pub struct WindowContext { + view: Weak>, +} + +impl WindowContext { + pub(crate) fn new(view: ViewRef<'_, BaseviewView>) -> Self { + view.view.retain(); + Self { view: Weak::from_retained(&view.view.retain()) } + } + + pub fn close(&self) { + let Some(view) = self.view.load() else { return }; + BaseviewView::close(view.inner_ref()); + } + + pub fn has_focus(&self) -> bool { + let Some(view) = self.view.load() else { return false }; + let Some(window) = view.window() else { + return false; + }; + + if !window.isKeyWindow() { + return false; + } + + let Some(first_responder) = window.firstResponder() else { + return false; + }; + + view.isEqual(Some(&*first_responder)) + } + + pub fn focus(&self) { + let Some(view) = self.view.load() else { return }; + if let Some(window) = view.window() { + window.makeFirstResponder(Some(&view)); + } + } + + pub fn resize(&self, size: Size) { + let Some(view) = self.view.load() else { return }; + let view = view.inner_ref(); + if view.inner.state.closed.get() { + return; + } + + BaseviewView::resize(view, size); + } + + pub fn set_mouse_cursor(&self, cursor: MouseCursor) { + let Some(view) = self.view.load() else { return }; + let native_cursor = Cursor::from(cursor); + view.addCursorRect_cursor(view.bounds(), &native_cursor.load()); + } + + #[cfg(feature = "opengl")] + pub fn gl_context(&self) -> Option { + Some(crate::gl::GlContext::new(self.view.load()?.inner().gl_context.get()?.clone())) + } + + pub fn window_handle(&self) -> Option> { + View::window_handle_from_weak(&self.view) + } + + pub fn display_handle(&self) -> DisplayHandle<'_> { + DisplayHandle::appkit() + } +} diff --git a/src/platform/macos/gl.rs b/src/platform/macos/gl.rs index 564f2461..ff9b39fd 100644 --- a/src/platform/macos/gl.rs +++ b/src/platform/macos/gl.rs @@ -17,6 +17,7 @@ use std::ffi::c_void; use std::ptr::NonNull; pub type CreationFailedError = (); +#[derive(Clone)] pub struct GlContext { pub(crate) view: Retained, context: Retained, diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index 5cb70243..614b8f9f 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -1,8 +1,10 @@ +mod context; mod cursor; mod keyboard; mod view; mod window; +pub use context::WindowContext; pub use window::*; #[cfg(feature = "opengl")] diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index d2180c6c..fdf1d8c4 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -2,6 +2,7 @@ use super::keyboard::{make_modifiers, KeyboardState}; use super::window::WindowSharedState; +use crate::platform::macos::context::WindowContext; use crate::wrappers::appkit::*; use crate::MouseEvent::{ButtonPressed, ButtonReleased}; use crate::{ @@ -37,12 +38,13 @@ pub(crate) struct BaseviewView { parenting: ViewParentingType, #[cfg(feature = "opengl")] - pub(crate) gl_context: OnceCell, + pub(crate) gl_context: OnceCell, } impl BaseviewView { pub fn new( - options: WindowOpenOptions, builder: impl FnOnce(&mut crate::Window) -> H, + options: WindowOpenOptions, + builder: impl FnOnce(crate::WindowContext) -> H + Send + 'static, parenting: ViewParentingType, ) -> (Retained>, Rc) { let view_rect = @@ -80,14 +82,14 @@ impl BaseviewView { #[cfg(feature = "opengl")] if let Some(gl_config) = options.gl_config { let gl_context = super::gl::GlContext::create(view.view, gl_config).unwrap(); - let gl_context = crate::gl::GlContext::new(gl_context); let Ok(()) = view.gl_context.set(gl_context) else { unreachable!() }; } + let context = WindowContext::new(view); + let handler = Box::new(builder(crate::WindowContext::new(context))); + // Initialize handler - let Ok(()) = view.window_handler.set(Box::new(builder(&mut view.into()))) else { - unreachable!() - }; + let Ok(()) = view.window_handler.set(handler) else { unreachable!() }; // Set up anything that might trigger events to the handler @@ -149,7 +151,7 @@ impl BaseviewView { // macOS. #[cfg(feature = "opengl")] if let Some(gl_context) = this.gl_context.get() { - gl_context.inner.resize(size); + gl_context.resize(size); } // If this is a standalone window then we'll also need to resize the window itself @@ -168,13 +170,13 @@ impl BaseviewView { return EventStatus::Ignored; }; - handler.on_event(&mut this.into(), event) + handler.on_event(event) } fn trigger_frame(this: ViewRef) { let Some(handler) = this.window_handler.get() else { return }; - handler.on_frame(&mut this.into()); + handler.on_frame(); } fn fetch_view_size(view: &NSView) -> WindowInfo { @@ -268,7 +270,7 @@ impl ViewImpl for BaseviewView { #[cfg(feature = "opengl")] { if let Some(gl_context) = this.gl_context.get() { - if *super_result == **gl_context.inner.0.view { + if *super_result == **gl_context.view { return Some(this.view); } } diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 64405957..aab2d7b8 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -2,24 +2,16 @@ use std::cell::{Cell, RefCell}; use std::rc::Rc; use objc2::rc::{autoreleasepool, Weak}; -use objc2::runtime::NSObjectProtocol; use objc2::MainThreadMarker; use objc2_app_kit::{ NSApplication, NSApplicationActivationPolicy, NSPasteboard, NSPasteboardTypeString, }; use objc2_foundation::NSString; -use raw_window_handle::{ - AppKitDisplayHandle, AppKitWindowHandle, HasRawDisplayHandle, HasRawWindowHandle, - RawDisplayHandle, RawWindowHandle, -}; - -use super::cursor::Cursor; -use crate::{MouseCursor, Size, WindowHandler, WindowInfo, WindowOpenOptions}; +use raw_window_handle::HasWindowHandle; -#[cfg(feature = "opengl")] -use crate::gl::GlContext; use crate::platform::macos::view::{BaseviewView, ViewParentingType}; -use crate::wrappers::appkit::{create_window, extract_raw_window_handle, View, ViewRef}; +use crate::wrappers::appkit::{create_window, extract_raw_window_handle, View}; +use crate::{WindowContext, WindowHandler, WindowInfo, WindowOpenOptions}; pub struct WindowHandle { view: RefCell>>>, @@ -40,40 +32,16 @@ impl WindowHandle { } } -unsafe impl HasRawWindowHandle for WindowHandle { - fn raw_window_handle(&self) -> RawWindowHandle { - let Some(view) = self.view.borrow().as_ref().and_then(|w| w.load()) else { - return AppKitWindowHandle::empty().into(); - }; - - view.raw_window_handle() - } -} - -pub struct Window<'a> { - view: &'a View, - inner: &'a BaseviewView, -} - -impl<'a> From> for crate::Window<'a> { - fn from(value: ViewRef<'a, BaseviewView>) -> Self { - crate::Window::new(Window { view: value.view, inner: value.inner }) - } -} +pub struct Window; -impl<'a> Window<'a> { - pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle - where - P: HasRawWindowHandle, - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { +impl Window { + pub fn open_parented( + parent: &impl HasWindowHandle, options: WindowOpenOptions, + build: impl FnOnce(WindowContext) -> H + Send + 'static, + ) -> WindowHandle { autoreleasepool(|_| { - let (_parent_window, parent_view) = - extract_raw_window_handle(parent.raw_window_handle()); - - let Some(parent_view) = parent_view else { + let Some(parent_view) = extract_raw_window_handle(parent.window_handle().unwrap()) + else { panic!("Invalid window handle: ns_view is NULL"); }; @@ -86,12 +54,9 @@ impl<'a> Window<'a> { }) } - pub fn open_blocking(options: WindowOpenOptions, build: B) - where - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { + pub fn open_blocking( + options: WindowOpenOptions, build: impl FnOnce(WindowContext) -> H + Send + 'static, + ) { autoreleasepool(|_| { let Some(mtm) = MainThreadMarker::new() else { panic!("macOS: open_blocking can only be called on the main thread!") @@ -119,50 +84,6 @@ impl<'a> Window<'a> { app.run(); }) } - - pub fn close(&self) { - BaseviewView::close(self.view.inner_ref()); - } - - pub fn has_focus(&self) -> bool { - let Some(window) = self.view.window() else { - return false; - }; - - if !window.isKeyWindow() { - return false; - } - - let Some(first_responder) = window.firstResponder() else { - return false; - }; - - self.view.isEqual(Some(&*first_responder)) - } - - pub fn focus(&self) { - if let Some(window) = self.view.window() { - window.makeFirstResponder(Some(self.view)); - } - } - - pub fn resize(&self, size: Size) { - if self.inner.state.closed.get() { - return; - } - - BaseviewView::resize(self.view.inner_ref(), size); - } - - pub fn set_mouse_cursor(&self, cursor: MouseCursor) { - let native_cursor = Cursor::from(cursor); - self.view.addCursorRect_cursor(self.view.bounds(), &native_cursor.load()); - } - - #[cfg(feature = "opengl")] - pub fn gl_context(&self) -> Option<&GlContext> { - self.inner.gl_context.get() - } } pub(crate) struct WindowSharedState { @@ -180,18 +101,6 @@ impl WindowSharedState { } } -unsafe impl<'a> HasRawWindowHandle for Window<'a> { - fn raw_window_handle(&self) -> RawWindowHandle { - self.view.raw_window_handle() - } -} - -unsafe impl<'a> HasRawDisplayHandle for Window<'a> { - fn raw_display_handle(&self) -> RawDisplayHandle { - RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()) - } -} - pub fn copy_to_clipboard(string: &str) { let pb = NSPasteboard::generalPasteboard(); let ns_str = NSString::from_str(string); diff --git a/src/platform/win/drop_target.rs b/src/platform/win/drop_target.rs index 5d79e6b2..7b41da47 100644 --- a/src/platform/win/drop_target.rs +++ b/src/platform/win/drop_target.rs @@ -13,10 +13,9 @@ use windows_sys::Win32::{ Foundation::POINT, Graphics::Gdi::ScreenToClient, UI::Shell::DragQueryFileW, }; +use super::window_state::WindowState; use crate::{DropData, DropEffect, Event, EventStatus, MouseEvent, PhyPoint, Point}; -use super::WindowState; - #[implement(IDropTarget)] pub(crate) struct DropTarget { window_state: Weak, diff --git a/src/platform/win/gl.rs b/src/platform/win/gl.rs index a173daf9..12092274 100644 --- a/src/platform/win/gl.rs +++ b/src/platform/win/gl.rs @@ -1,6 +1,6 @@ use std::ffi::{c_void, CString, OsStr}; use std::os::windows::ffi::OsStrExt; - +use std::rc::Rc; use windows_sys::{ core::s, Win32::{ @@ -25,11 +25,9 @@ use windows_sys::{ }, }; -use raw_window_handle::RawWindowHandle; - use crate::gl::*; use crate::wrappers::win32::uuid::Uuid; - +use crate::wrappers::win32::window::HWnd; // See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt type WglCreateContextAttribsARB = extern "system" fn(HDC, HGLRC, *const i32) -> HGLRC; @@ -75,7 +73,9 @@ const WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB: i32 = 0x20A9; type WglSwapIntervalEXT = extern "system" fn(i32) -> i32; pub type CreationFailedError = (); -pub struct GlContext { +pub type GlContext = Rc; + +pub struct GlContextInner { hwnd: HWND, hdc: HDC, hglrc: HGLRC, @@ -86,18 +86,8 @@ extern "C" { static __ImageBase: IMAGE_DOS_HEADER; } -impl GlContext { - pub unsafe fn create(parent: &RawWindowHandle, config: GlConfig) -> Result { - let handle = if let RawWindowHandle::Win32(handle) = parent { - handle - } else { - return Err(GlError::InvalidWindowHandle); - }; - - if handle.hwnd.is_null() { - return Err(GlError::InvalidWindowHandle); - } - +impl GlContextInner { + pub unsafe fn create(window: HWnd, config: GlConfig) -> Result { // Create temporary window and context to load function pointers let class_name_str = format!("raw-gl-context-window-{}", Uuid::new()); @@ -188,7 +178,7 @@ impl GlContext { // Create actual context - let hwnd = handle.hwnd; + let hwnd = window.as_raw(); let hdc = GetDC(hwnd); @@ -295,7 +285,7 @@ impl GlContext { wglSwapIntervalEXT.unwrap()(config.vsync as i32); wglMakeCurrent(hdc, std::ptr::null_mut()); - Ok(GlContext { hwnd, hdc, hglrc, gl_library }) + Ok(Self { hwnd, hdc, hglrc, gl_library }) } pub unsafe fn make_current(&self) { @@ -327,7 +317,7 @@ impl GlContext { } } -impl Drop for GlContext { +impl Drop for GlContextInner { fn drop(&mut self) { unsafe { wglMakeCurrent(std::ptr::null_mut(), std::ptr::null_mut()); diff --git a/src/platform/win/mod.rs b/src/platform/win/mod.rs index fb1f608b..ca6b84a6 100644 --- a/src/platform/win/mod.rs +++ b/src/platform/win/mod.rs @@ -2,8 +2,12 @@ mod drop_target; mod hook; mod keyboard; mod window; +mod window_state; +use std::rc::Rc; pub use window::*; #[cfg(feature = "opengl")] pub mod gl; + +pub type WindowContext = Rc; diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 3209ed08..b82b7381 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -13,28 +13,23 @@ use windows_sys::Win32::{ }, }; -use std::cell::{Cell, OnceCell, Ref, RefCell}; +use std::cell::Cell; use std::num::NonZeroUsize; use std::ptr::null_mut; -use std::rc::Rc; -use raw_window_handle::{ - HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, Win32WindowHandle, - WindowsDisplayHandle, -}; +use raw_window_handle::{HasWindowHandle, RawWindowHandle}; -const BV_WINDOW_MUST_CLOSE: u32 = WM_USER + 1; +pub(crate) const BV_WINDOW_MUST_CLOSE: u32 = WM_USER + 1; +use super::drop_target::DropTarget; use super::*; +use crate::platform::win::window_state::WindowState; +use crate::wrappers::win32::cursor::SystemCursor; use crate::{ - Event, EventStatus, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, - WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, + Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent, + WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; -use super::drop_target::DropTarget; -use super::keyboard::KeyboardState; -use crate::wrappers::win32::cursor::SystemCursor; - use crate::wrappers::win32::window::*; use crate::wrappers::win32::{ ole_initialize, run_thread_message_loop_until, Dpi, DpiAwarenessContext, ExtendedUser32, Rect, @@ -75,19 +70,6 @@ impl WindowHandle { } } -unsafe impl HasRawWindowHandle for WindowHandle { - fn raw_window_handle(&self) -> RawWindowHandle { - if let Some(hwnd) = self.hwnd.get() { - let mut handle = Win32WindowHandle::empty(); - handle.hwnd = hwnd; - - RawWindowHandle::Win32(handle) - } else { - RawWindowHandle::Win32(Win32WindowHandle::empty()) - } - } -} - struct ParentHandle { is_open: Rc>, } @@ -98,7 +80,7 @@ impl Drop for ParentHandle { } } -type HandlerBuilder = dyn FnOnce(&mut crate::Window) -> Box; +type HandlerBuilder = dyn FnOnce(crate::WindowContext) -> Box; pub struct BaseviewWindow { window_state: Rc, @@ -154,23 +136,17 @@ impl WindowImpl for BaseviewWindow { #[cfg(feature = "opengl")] if let Some(gl_config) = self.gl_config.clone() { - let mut handle = Win32WindowHandle::empty(); - handle.hwnd = hwnd; - let handle = RawWindowHandle::Win32(handle); - - let gl_context = unsafe { gl::GlContext::create(&handle, gl_config) } + let gl_context = unsafe { gl::GlContextInner::create(window, gl_config) } .expect("Could not create OpenGL context"); - let Ok(()) = self.window_state.gl_context.set(crate::gl::GlContext::new(gl_context)) - else { + let Ok(()) = self.window_state.gl_context.set(Rc::new(gl_context)) else { unreachable!(); }; }; let handler = { - let mut window = crate::Window::new(Window { state: window_state }); - - self.handler_builder.take().unwrap()(&mut window) + let context = crate::WindowContext::new(Rc::clone(&self.window_state)); + self.handler_builder.take().unwrap()(context) }; let Ok(()) = window_state.handler.set(handler) else { unreachable!() }; @@ -435,122 +411,34 @@ unsafe fn wnd_proc_inner( } } -/// All data associated with the window. -pub(crate) struct WindowState { - /// The HWND belonging to this window. - pub hwnd: HWND, - current_size: Cell, - current_dpi: Cell, // None if in non-system scale policy - keyboard_state: RefCell, - mouse_button_counter: Cell, - mouse_was_outside_window: Cell, - cursor_icon: Cell, - // Initialized late so the `Window` can hold a reference to this `WindowState` - handler: OnceCell>, - scale_policy: WindowScalePolicy, - - user32: ExtendedUser32, - - #[cfg(feature = "opengl")] - pub gl_context: OnceCell, -} - -impl WindowState { - pub fn new( - hwnd: HWND, current_size: PhySize, scale_policy: WindowScalePolicy, user32: ExtendedUser32, - ) -> Self { - Self { - hwnd, - current_dpi: Dpi::default().into(), - current_size: current_size.into(), - keyboard_state: RefCell::new(KeyboardState::new()), - mouse_button_counter: Cell::new(0), - mouse_was_outside_window: true.into(), - cursor_icon: Cell::new(MouseCursor::Default), - handler: OnceCell::new(), - scale_policy, - user32, - - #[cfg(feature = "opengl")] - gl_context: OnceCell::new(), - } - } - - pub(crate) fn handle_on_frame(&self) { - let Some(handler) = self.handler.get() else { return }; - let mut window = crate::window::Window::new(Window { state: self }); - - handler.on_frame(&mut window) - } - - pub(crate) fn handle_event(&self, event: Event) -> EventStatus { - let Some(handler) = self.handler.get() else { - return EventStatus::Ignored; - }; - - let mut window = crate::window::Window::new(Window { state: self }); - handler.on_event(&mut window, event) - } - - pub(crate) fn window_info(&self) -> WindowInfo { - WindowInfo::from_physical_size(self.current_size.get(), self.current_scale_factor()) - } - - fn current_scale_factor(&self) -> f64 { - match self.scale_policy { - WindowScalePolicy::ScaleFactor(scale) => scale, - WindowScalePolicy::SystemScaleFactor => self.current_dpi.get().scale_factor(), - } - } - - pub(crate) fn keyboard_state(&self) -> Ref<'_, KeyboardState> { - self.keyboard_state.borrow() - } - - fn send_resized(&self) { - self.handle_event(Event::Window(WindowEvent::Resized(self.window_info()))); - } -} - -pub struct Window<'a> { - state: &'a WindowState, -} +pub struct Window; -impl Window<'_> { - pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle - where - P: HasRawWindowHandle, - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { - let parent = match parent.raw_window_handle() { +impl Window { + pub fn open_parented( + parent: &impl HasWindowHandle, options: WindowOpenOptions, + build: impl for<'a> FnOnce(crate::WindowContext) -> H + Send + 'static, + ) -> WindowHandle { + let parent = match parent.window_handle().unwrap().as_raw() { RawWindowHandle::Win32(h) => h.hwnd, h => panic!("unsupported parent handle {:?}", h), }; - Self::open(true, parent, options, build) + Self::open(true, parent.get() as *mut _, options, build) } - pub fn open_blocking(options: WindowOpenOptions, build: B) - where - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { + pub fn open_blocking( + options: WindowOpenOptions, + build: impl for<'a> FnOnce(crate::WindowContext) -> H + Send + 'static, + ) { let window_handle = Self::open(false, null_mut(), options, build); run_thread_message_loop_until(|| !window_handle.is_open()).unwrap(); } - fn open( - parented: bool, parent: HWND, options: WindowOpenOptions, build: B, - ) -> WindowHandle - where - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { + fn open( + parented: bool, parent: HWND, options: WindowOpenOptions, + build: impl for<'a> FnOnce(crate::WindowContext) -> H + Send + 'static, + ) -> WindowHandle { let extended_user_32 = ExtendedUser32::load().unwrap(); let title = HSTRING::from(options.title); @@ -615,62 +503,6 @@ impl Window<'_> { WindowHandle { hwnd: Some(hwnd).into(), is_open: Rc::clone(&is_open) } } - - pub fn close(&self) { - unsafe { - PostMessageW(self.state.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0); - } - } - - pub fn has_focus(&self) -> bool { - HWnd::get_focused_window() == self.state.hwnd - } - - pub fn focus(&self) { - self.hwnd().set_focus().unwrap() - } - - fn hwnd(&self) -> HWnd<'_> { - // SAFETY: this handle should be safe to use - unsafe { HWnd::from_raw(self.state.hwnd) } - } - - pub fn resize(&self, size: Size) { - // `self.window_info` will be modified in response to the `WM_SIZE` event that - // follows the `SetWindowPos()` call - let dpi = self.state.current_dpi.get(); - let window_info = WindowInfo::from_logical_size(size, dpi.scale_factor()); - let new_size = window_info.physical_size(); - - self.hwnd().resize_and_activate(new_size, dpi, &self.state.user32).unwrap(); - } - - pub fn set_mouse_cursor(&self, mouse_cursor: MouseCursor) { - self.state.cursor_icon.set(mouse_cursor); - if let Ok(cursor) = SystemCursor::load(mouse_cursor) { - cursor.set() - } - } - - #[cfg(feature = "opengl")] - pub fn gl_context(&self) -> Option<&crate::gl::GlContext> { - self.state.gl_context.get() - } -} - -unsafe impl HasRawWindowHandle for Window<'_> { - fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = Win32WindowHandle::empty(); - handle.hwnd = self.state.hwnd; - - RawWindowHandle::Win32(handle) - } -} - -unsafe impl HasRawDisplayHandle for Window<'_> { - fn raw_display_handle(&self) -> RawDisplayHandle { - RawDisplayHandle::Windows(WindowsDisplayHandle::empty()) - } } pub fn copy_to_clipboard(_data: &str) { diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs new file mode 100644 index 00000000..f6afd46d --- /dev/null +++ b/src/platform/win/window_state.rs @@ -0,0 +1,141 @@ +use crate::platform::win::keyboard::KeyboardState; +use crate::wrappers::win32::cursor::SystemCursor; +use crate::wrappers::win32::window::HWnd; +use crate::wrappers::win32::{Dpi, ExtendedUser32}; +use crate::{ + Event, EventStatus, MouseCursor, PhySize, Size, WindowEvent, WindowHandler, WindowInfo, + WindowScalePolicy, +}; +use raw_window_handle::{DisplayHandle, Win32WindowHandle}; +use std::cell::{Cell, OnceCell, Ref, RefCell}; +use std::num::NonZeroIsize; +use windows_sys::Win32::Foundation::HWND; +use windows_sys::Win32::UI::WindowsAndMessaging::PostMessageW; + +/// All data associated with the window. +pub(crate) struct WindowState { + /// The HWND belonging to this window. + pub hwnd: HWND, + pub current_size: Cell, + pub current_dpi: Cell, // None if in non-system scale policy + pub keyboard_state: RefCell, + pub mouse_button_counter: Cell, + pub mouse_was_outside_window: Cell, + pub cursor_icon: Cell, + // Initialized late so the `Window` can hold a reference to this `WindowState` + pub handler: OnceCell>, + pub scale_policy: WindowScalePolicy, + + pub user32: ExtendedUser32, + + #[cfg(feature = "opengl")] + pub gl_context: OnceCell, +} + +impl WindowState { + pub fn new( + hwnd: HWND, current_size: PhySize, scale_policy: WindowScalePolicy, user32: ExtendedUser32, + ) -> Self { + Self { + hwnd, + current_dpi: Dpi::default().into(), + current_size: current_size.into(), + keyboard_state: RefCell::new(KeyboardState::new()), + mouse_button_counter: Cell::new(0), + mouse_was_outside_window: true.into(), + cursor_icon: Cell::new(MouseCursor::Default), + handler: OnceCell::new(), + scale_policy, + user32, + + #[cfg(feature = "opengl")] + gl_context: OnceCell::new(), + } + } + + pub(crate) fn handle_on_frame(&self) { + let Some(handler) = self.handler.get() else { return }; + + handler.on_frame() + } + + pub(crate) fn handle_event(&self, event: Event) -> EventStatus { + let Some(handler) = self.handler.get() else { + return EventStatus::Ignored; + }; + + handler.on_event(event) + } + + pub(crate) fn window_info(&self) -> WindowInfo { + WindowInfo::from_physical_size(self.current_size.get(), self.current_scale_factor()) + } + + pub fn current_scale_factor(&self) -> f64 { + match self.scale_policy { + WindowScalePolicy::ScaleFactor(scale) => scale, + WindowScalePolicy::SystemScaleFactor => self.current_dpi.get().scale_factor(), + } + } + + pub(crate) fn keyboard_state(&self) -> Ref<'_, KeyboardState> { + self.keyboard_state.borrow() + } + + pub fn send_resized(&self) { + self.handle_event(Event::Window(WindowEvent::Resized(self.window_info()))); + } + + pub fn close(&self) { + unsafe { + PostMessageW(self.hwnd, crate::platform::win::window::BV_WINDOW_MUST_CLOSE, 0, 0); + } + } + + pub fn has_focus(&self) -> bool { + HWnd::get_focused_window() == self.hwnd + } + + pub fn focus(&self) { + self.hwnd().set_focus().unwrap() + } + + fn hwnd(&self) -> HWnd { + // SAFETY: this handle should be safe to use + unsafe { HWnd::from_raw(self.hwnd) } + } + + pub fn resize(&self, size: Size) { + // `self.window_info` will be modified in response to the `WM_SIZE` event that + // follows the `SetWindowPos()` call + let dpi = self.current_dpi.get(); + let window_info = WindowInfo::from_logical_size(size, dpi.scale_factor()); + let new_size = window_info.physical_size(); + + self.hwnd().resize_and_activate(new_size, dpi, &self.user32).unwrap(); + } + + pub fn set_mouse_cursor(&self, mouse_cursor: MouseCursor) { + self.cursor_icon.set(mouse_cursor); + if let Ok(cursor) = SystemCursor::load(mouse_cursor) { + cursor.set() + } + } + + #[cfg(feature = "opengl")] + pub fn gl_context(&self) -> Option { + use std::rc::Rc; + Some(crate::gl::GlContext::new(Rc::clone(self.gl_context.get()?))) + } + + pub fn window_handle(&self) -> Option> { + let Some(hwnd) = NonZeroIsize::new(self.hwnd as _) else { unreachable!() }; + let handle = Win32WindowHandle::new(hwnd); + // TODO: add HINSTANCE + Some(unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) }) + } + + pub fn display_handle(&self) -> DisplayHandle<'_> { + DisplayHandle::windows() + } +} diff --git a/src/platform/x11/drag_n_drop.rs b/src/platform/x11/drag_n_drop.rs index 64b6880e..6cf2b950 100644 --- a/src/platform/x11/drag_n_drop.rs +++ b/src/platform/x11/drag_n_drop.rs @@ -19,7 +19,8 @@ use x11rb::{ use super::xcb_connection::{Atoms, GetPropertyError}; use super::*; -use crate::{DropData, Event, MouseEvent, PhyPoint, WindowHandler}; +use crate::handler::WindowHandler; +use crate::{DropData, Event, MouseEvent, PhyPoint}; use DragNDropState::*; /// The Drag-N-Drop session state of a `baseview` X11 window, for which it is the target. @@ -106,9 +107,9 @@ impl DragNDropState { let supported_types = if !has_more_types { &data[2..5] } else { - extra_types = window.xcb_connection.get_property( + extra_types = window.connection.get_property( source_window, - window.xcb_connection.atoms.XdndTypeList, + window.connection.atoms.XdndTypeList, xproto::Atom::from(xproto::AtomEnum::ATOM), )?; @@ -116,8 +117,7 @@ impl DragNDropState { }; // We only support the TextUriList type - let data_type_supported = - supported_types.contains(&window.xcb_connection.atoms.TextUriList); + let data_type_supported = supported_types.contains(&window.connection.atoms.TextUriList); // If there was an active drag session that we informed the handler about, we need to // generate the matching DragLeft before cancelling the previous session. @@ -133,10 +133,7 @@ impl DragNDropState { // Do this at the end, in case the handler panics, so that it doesn't poison our internal state. if interrupted_active_drag { - handler.on_event( - &mut crate::Window::new(Window { inner: window }), - Event::Mouse(MouseEvent::DragLeft), - ); + handler.on_event(Event::Mouse(MouseEvent::DragLeft)); } Ok(()) @@ -180,8 +177,7 @@ impl DragNDropState { // In version <2, action isn't specified let requested_action = (*protocol_version >= 2).then_some(event_data[4] as Atom); let requested_action = requested_action.map(|a| { - DndAction::from_atom(a, &window.xcb_connection.atoms) - .unwrap_or(DndAction::Private) + DndAction::from_atom(a, &window.connection.atoms).unwrap_or(DndAction::Private) }); request_convert_selection(window, timestamp)?; @@ -224,18 +220,15 @@ impl DragNDropState { *requested_action = if *protocol_version < 2 { None } else { - DndAction::from_atom(event_data[4] as Atom, &window.xcb_connection.atoms) + DndAction::from_atom(event_data[4] as Atom, &window.connection.atoms) }; - handler.on_event( - &mut crate::Window::new(Window { inner: window }), - Event::Mouse(MouseEvent::DragMoved { - position: position.to_logical(&window.window_info), - data: data.clone(), - // We don't get modifiers for drag n drop events. - modifiers: Modifiers::empty(), - }), - ); + handler.on_event(Event::Mouse(MouseEvent::DragMoved { + position: position.to_logical(&window.window_info.get()), + data: data.clone(), + // We don't get modifiers for drag n drop events. + modifiers: Modifiers::empty(), + })); status_result?; Ok(()) @@ -244,8 +237,7 @@ impl DragNDropState { } pub fn handle_leave_event( - &mut self, window: &WindowInner, handler: &mut dyn WindowHandler, - event: &ClientMessageEvent, + &mut self, handler: &mut dyn WindowHandler, event: &ClientMessageEvent, ) { let data = event.data.as_data32(); let event_source_window = data[0] as xproto::Window; @@ -272,10 +264,7 @@ impl DragNDropState { // Do this at the end, in case the handler panics, so that it doesn't poison our internal state. if left_active_drag { - handler.on_event( - &mut crate::Window::new(Window { inner: window }), - Event::Mouse(MouseEvent::DragLeft), - ); + handler.on_event(Event::Mouse(MouseEvent::DragLeft)); } } @@ -382,15 +371,12 @@ impl DragNDropState { let reply_result = send_finished_event(event_source_window, window, requested_action); - handler.on_event( - &mut crate::Window::new(Window { inner: window }), - Event::Mouse(MouseEvent::DragDropped { - position: position.to_logical(&window.window_info), - data, - // We don't get modifiers for drag n drop events. - modifiers: Modifiers::empty(), - }), - ); + handler.on_event(Event::Mouse(MouseEvent::DragDropped { + position: position.to_logical(&window.window_info.get()), + data, + // We don't get modifiers for drag n drop events. + modifiers: Modifiers::empty(), + })); reply_result } @@ -415,7 +401,7 @@ impl DragNDropState { }; // Ignore if this was meant for another window (?) - if event.requestor != window.window_id { + if event.requestor != window.window_id.get() { return Ok(()); } @@ -440,7 +426,7 @@ impl DragNDropState { // TODO: Log warning } Ok(data) => { - let logical_position = position.to_logical(&window.window_info); + let logical_position = position.to_logical(&window.window_info.get()); // Inform the source that we are (still) accepting the drop. @@ -451,25 +437,19 @@ impl DragNDropState { let reply_result = send_finished_event(source_window, window, requested_action); // Now that we have actual drop data, we can inform the handler about the drag AND drop events. - handler.on_event( - &mut crate::Window::new(Window { inner: window }), - Event::Mouse(MouseEvent::DragEntered { - position: logical_position, - data: data.clone(), - // We don't get modifiers for drag n drop events. - modifiers: Modifiers::empty(), - }), - ); - - handler.on_event( - &mut crate::Window::new(Window { inner: window }), - Event::Mouse(MouseEvent::DragDropped { - position: logical_position, - data: data.clone(), - // We don't get modifiers for drag n drop events. - modifiers: Modifiers::empty(), - }), - ); + handler.on_event(Event::Mouse(MouseEvent::DragEntered { + position: logical_position, + data: data.clone(), + // We don't get modifiers for drag n drop events. + modifiers: Modifiers::empty(), + })); + + handler.on_event(Event::Mouse(MouseEvent::DragDropped { + position: logical_position, + data: data.clone(), + // We don't get modifiers for drag n drop events. + modifiers: Modifiers::empty(), + })); reply_result } else { @@ -485,15 +465,12 @@ impl DragNDropState { let reply_result = send_status_event(source_window, window, requested_action); // Now that we have actual drop data, we can inform the handler about the drag event. - handler.on_event( - &mut crate::Window::new(Window { inner: window }), - Event::Mouse(MouseEvent::DragEntered { - position: logical_position, - data, - // We don't get modifiers for drag n drop events. - modifiers: Modifiers::empty(), - }), - ); + handler.on_event(Event::Mouse(MouseEvent::DragEntered { + position: logical_position, + data, + // We don't get modifiers for drag n drop events. + modifiers: Modifiers::empty(), + })); reply_result } @@ -505,13 +482,13 @@ impl DragNDropState { fn send_status_rejected( source_window: xproto::Window, window: &WindowInner, ) -> Result<(), ConnectionError> { - let conn = &window.xcb_connection; + let conn = &window.connection; let event = ClientMessageEvent { response_type: xproto::CLIENT_MESSAGE_EVENT, window: source_window, format: 32, - data: [window.window_id, 0, 0, 0, conn.atoms.None as _].into(), + data: [window.window_id.get(), 0, 0, 0, conn.atoms.None as _].into(), sequence: 0, type_: conn.atoms.XdndStatus, }; @@ -524,17 +501,16 @@ fn send_status_rejected( fn send_status_event( source_window: xproto::Window, window: &WindowInner, action: Option, ) -> Result<(), ConnectionError> { - let conn = &window.xcb_connection; + let conn = &window.connection; - let action = action - .map(|a| a.to_atom(&window.xcb_connection.atoms)) - .unwrap_or(window.xcb_connection.atoms.None); + let action = + action.map(|a| a.to_atom(&window.connection.atoms)).unwrap_or(window.connection.atoms.None); let event = ClientMessageEvent { response_type: xproto::CLIENT_MESSAGE_EVENT, window: source_window, format: 32, - data: [window.window_id, 1, 0, 0, action as _].into(), + data: [window.window_id.get(), 1, 0, 0, action as _].into(), sequence: 0, type_: conn.atoms.XdndStatus, }; @@ -547,13 +523,13 @@ fn send_status_event( pub fn send_finished_rejected( source_window: xproto::Window, window: &WindowInner, ) -> Result<(), ConnectionError> { - let conn = &window.xcb_connection; + let conn = &window.connection; let event = ClientMessageEvent { response_type: xproto::CLIENT_MESSAGE_EVENT, window: source_window, format: 32, - data: [window.window_id, 1, window.xcb_connection.atoms.None as _, 0, 0].into(), + data: [window.window_id.get(), 1, window.connection.atoms.None as _, 0, 0].into(), sequence: 0, type_: conn.atoms.XdndFinished as _, }; @@ -566,16 +542,15 @@ pub fn send_finished_rejected( fn send_finished_event( source_window: xproto::Window, window: &WindowInner, action: Option, ) -> Result<(), ConnectionError> { - let conn = &window.xcb_connection; - let action = action - .map(|a| a.to_atom(&window.xcb_connection.atoms)) - .unwrap_or(window.xcb_connection.atoms.None); + let conn = &window.connection; + let action = + action.map(|a| a.to_atom(&window.connection.atoms)).unwrap_or(window.connection.atoms.None); let event = ClientMessageEvent { response_type: xproto::CLIENT_MESSAGE_EVENT, window: source_window, format: 32, - data: [window.window_id, 1, action as _, 0, 0].into(), + data: [window.window_id.get(), 1, action as _, 0, 0].into(), sequence: 0, type_: conn.atoms.XdndFinished as _, }; @@ -588,10 +563,10 @@ fn send_finished_event( fn request_convert_selection( window: &WindowInner, timestamp: Option, ) -> Result<(), ConnectionError> { - let conn = &window.xcb_connection; + let conn = &window.connection; conn.conn.convert_selection( - window.window_id, + window.window_id.get(), conn.atoms.XdndSelection, conn.atoms.TextUriList, conn.atoms.XdndSelection, @@ -608,25 +583,28 @@ fn decode_xy(data: u32) -> (u16, u16) { fn translate_root_coordinates( window: &WindowInner, x: u16, y: u16, ) -> Result { - let root_id = window.xcb_connection.screen().root; - if root_id == window.window_id { + let root_id = window.connection.screen().root; + if root_id == window.window_id.get() { return Ok(PhyPoint::new(x as i32, y as i32)); } let reply = window - .xcb_connection + .connection .conn - .translate_coordinates(root_id, window.window_id, x as i16, y as i16)? + .translate_coordinates(root_id, window.window_id.get(), x as i16, y as i16)? .reply()?; Ok(PhyPoint::new(reply.dst_x as i32, reply.dst_y as i32)) } fn fetch_dnd_data(window: &WindowInner) -> Result> { - let conn = &window.xcb_connection; + let conn = &window.connection; - let data: Vec = - conn.get_property(window.window_id, conn.atoms.XdndSelection, conn.atoms.TextUriList)?; + let data: Vec = conn.get_property( + window.window_id.get(), + conn.atoms.XdndSelection, + conn.atoms.TextUriList, + )?; let path_list = parse_data(&data)?; diff --git a/src/platform/x11/event_loop.rs b/src/platform/x11/event_loop.rs index 5a012867..4d14d7fd 100644 --- a/src/platform/x11/event_loop.rs +++ b/src/platform/x11/event_loop.rs @@ -16,7 +16,7 @@ use x11rb::protocol::Event as XEvent; pub(crate) struct EventLoop { handler: Box, - window: WindowInner, + window: Rc, parent_handle: Option, new_physical_size: Option, @@ -30,8 +30,8 @@ pub(crate) struct EventLoop { impl EventLoop { pub fn new( - window: WindowInner, handler: impl WindowHandler + 'static, - parent_handle: Option, xkb_state: Option, + window: Rc, handler: impl WindowHandler, parent_handle: Option, + xkb_state: Option, ) -> Self { Self { window, @@ -52,15 +52,16 @@ impl EventLoop { // when they've all been coalesced. self.new_physical_size = None; - while let Some(event) = self.window.xcb_connection.conn.poll_for_event()? { + while let Some(event) = self.window.connection.conn.poll_for_event()? { self.handle_xcb_event(event); } if let Some(size) = self.new_physical_size.take() { - self.window.window_info = - WindowInfo::from_physical_size(size, self.window.window_info.scale()); + self.window + .window_info + .set(WindowInfo::from_physical_size(size, self.window.window_info.get().scale())); - let window_info = self.window.window_info; + let window_info = self.window.window_info.get(); self.handle_event(Event::Window(WindowEvent::Resized(window_info))); } @@ -70,7 +71,7 @@ impl EventLoop { // Event loop pub fn run(&mut self) -> Result<(), Box> { - let connection = Rc::clone(&self.window.xcb_connection); + let connection = Rc::clone(&self.window.connection); let mut poller = ConnectionPoller::new(&connection.conn)?; let mut last_frame = Instant::now(); @@ -85,7 +86,7 @@ impl EventLoop { // if it's already time to draw a new frame. let next_frame = last_frame + self.frame_interval; if Instant::now() >= next_frame { - self.handler.on_frame(&mut crate::Window::new(Window { inner: &self.window })); + self.handler.on_frame(); last_frame = Instant::max(next_frame, Instant::now() - self.frame_interval); } @@ -149,7 +150,7 @@ impl EventLoop { return; } - if event.data.as_data32()[0] == self.window.xcb_connection.atoms.WM_DELETE_WINDOW { + if event.data.as_data32()[0] == self.window.connection.atoms.WM_DELETE_WINDOW { self.handle_close_requested(); return; } @@ -157,7 +158,7 @@ impl EventLoop { //// // drag n drop //// - if event.type_ == self.window.xcb_connection.atoms.XdndEnter { + if event.type_ == self.window.connection.atoms.XdndEnter { if let Err(_e) = self.drag_n_drop.handle_enter_event( &self.window, &mut *self.handler, @@ -165,7 +166,7 @@ impl EventLoop { ) { // TODO: log warning } - } else if event.type_ == self.window.xcb_connection.atoms.XdndPosition { + } else if event.type_ == self.window.connection.atoms.XdndPosition { if let Err(_e) = self.drag_n_drop.handle_position_event( &self.window, &mut *self.handler, @@ -173,19 +174,19 @@ impl EventLoop { ) { // TODO: log warning } - } else if event.type_ == self.window.xcb_connection.atoms.XdndDrop { + } else if event.type_ == self.window.connection.atoms.XdndDrop { if let Err(_e) = self.drag_n_drop.handle_drop_event(&self.window, &mut *self.handler, &event) { // TODO: log warning } - } else if event.type_ == self.window.xcb_connection.atoms.XdndLeave { - self.drag_n_drop.handle_leave_event(&self.window, &mut *self.handler, &event); + } else if event.type_ == self.window.connection.atoms.XdndLeave { + self.drag_n_drop.handle_leave_event(&mut *self.handler, &event); } } XEvent::SelectionNotify(event) => { - if event.property == self.window.xcb_connection.atoms.XdndSelection { + if event.property == self.window.connection.atoms.XdndSelection { if let Err(_e) = self.drag_n_drop.handle_selection_notify_event( &self.window, &mut *self.handler, @@ -200,7 +201,7 @@ impl EventLoop { let new_physical_size = PhySize::new(event.width as u32, event.height as u32); if self.new_physical_size.is_some() - || new_physical_size != self.window.window_info.physical_size() + || new_physical_size != self.window.window_info.get().physical_size() { self.new_physical_size = Some(new_physical_size); } @@ -211,7 +212,7 @@ impl EventLoop { //// XEvent::MotionNotify(event) => { let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32); - let logical_pos = physical_pos.to_logical(&self.window.window_info); + let logical_pos = physical_pos.to_logical(&self.window.window_info.get()); self.handle_event(Event::Mouse(MouseEvent::CursorMoved { position: logical_pos, @@ -224,7 +225,7 @@ impl EventLoop { // since no `MOTION_NOTIFY` event is generated when `ENTER_NOTIFY` is generated, // we generate a CursorMoved as well, so the mouse position from here isn't lost let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32); - let logical_pos = physical_pos.to_logical(&self.window.window_info); + let logical_pos = physical_pos.to_logical(&self.window.window_info.get()); self.handle_event(Event::Mouse(MouseEvent::CursorMoved { position: logical_pos, modifiers: key_mods(event.state), @@ -293,7 +294,7 @@ impl EventLoop { } fn handle_event(&mut self, event: Event) { - self.handler.on_event(&mut crate::Window::new(Window { inner: &self.window }), event); + self.handler.on_event(event); } fn handle_close_requested(&mut self) { diff --git a/src/platform/x11/gl.rs b/src/platform/x11/gl.rs index eb6513bb..b874b5af 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -32,10 +32,12 @@ impl From for GlError { } } -pub struct GlContext { +pub type GlContext = Rc; + +pub struct GlContextInner { glx: Glx, window: c_ulong, - connection: Rc, + connection: Rc, context: GLXContext, } @@ -47,13 +49,13 @@ pub struct FbConfig { } /// The configuration a window should be created with after calling -/// [GlContext::get_fb_config_and_visual]. +/// [GlContextInner::get_fb_config_and_visual]. pub struct WindowConfig { pub depth: u8, pub visual: u32, } -impl GlContext { +impl GlContextInner { /// Creating an OpenGL context under X11 works slightly different. Different OpenGL /// configurations require different framebuffer configurations, and to be able to use that /// context with a window the window needs to be created with a matching visual. This means that @@ -63,8 +65,8 @@ impl GlContext { /// /// Use [Self::get_fb_config_and_visual] to create both of these things. pub fn create( - window: c_ulong, connection: Rc, config: FbConfig, - ) -> Result { + window: c_ulong, connection: Rc, config: FbConfig, + ) -> Result { let glx = Glx::open()?; let xlib_connection = connection.conn.xlib_connection(); @@ -86,7 +88,8 @@ impl GlContext { )?; // Create context object here so that error or panic will properly free the context - let context = GlContext { glx, window, connection: Rc::clone(&connection), context }; + let context = + GlContextInner { glx, window, connection: Rc::clone(&connection), context }; unsafe { context.glx.with_current_context( @@ -113,7 +116,7 @@ impl GlContext { /// This needs to be passed to [Self::create] along with a handle to a window that was created /// using the visual also returned from this function. pub fn get_fb_config_and_visual( - connection: &XcbConnection, config: GlConfig, + connection: &X11Connection, config: GlConfig, ) -> Result<(FbConfig, WindowConfig), GlError> { let glx = Glx::open()?; @@ -171,7 +174,7 @@ impl GlContext { } } -impl Drop for GlContext { +impl Drop for GlContextInner { fn drop(&mut self) { unsafe { self.glx.destroy_context(self.connection.conn.xlib_connection(), self.context) } } diff --git a/src/platform/x11/mod.rs b/src/platform/x11/mod.rs index de14b33d..4f9efc95 100644 --- a/src/platform/x11/mod.rs +++ b/src/platform/x11/mod.rs @@ -1,5 +1,7 @@ mod xcb_connection; -pub(crate) use xcb_connection::XcbConnection; + +use std::rc::Rc; +pub(crate) use xcb_connection::X11Connection; mod window; pub use window::*; @@ -10,5 +12,10 @@ mod event_loop; mod keyboard; mod visual_info; +mod window_shared; + +use crate::platform::x11::window_shared::WindowInner; +pub type WindowContext = Rc; + #[cfg(feature = "opengl")] pub mod gl; diff --git a/src/platform/x11/visual_info.rs b/src/platform/x11/visual_info.rs index 943b0229..f45fb8d3 100644 --- a/src/platform/x11/visual_info.rs +++ b/src/platform/x11/visual_info.rs @@ -1,4 +1,4 @@ -use super::xcb_connection::XcbConnection; +use super::xcb_connection::X11Connection; use std::error::Error; use x11rb::connection::Connection; use x11rb::protocol::xproto::{ @@ -19,12 +19,12 @@ pub(crate) struct WindowVisualConfig { impl WindowVisualConfig { #[cfg(feature = "opengl")] pub fn find_best_visual_config_for_gl( - connection: &XcbConnection, gl_config: Option, + connection: &X11Connection, gl_config: Option, ) -> Result> { let Some(gl_config) = gl_config else { return Self::find_best_visual_config(connection) }; let (fb_config, window_config) = - super::gl::GlContext::get_fb_config_and_visual(connection, gl_config) + super::gl::GlContextInner::get_fb_config_and_visual(connection, gl_config) .expect("Could not fetch framebuffer config"); Ok(Self { @@ -35,7 +35,7 @@ impl WindowVisualConfig { }) } - pub fn find_best_visual_config(connection: &XcbConnection) -> Result> { + pub fn find_best_visual_config(connection: &X11Connection) -> Result> { match find_visual_for_depth(connection.screen(), 32) { None => Ok(Self::copy_from_parent()), Some(visual_id) => Ok(Self { @@ -62,7 +62,7 @@ impl WindowVisualConfig { // For this 32-bit depth to work, you also need to define a color map and set a border // pixel: https://cgit.freedesktop.org/xorg/xserver/tree/dix/window.c#n818 fn create_color_map( - connection: &XcbConnection, visual_id: Visualid, + connection: &X11Connection, visual_id: Visualid, ) -> Result> { let colormap = connection.conn.generate_id()?; connection.conn.create_colormap( diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index 19f65e67..832fa65d 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -1,38 +1,28 @@ use std::cell::Cell; use std::error::Error; -use std::ffi::c_void; +use std::num::NonZero; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc; use std::sync::Arc; use std::thread::{self, JoinHandle}; -use raw_window_handle::{ - HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, XlibDisplayHandle, - XlibWindowHandle, -}; +use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use x11rb::connection::Connection; use x11rb::protocol::xproto::{ - AtomEnum, ChangeWindowAttributesAux, ConfigureWindowAux, ConnectionExt, CreateGCAux, - CreateWindowAux, EventMask, InputFocus, PropMode, Visualid, Window as XWindow, WindowClass, + AtomEnum, ConnectionExt, CreateGCAux, CreateWindowAux, EventMask, PropMode, WindowClass, }; use x11rb::wrapper::ConnectionExt as _; -use x11rb::CURRENT_TIME; - -use super::XcbConnection; -use crate::{ - Event, MouseCursor, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, - WindowScalePolicy, -}; - -#[cfg(feature = "opengl")] -use crate::gl::*; +use super::X11Connection; use super::{event_loop::EventLoop, visual_info::WindowVisualConfig}; +use crate::context::WindowContext; +use crate::platform::x11::window_shared::WindowInner; +use crate::{Event, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy}; pub struct WindowHandle { - raw_window_handle: Option, + window_id: Option>, event_loop_handle: Cell>>, close_requested: Arc, is_open: Arc, @@ -51,18 +41,6 @@ impl WindowHandle { } } -unsafe impl HasRawWindowHandle for WindowHandle { - fn raw_window_handle(&self) -> RawWindowHandle { - if let Some(raw_window_handle) = self.raw_window_handle { - if self.is_open.load(Ordering::Relaxed) { - return raw_window_handle; - } - } - - RawWindowHandle::Xlib(XlibWindowHandle::empty()) - } -} - pub(crate) struct ParentHandle { close_requested: Arc, is_open: Arc, @@ -73,7 +51,7 @@ impl ParentHandle { let close_requested = Arc::new(AtomicBool::new(false)); let is_open = Arc::new(AtomicBool::new(true)); let handle = WindowHandle { - raw_window_handle: None, + window_id: None, event_loop_handle: None.into(), close_requested: Arc::clone(&close_requested), is_open: Arc::clone(&is_open), @@ -93,44 +71,19 @@ impl Drop for ParentHandle { } } -pub(crate) struct WindowInner { - // GlContext should be dropped **before** XcbConnection is dropped - #[cfg(feature = "opengl")] - gl_context: Option, - - pub(crate) xcb_connection: Rc, - pub(crate) window_id: XWindow, - pub(crate) window_info: WindowInfo, - visual_id: Visualid, - mouse_cursor: Cell, - - pub(crate) close_requested: Cell, - pub(crate) is_focused: Cell, -} - -pub struct Window<'a> { - pub(crate) inner: &'a WindowInner, -} - -// Hack to allow sending a RawWindowHandle between threads. Do not make public -struct SendableRwh(RawWindowHandle); - -unsafe impl Send for SendableRwh {} +pub struct Window; -type WindowOpenResult = Result; +type WindowOpenResult = Result, ()>; -impl<'a> Window<'a> { - pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle - where - P: HasRawWindowHandle, - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { +impl Window { + pub fn open_parented( + parent: &impl HasWindowHandle, options: WindowOpenOptions, + build: impl FnOnce(WindowContext) -> H + Send + 'static, + ) -> WindowHandle { // Convert parent into something that X understands - let parent_id = match parent.raw_window_handle() { + let parent_id = match parent.window_handle().unwrap().as_raw() { RawWindowHandle::Xlib(h) => h.window as u32, - RawWindowHandle::Xcb(h) => h.window, + RawWindowHandle::Xcb(h) => h.window.get(), h => panic!("unsupported parent handle type {:?}", h), }; @@ -142,17 +95,14 @@ impl<'a> Window<'a> { }); let raw_window_handle = rx.recv().unwrap().unwrap(); - window_handle.raw_window_handle = Some(raw_window_handle.0); + window_handle.window_id = Some(raw_window_handle); window_handle.event_loop_handle = Some(join_handle).into(); window_handle } - pub fn open_blocking(options: WindowOpenOptions, build: B) - where - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { + pub fn open_blocking( + options: WindowOpenOptions, build: impl FnOnce(WindowContext) -> H + Send + 'static, + ) { let (tx, rx) = mpsc::sync_channel::(1); let thread = thread::spawn(move || { @@ -166,18 +116,14 @@ impl<'a> Window<'a> { }); } - fn window_thread( - parent: Option, options: WindowOpenOptions, build: B, + fn window_thread( + parent: Option, options: WindowOpenOptions, + build: impl FnOnce(WindowContext) -> H + Send + 'static, tx: mpsc::SyncSender, parent_handle: Option, - ) -> Result<(), Box> - where - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { + ) -> Result<(), Box> { // Connect to the X server // FIXME: baseview error type instead of unwrap() - let xcb_connection = XcbConnection::new()?; + let xcb_connection = X11Connection::new()?; // Setup xkbcommon let xkb_state = crate::wrappers::xkbcommon::XkbcommonState::new(&xcb_connection); @@ -207,10 +153,13 @@ impl<'a> Window<'a> { #[cfg(not(feature = "opengl"))] let visual_info = WindowVisualConfig::find_best_visual_config(&xcb_connection)?; - let window_id = xcb_connection.conn.generate_id()?; + let Some(window_id) = NonZero::new(xcb_connection.conn.generate_id()?) else { + unreachable!(); + }; + xcb_connection.conn.create_window( visual_info.visual_depth, - window_id, + window_id.get(), parent_id, 0, // x coordinate of the new window 0, // y coordinate of the new window @@ -237,13 +186,13 @@ impl<'a> Window<'a> { .colormap(visual_info.color_map) .border_pixel(0), )?; - xcb_connection.conn.map_window(window_id)?; + xcb_connection.conn.map_window(window_id.get())?; // Change window title let title = options.title; xcb_connection.conn.change_property8( PropMode::REPLACE, - window_id, + window_id.get(), AtomEnum::WM_NAME, AtomEnum::STRING, title.as_bytes(), @@ -251,7 +200,7 @@ impl<'a> Window<'a> { xcb_connection.conn.change_property32( PropMode::REPLACE, - window_id, + window_id.get(), xcb_connection.atoms.WM_PROTOCOLS, AtomEnum::ATOM, &[xcb_connection.atoms.WM_DELETE_WINDOW], @@ -260,7 +209,7 @@ impl<'a> Window<'a> { // Enable drag and drop (TODO: Make this toggleable?) xcb_connection.conn.change_property32( PropMode::REPLACE, - window_id, + window_id.get(), xcb_connection.atoms.XdndAware, AtomEnum::ATOM, &[5u32], // Latest version; hasn't changed since 2002 @@ -276,123 +225,36 @@ impl<'a> Window<'a> { let gl_context = visual_info.fb_config.map(|fb_config| { use std::ffi::c_ulong; - let window = window_id as c_ulong; + let window = window_id.get() as c_ulong; // Because of the visual negotation we had to take some extra steps to create this context let context = - super::gl::GlContext::create(window, Rc::clone(&xcb_connection), fb_config) + super::gl::GlContextInner::create(window, Rc::clone(&xcb_connection), fb_config) .expect("Could not create OpenGL context"); - GlContext::new(context) + Rc::new(context) }); - let mut inner = WindowInner { + let inner = Rc::new(WindowInner::new( xcb_connection, window_id, window_info, - visual_id: visual_info.visual_id, - mouse_cursor: Cell::new(MouseCursor::default()), - - close_requested: Cell::new(false), - is_focused: false.into(), - #[cfg(feature = "opengl")] gl_context, - }; + )); - let mut window = crate::Window::new(Window { inner: &mut inner }); - - let handler = build(&mut window); + let handler = build(WindowContext::new(Rc::clone(&inner))); // Send an initial window resized event so the user is alerted of // the correct dpi scaling. - handler.on_event(&mut window, Event::Window(WindowEvent::Resized(window_info))); + handler.on_event(Event::Window(WindowEvent::Resized(window_info))); - let _ = tx.send(Ok(SendableRwh(window.raw_window_handle()))); + let _ = tx.send(Ok(window_id)); EventLoop::new(inner, handler, parent_handle, xkb_state).run()?; Ok(()) } - - pub fn set_mouse_cursor(&self, mouse_cursor: MouseCursor) { - if self.inner.mouse_cursor.get() == mouse_cursor { - return; - } - - let xid = self.inner.xcb_connection.get_cursor(mouse_cursor).unwrap(); - - if xid != 0 { - let _ = self.inner.xcb_connection.conn.change_window_attributes( - self.inner.window_id, - &ChangeWindowAttributesAux::new().cursor(xid), - ); - let _ = self.inner.xcb_connection.conn.flush(); - } - - self.inner.mouse_cursor.set(mouse_cursor); - } - - pub fn close(&self) { - self.inner.close_requested.set(true); - } - - pub fn has_focus(&self) -> bool { - self.inner.is_focused.get() - } - - pub fn focus(&self) { - let _ = self.inner.xcb_connection.conn.set_input_focus( - InputFocus::POINTER_ROOT, - self.inner.window_id, - CURRENT_TIME, - ); - let _ = self.inner.xcb_connection.conn.flush(); - } - - pub fn resize(&self, size: Size) { - let scaling = self.inner.window_info.scale(); - let new_window_info = WindowInfo::from_logical_size(size, scaling); - - let _ = self.inner.xcb_connection.conn.configure_window( - self.inner.window_id, - &ConfigureWindowAux::new() - .width(new_window_info.physical_size().width) - .height(new_window_info.physical_size().height), - ); - let _ = self.inner.xcb_connection.conn.flush(); - - // This will trigger a `ConfigureNotify` event which will in turn change `self.window_info` - // and notify the window handler about it - } - - #[cfg(feature = "opengl")] - pub fn gl_context(&self) -> Option<&crate::gl::GlContext> { - self.inner.gl_context.as_ref() - } -} - -unsafe impl<'a> HasRawWindowHandle for Window<'a> { - fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = XlibWindowHandle::empty(); - - handle.window = self.inner.window_id.into(); - handle.visual_id = self.inner.visual_id.into(); - - RawWindowHandle::Xlib(handle) - } -} - -unsafe impl<'a> HasRawDisplayHandle for Window<'a> { - fn raw_display_handle(&self) -> RawDisplayHandle { - let display = self.inner.xcb_connection.conn.xlib_display(); - let mut handle = XlibDisplayHandle::empty(); - - handle.display = display as *mut c_void; - handle.screen = self.inner.xcb_connection.conn.default_screen(); - - RawDisplayHandle::Xlib(handle) - } } pub fn copy_to_clipboard(_data: &str) { diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs new file mode 100644 index 00000000..2ba0bcb1 --- /dev/null +++ b/src/platform/x11/window_shared.rs @@ -0,0 +1,110 @@ +use crate::platform::X11Connection; +use crate::{MouseCursor, Size, WindowInfo}; +use raw_window_handle::{DisplayHandle, XcbWindowHandle}; +use std::cell::Cell; +use std::num::NonZero; +use std::rc::Rc; +use x11rb::connection::Connection; +use x11rb::protocol::xproto::{ + ChangeWindowAttributesAux, ConfigureWindowAux, ConnectionExt, InputFocus, Window as XWindow, +}; +use x11rb::CURRENT_TIME; + +pub(crate) struct WindowInner { + // GlContext should be dropped **before** XcbConnection is dropped + #[cfg(feature = "opengl")] + gl_context: Option, + + pub(crate) connection: Rc, + pub(crate) window_id: NonZero, + pub(crate) window_info: Cell, + mouse_cursor: Cell, + + pub(crate) close_requested: Cell, + pub(crate) is_focused: Cell, +} + +impl WindowInner { + pub(crate) fn new( + connection: Rc, window_id: NonZero, window_info: WindowInfo, + #[cfg(feature = "opengl")] gl_context: Option, + ) -> Self { + Self { + connection, + window_id, + window_info: window_info.into(), + mouse_cursor: MouseCursor::default().into(), + + close_requested: false.into(), + is_focused: false.into(), + + #[cfg(feature = "opengl")] + gl_context, + } + } + + pub fn set_mouse_cursor(&self, mouse_cursor: MouseCursor) { + if self.mouse_cursor.get() == mouse_cursor { + return; + } + + let xid = self.connection.get_cursor(mouse_cursor).unwrap(); + + if xid != 0 { + let _ = self.connection.conn.change_window_attributes( + self.window_id.get(), + &ChangeWindowAttributesAux::new().cursor(xid), + ); + let _ = self.connection.conn.flush(); + } + + self.mouse_cursor.set(mouse_cursor); + } + + pub fn close(&self) { + self.close_requested.set(true); + } + + pub fn has_focus(&self) -> bool { + self.is_focused.get() + } + + pub fn focus(&self) { + let _ = self.connection.conn.set_input_focus( + InputFocus::POINTER_ROOT, + self.window_id, + CURRENT_TIME, + ); + let _ = self.connection.conn.flush(); + } + + pub fn resize(&self, size: Size) { + let scaling = self.window_info.get().scale(); + let new_window_info = WindowInfo::from_logical_size(size, scaling); + + let _ = self.connection.conn.configure_window( + self.window_id.get(), + &ConfigureWindowAux::new() + .width(new_window_info.physical_size().width) + .height(new_window_info.physical_size().height), + ); + let _ = self.connection.conn.flush(); + + // This will trigger a `ConfigureNotify` event which will in turn change `self.window_info` + // and notify the window handler about it + } + + pub fn window_handle(&self) -> Option> { + let handle = XcbWindowHandle::new(self.window_id); + Some(unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) }) + } + + pub fn display_handle(&self) -> DisplayHandle<'_> { + self.connection.display_handle() + } + + #[cfg(feature = "opengl")] + pub fn gl_context(&self) -> Option { + Some(crate::gl::GlContext::new(Rc::clone(self.gl_context.as_ref()?))) + } +} diff --git a/src/platform/x11/xcb_connection.rs b/src/platform/x11/xcb_connection.rs index f57d03ec..08ebb48e 100644 --- a/src/platform/x11/xcb_connection.rs +++ b/src/platform/x11/xcb_connection.rs @@ -1,6 +1,8 @@ +use raw_window_handle::{DisplayHandle, XcbDisplayHandle}; use std::cell::RefCell; use std::collections::hash_map::{Entry, HashMap}; use std::error::Error; +use std::ptr::NonNull; use x11rb::connection::Connection; use x11rb::cursor::Handle as CursorHandle; use x11rb::protocol::xproto::{self, Cursor, Screen}; @@ -41,7 +43,7 @@ x11rb::atom_manager! { /// A very light abstraction around the XCB connection. /// /// Keeps track of the xcb connection itself and the xlib display ID that was used to connect. -pub struct XcbConnection { +pub struct X11Connection { pub(crate) conn: XlibXcbConnection, pub(crate) atoms: Atoms, pub(crate) resources: resource_manager::Database, @@ -49,7 +51,7 @@ pub struct XcbConnection { pub(crate) cursor_cache: RefCell>, } -impl XcbConnection { +impl X11Connection { pub fn new() -> Result> { let conn = XlibXcbConnection::open()?; let screen = conn.default_screen(); @@ -138,4 +140,12 @@ impl XcbConnection { ) -> Result, GetPropertyError> { self::get_property::get_property(window, property, property_type, &self.conn) } + + pub fn display_handle(&self) -> DisplayHandle<'_> { + let raw_connection = self.conn.xcb_connection().get_raw_xcb_connection(); + let Some(raw_connection) = NonNull::new(raw_connection) else { unreachable!() }; + let handle = XcbDisplayHandle::new(Some(raw_connection), self.conn.default_screen()); + + unsafe { DisplayHandle::borrow_raw(handle.into()) } + } } diff --git a/src/window.rs b/src/window.rs index 747b4537..275199ca 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,12 +1,9 @@ -use std::marker::PhantomData; - -use raw_window_handle::{ - HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, -}; - -use crate::event::{Event, EventStatus}; +use crate::context::WindowContext; +use crate::handler::WindowHandler; +use crate::platform; use crate::window_open_options::WindowOpenOptions; -use crate::{platform, MouseCursor, Size}; +use raw_window_handle::HasWindowHandle; +use std::marker::PhantomData; pub struct WindowHandle { window_handle: platform::WindowHandle, @@ -31,94 +28,22 @@ impl WindowHandle { } } -unsafe impl HasRawWindowHandle for WindowHandle { - fn raw_window_handle(&self) -> RawWindowHandle { - self.window_handle.raw_window_handle() - } -} - -pub trait WindowHandler { - fn on_frame(&self, window: &mut Window); - fn on_event(&self, window: &mut Window, event: Event) -> EventStatus; -} - -pub struct Window<'a> { - window: platform::Window<'a>, - - // so that Window is !Send on all platforms - phantom: PhantomData<*mut ()>, +pub struct Window { + _private: (), } -impl<'a> Window<'a> { - #[cfg(target_os = "windows")] - pub(crate) fn new(window: platform::Window<'a>) -> Window<'a> { - Window { window, phantom: PhantomData } - } - - #[cfg(not(target_os = "windows"))] - pub(crate) fn new(window: platform::Window) -> Window { - Window { window, phantom: PhantomData } - } - - pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle - where - P: HasRawWindowHandle, - H: WindowHandler + 'static, - B: FnOnce(&mut Window) -> H, - B: Send + 'static, - { - let window_handle = platform::Window::open_parented::(parent, options, build); +impl Window { + pub fn open_parented( + parent: &impl HasWindowHandle, options: WindowOpenOptions, + build: impl FnOnce(WindowContext) -> H + Send + 'static, + ) -> WindowHandle { + let window_handle = platform::Window::open_parented(parent, options, build); WindowHandle::new(window_handle) } - pub fn open_blocking(options: WindowOpenOptions, build: B) - where - H: WindowHandler + 'static, - B: FnOnce(&mut Window) -> H, - B: Send + 'static, - { - platform::Window::open_blocking::(options, build) - } - - /// Close the window - pub fn close(&self) { - self.window.close(); - } - - /// Resize the window to the given size. The size is always in logical pixels. DPI scaling will - /// automatically be accounted for. - pub fn resize(&self, size: Size) { - self.window.resize(size); - } - - pub fn set_mouse_cursor(&self, cursor: MouseCursor) { - self.window.set_mouse_cursor(cursor); - } - - pub fn has_focus(&self) -> bool { - self.window.has_focus() - } - - pub fn focus(&self) { - self.window.focus() - } - - /// If provided, then an OpenGL context will be created for this window. You'll be able to - /// access this context through [crate::Window::gl_context]. - #[cfg(feature = "opengl")] - pub fn gl_context(&self) -> Option<&crate::gl::GlContext> { - self.window.gl_context() - } -} - -unsafe impl<'a> HasRawWindowHandle for Window<'a> { - fn raw_window_handle(&self) -> RawWindowHandle { - self.window.raw_window_handle() - } -} - -unsafe impl<'a> HasRawDisplayHandle for Window<'a> { - fn raw_display_handle(&self) -> RawDisplayHandle { - self.window.raw_display_handle() + pub fn open_blocking( + options: WindowOpenOptions, build: impl FnOnce(WindowContext) -> H + Send + 'static, + ) { + platform::Window::open_blocking(options, build) } } diff --git a/src/window_open_options.rs b/src/window_open_options.rs index 753edb90..7c903513 100644 --- a/src/window_open_options.rs +++ b/src/window_open_options.rs @@ -28,7 +28,7 @@ pub struct WindowOpenOptions { pub scale: WindowScalePolicy, /// If provided, then an OpenGL context will be created for this window. You'll be able to - /// access this context through [crate::Window::gl_context]. + /// access this context through [crate::WindowContext::gl_context]. /// /// By default, this is set to `None`. #[cfg(feature = "opengl")] diff --git a/src/wrappers/appkit.rs b/src/wrappers/appkit.rs index 0db5d6c6..08f032e5 100644 --- a/src/wrappers/appkit.rs +++ b/src/wrappers/appkit.rs @@ -4,7 +4,7 @@ mod view; mod window; use objc2::rc::Retained; -use objc2_app_kit::{NSView, NSWindow}; +use objc2_app_kit::NSView; use objc2_core_foundation::CFUUID; use std::ffi::CString; @@ -13,7 +13,7 @@ pub use timer::TimerHandle; pub use view::*; pub use window::*; -use raw_window_handle::RawWindowHandle; +use raw_window_handle::{RawWindowHandle, WindowHandle}; fn new_class_name(prefix: &str) -> CString { // PANIC: CFUUIDCreate is not documented to return NULL. @@ -26,15 +26,10 @@ fn new_class_name(prefix: &str) -> CString { CString::new(class_name).unwrap() } -pub fn extract_raw_window_handle( - handle: RawWindowHandle, -) -> (Option>, Option>) { - let RawWindowHandle::AppKit(handle) = handle else { +pub fn extract_raw_window_handle(handle: WindowHandle) -> Option> { + let RawWindowHandle::AppKit(handle) = handle.as_raw() else { panic!("Not a macOS window"); }; - let parent_window = unsafe { Retained::retain(handle.ns_window as *mut NSWindow) }; - let parent_view = unsafe { Retained::retain(handle.ns_view as *mut NSView) }; - - (parent_window, parent_view) + unsafe { Retained::retain(handle.ns_view.as_ptr() as *mut NSView) } } diff --git a/src/wrappers/appkit/view.rs b/src/wrappers/appkit/view.rs index 3c047778..604cd3c7 100644 --- a/src/wrappers/appkit/view.rs +++ b/src/wrappers/appkit/view.rs @@ -1,13 +1,15 @@ use objc2::__framework_prelude::{Allocated, AnyClass, ProtocolObject, Retained}; +use objc2::rc::Weak; use objc2::runtime::AnyObject; use objc2::{msg_send, Encoding, Message, RefEncode}; use objc2_app_kit::{NSDragOperation, NSDraggingInfo, NSEvent, NSView, NSWindow}; use objc2_core_foundation::CGRect; use objc2_foundation::{NSNotification, NSPoint}; -use raw_window_handle::{AppKitWindowHandle, HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::{AppKitWindowHandle, WindowHandle}; use std::ffi::{c_void, CStr}; use std::marker::PhantomData; use std::ops::Deref; +use std::ptr::NonNull; mod implementation; @@ -83,6 +85,14 @@ impl View { pub fn inner_ref(&self) -> ViewRef<'_, V> { ViewRef { view: self, inner: self.inner() } } + + pub fn window_handle_from_weak(this: &Weak) -> Option> { + let view = this.load()?; + let ns_view = NonNull::from(&view.parent).cast(); + let handle = AppKitWindowHandle::new(ns_view); + + Some(unsafe { WindowHandle::borrow_raw(handle.into()) }) + } } pub struct ViewInner { @@ -94,6 +104,8 @@ pub struct ViewRef<'a, V> { pub inner: &'a V, } +impl<'a, V> ViewRef<'a, V> {} + impl<'a, V> Clone for ViewRef<'a, V> { fn clone(&self) -> Self { *self @@ -149,17 +161,3 @@ pub trait ViewImpl: Sized { fn key_up(this: ViewRef, event: &NSEvent); fn flags_changed(this: ViewRef, event: &NSEvent); } - -unsafe impl HasRawWindowHandle for View { - fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = AppKitWindowHandle::empty(); - - handle.ns_view = (&self.parent as *const NSView).cast_mut().cast(); - - if let Some(window) = self.window() { - handle.ns_window = Retained::as_ptr(&window).cast_mut().cast() - } - - handle.into() - } -} diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 45de8a65..68f2a756 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -2,7 +2,6 @@ use crate::wrappers::win32::user32::ExtendedUser32; use crate::wrappers::win32::{Dpi, DpiAwarenessContext, Rect}; use crate::PhySize; -use std::marker::PhantomData; use std::num::NonZeroUsize; use std::ptr::{null_mut, NonNull}; use windows::Win32::System::Ole::IDropTarget; @@ -25,11 +24,11 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ /// /// The role of this type is to help safely encapsulating most of the unsafe Win32 HWND APIs. #[derive(Copy, Clone)] -pub struct HWnd<'a>(HWND, PhantomData<&'a ()>); +pub struct HWnd(HWND); -impl HWnd<'_> { +impl HWnd { pub unsafe fn from_raw(hwnd: HWND) -> Self { - Self(hwnd, PhantomData) + Self(hwnd) } pub fn as_raw(&self) -> HWND { diff --git a/src/wrappers/xkbcommon.rs b/src/wrappers/xkbcommon.rs index 53db6f20..a6a9c1de 100644 --- a/src/wrappers/xkbcommon.rs +++ b/src/wrappers/xkbcommon.rs @@ -8,7 +8,7 @@ pub struct XkbcommonState { } impl XkbcommonState { - pub fn new(xcb_connection: &crate::platform::XcbConnection) -> Option { + pub fn new(xcb_connection: &crate::platform::X11Connection) -> Option { let xkb_common = xkbc::xkbcommon_option()?; let xkb_x11 = xkbc::x11::xkbcommon_x11_option()?; diff --git a/src/wrappers/xlib/xlib_xcb.rs b/src/wrappers/xlib/xlib_xcb.rs index e321b736..27d83b56 100644 --- a/src/wrappers/xlib/xlib_xcb.rs +++ b/src/wrappers/xlib/xlib_xcb.rs @@ -3,7 +3,6 @@ use std::error::Error; use std::ops::Deref; use std::os::fd::{AsFd, BorrowedFd}; use std::os::raw::c_int; -use x11_dl::xlib::Display; use x11_dl::xlib_xcb::Xlib_xcb; use x11rb::xcb_ffi::XCBConnection; @@ -51,10 +50,6 @@ impl XlibXcbConnection { self.xlib_connection.default_screen_index() } - pub fn xlib_display(&self) -> *mut Display { - self.xlib_connection.as_raw() - } - pub fn xcb_connection(&self) -> &XCBConnection { &self.xcb_connection }