From c0c7468bff32a7acf8a6d56671324a0e3df3e16a Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 16 Jun 2026 06:03:55 +0200 Subject: [PATCH 01/16] wip --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 } From dc74abd2a6ba5791c1b0e6299e20aa563256b11e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:54:10 +0200 Subject: [PATCH 02/16] wip --- examples/render_femtovg/src/main.rs | 3 +++ src/context.rs | 3 +++ src/handler.rs | 36 +++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/window.rs | 5 ---- 5 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 src/context.rs create mode 100644 src/handler.rs diff --git a/examples/render_femtovg/src/main.rs b/examples/render_femtovg/src/main.rs index 67a581ed..b7fa64bd 100644 --- a/examples/render_femtovg/src/main.rs +++ b/examples/render_femtovg/src/main.rs @@ -98,6 +98,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 && !_window.has_focus() { + _window.focus() + } self.damaged.set(true); } _ => {} diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 00000000..0cb39884 --- /dev/null +++ b/src/context.rs @@ -0,0 +1,3 @@ +pub struct WindowContext<'a> { + _marker: std::marker::PhantomData<&'a ()>, +} diff --git a/src/handler.rs b/src/handler.rs new file mode 100644 index 00000000..eb6e38f4 --- /dev/null +++ b/src/handler.rs @@ -0,0 +1,36 @@ +use crate::context::WindowContext; +use crate::{Event, EventStatus, Window}; +use std::cell::{Cell, OnceCell}; + +pub trait WindowHandler<'a> { + fn on_frame(&mut self, window: &mut Window); + fn on_event(&mut self, window: &mut Window, event: Event) -> EventStatus; +} + +pub(crate) struct HandlerContainer { + initializer: + Cell FnOnce(WindowContext<'a>) -> Box>>>>, + handler: OnceCell>>, +} + +impl HandlerContainer { + pub fn new WindowHandler<'a>>( + initializer: impl for<'a> FnOnce(WindowContext<'a>) -> H, + ) -> Self { + Self { + initializer: Cell::new(Some(Box::new(|w| Box::new(initializer(w))))), + handler: OnceCell::new(), + } + } + + pub fn initialize(&self, context: WindowContext<'static>) -> &dyn WindowHandler { + let initializer = self.initializer.take().unwrap(); + let result = initializer(context); + self.handler.set(result).unwrap(); + self.handler.get().unwrap() + } + + pub fn get(&self) -> Option<&dyn WindowHandler> { + self.handler.get() + } +} diff --git a/src/lib.rs b/src/lib.rs index 7941c52f..bcfdef4b 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; diff --git a/src/window.rs b/src/window.rs index 747b4537..4802f451 100644 --- a/src/window.rs +++ b/src/window.rs @@ -37,11 +37,6 @@ unsafe impl HasRawWindowHandle for WindowHandle { } } -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>, From a96a69b8eda31c9f497844696746e46a57203900 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 16 Jun 2026 22:29:14 +0200 Subject: [PATCH 03/16] wip --- src/handler.rs | 2 +- src/window.rs | 85 +++++++------------------------------------------- 2 files changed, 13 insertions(+), 74 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index eb6e38f4..7913ffc6 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -2,7 +2,7 @@ use crate::context::WindowContext; use crate::{Event, EventStatus, Window}; use std::cell::{Cell, OnceCell}; -pub trait WindowHandler<'a> { +pub trait WindowHandler<'a>: 'a { fn on_frame(&mut self, window: &mut Window); fn on_event(&mut self, window: &mut Window, event: Event) -> EventStatus; } diff --git a/src/window.rs b/src/window.rs index 4802f451..6f93b447 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,8 +1,6 @@ +use raw_window_handle::{HasRawWindowHandle, HasWindowHandle}; use std::marker::PhantomData; - -use raw_window_handle::{ - HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, -}; +use std::process::Output; use crate::event::{Event, EventStatus}; use crate::window_open_options::WindowOpenOptions; @@ -31,34 +29,17 @@ impl WindowHandle { } } -unsafe impl HasRawWindowHandle for WindowHandle { - fn raw_window_handle(&self) -> RawWindowHandle { - self.window_handle.raw_window_handle() - } -} - -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 +impl Window { + pub fn open_parented( + parent: &impl HasWindowHandle, options: WindowOpenOptions, + build: impl for<'a> FnOnce(WindowContext<'a>) -> H<'a>, + ) -> WindowHandle where - P: HasRawWindowHandle, - H: WindowHandler + 'static, + H: for<'a> WindowHandler<'a>, B: FnOnce(&mut Window) -> H, B: Send + 'static, { @@ -68,52 +49,10 @@ impl<'a> Window<'a> { pub fn open_blocking(options: WindowOpenOptions, build: B) where - H: WindowHandler + 'static, - B: FnOnce(&mut Window) -> H, + H: for<'a> WindowHandler<'a>, + B: FnOnce(WindowContext) -> 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() - } } From 66e61fbd4576ea7ef576035c9bcf01bc075c684a Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Wed, 17 Jun 2026 23:32:50 +0200 Subject: [PATCH 04/16] wip --- src/context.rs | 39 +++++++++- src/handler.rs | 36 +-------- src/lib.rs | 1 + src/platform/x11/drag_n_drop.rs | 97 ++++++++++--------------- src/platform/x11/event_loop.rs | 8 +- src/platform/x11/mod.rs | 7 ++ src/platform/x11/window.rs | 117 +++--------------------------- src/platform/x11/window_shared.rs | 79 ++++++++++++++++++++ src/window.rs | 13 ++-- 9 files changed, 185 insertions(+), 212 deletions(-) create mode 100644 src/platform/x11/window_shared.rs diff --git a/src/context.rs b/src/context.rs index 0cb39884..230e5428 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,3 +1,40 @@ +use crate::{platform, MouseCursor, Size}; +use raw_window_handle::{HandleError, HasWindowHandle, WindowHandle}; +use std::marker::PhantomData; + pub struct WindowContext<'a> { - _marker: std::marker::PhantomData<&'a ()>, + inner: platform::WindowContext, + _marker: PhantomData<&'a ()>, +} + +impl WindowContext<'_> { + pub(crate) fn new(inner: platform::WindowContext) -> Self { + Self { inner, _marker: PhantomData } + } + + 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(&mut self, size: Size) { + self.inner.resize(size); + } +} + +impl HasWindowHandle for WindowContext<'_> { + fn window_handle(&self) -> Result, HandleError> { + todo!() + } } diff --git a/src/handler.rs b/src/handler.rs index 7913ffc6..ae2cab12 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,36 +1,6 @@ -use crate::context::WindowContext; -use crate::{Event, EventStatus, Window}; -use std::cell::{Cell, OnceCell}; +use crate::{Event, EventStatus}; pub trait WindowHandler<'a>: 'a { - fn on_frame(&mut self, window: &mut Window); - fn on_event(&mut self, window: &mut Window, event: Event) -> EventStatus; -} - -pub(crate) struct HandlerContainer { - initializer: - Cell FnOnce(WindowContext<'a>) -> Box>>>>, - handler: OnceCell>>, -} - -impl HandlerContainer { - pub fn new WindowHandler<'a>>( - initializer: impl for<'a> FnOnce(WindowContext<'a>) -> H, - ) -> Self { - Self { - initializer: Cell::new(Some(Box::new(|w| Box::new(initializer(w))))), - handler: OnceCell::new(), - } - } - - pub fn initialize(&self, context: WindowContext<'static>) -> &dyn WindowHandler { - let initializer = self.initializer.take().unwrap(); - let result = initializer(context); - self.handler.set(result).unwrap(); - self.handler.get().unwrap() - } - - pub fn get(&self) -> Option<&dyn WindowHandler> { - self.handler.get() - } + fn on_frame(&mut self); + fn on_event(&mut self, event: Event) -> EventStatus; } diff --git a/src/lib.rs b/src/lib.rs index bcfdef4b..bd793359 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ pub mod gl; pub use clipboard::*; pub use event::*; +pub use handler::WindowHandler; pub use mouse_cursor::MouseCursor; pub use window::*; pub use window_info::*; diff --git a/src/platform/x11/drag_n_drop.rs b/src/platform/x11/drag_n_drop.rs index 64b6880e..f826b6f1 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. @@ -89,7 +90,7 @@ pub(crate) enum DragNDropState { impl DragNDropState { pub fn handle_enter_event( - &mut self, window: &WindowInner, handler: &mut dyn WindowHandler, + &mut self, window: &WindowInner, handler: &mut dyn WindowHandler<'static>, event: &ClientMessageEvent, ) -> Result<(), GetPropertyError> { let data = event.data.as_data32(); @@ -133,17 +134,14 @@ 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(()) } pub fn handle_position_event( - &mut self, window: &WindowInner, handler: &mut dyn WindowHandler, + &mut self, window: &WindowInner, handler: &mut dyn WindowHandler<'static>, event: &ClientMessageEvent, ) -> Result<(), ReplyError> { let event_data = event.data.as_data32(); @@ -227,15 +225,12 @@ impl DragNDropState { DndAction::from_atom(event_data[4] as Atom, &window.xcb_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), + data: data.clone(), + // We don't get modifiers for drag n drop events. + modifiers: Modifiers::empty(), + })); status_result?; Ok(()) @@ -244,8 +239,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 +266,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 +373,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), + data, + // We don't get modifiers for drag n drop events. + modifiers: Modifiers::empty(), + })); reply_result } @@ -451,25 +439,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 +467,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 } diff --git a/src/platform/x11/event_loop.rs b/src/platform/x11/event_loop.rs index 5a012867..6857c6fa 100644 --- a/src/platform/x11/event_loop.rs +++ b/src/platform/x11/event_loop.rs @@ -15,7 +15,7 @@ use x11rb::connection::Connection; use x11rb::protocol::Event as XEvent; pub(crate) struct EventLoop { - handler: Box, + handler: Box>, window: WindowInner, parent_handle: Option, @@ -30,7 +30,7 @@ pub(crate) struct EventLoop { impl EventLoop { pub fn new( - window: WindowInner, handler: impl WindowHandler + 'static, + window: WindowInner, handler: impl WindowHandler<'static>, parent_handle: Option, xkb_state: Option, ) -> Self { Self { @@ -85,7 +85,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); } @@ -180,7 +180,7 @@ impl EventLoop { // 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); + self.drag_n_drop.handle_leave_event(&mut *self.handler, &event); } } diff --git a/src/platform/x11/mod.rs b/src/platform/x11/mod.rs index de14b33d..2db7795f 100644 --- a/src/platform/x11/mod.rs +++ b/src/platform/x11/mod.rs @@ -1,4 +1,6 @@ mod xcb_connection; + +use std::rc::Rc; pub(crate) use xcb_connection::XcbConnection; mod 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/window.rs b/src/platform/x11/window.rs index 19f65e67..e34f1f85 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -1,28 +1,22 @@ use std::cell::Cell; use std::error::Error; -use std::ffi::c_void; 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::{HasRawWindowHandle, HasWindowHandle, RawWindowHandle, XlibWindowHandle}; 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, + Event, MouseCursor, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; @@ -93,24 +87,7 @@ 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, -} +pub struct Window; // Hack to allow sending a RawWindowHandle between threads. Do not make public struct SendableRwh(RawWindowHandle); @@ -119,11 +96,11 @@ unsafe impl Send for SendableRwh {} type WindowOpenResult = Result; -impl<'a> Window<'a> { +impl Window { pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle where - P: HasRawWindowHandle, - H: WindowHandler + 'static, + P: HasWindowHandle, + H: for<'a> WindowHandler<'a>, B: FnOnce(&mut crate::Window) -> H, B: Send + 'static, { @@ -300,13 +277,11 @@ impl<'a> Window<'a> { gl_context, }; - let mut window = crate::Window::new(Window { inner: &mut inner }); - let handler = build(&mut window); // 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()))); @@ -315,86 +290,12 @@ impl<'a> Window<'a> { 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> { + pub fn gl_context(&self) -> Option<&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) { todo!() } diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs new file mode 100644 index 00000000..43e88e5c --- /dev/null +++ b/src/platform/x11/window_shared.rs @@ -0,0 +1,79 @@ +use crate::gl::GlContext; +use crate::platform::XcbConnection; +use crate::{MouseCursor, Size, WindowInfo}; +use std::cell::Cell; +use std::rc::Rc; +use x11rb::connection::Connection; +use x11rb::protocol::xproto::{ + ChangeWindowAttributesAux, ConfigureWindowAux, ConnectionExt, InputFocus, Visualid, + 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, + + xcb_connection: Rc, + window_id: XWindow, + window_info: WindowInfo, + visual_id: Visualid, + mouse_cursor: Cell, + + close_requested: Cell, + is_focused: Cell, +} + +impl WindowInner { + pub fn set_mouse_cursor(&self, mouse_cursor: MouseCursor) { + if self.mouse_cursor.get() == mouse_cursor { + return; + } + + let xid = self.xcb_connection.get_cursor(mouse_cursor).unwrap(); + + if xid != 0 { + let _ = self.xcb_connection.conn.change_window_attributes( + self.window_id, + &ChangeWindowAttributesAux::new().cursor(xid), + ); + let _ = self.xcb_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.xcb_connection.conn.set_input_focus( + InputFocus::POINTER_ROOT, + self.window_id, + CURRENT_TIME, + ); + let _ = self.xcb_connection.conn.flush(); + } + + pub fn resize(&self, size: Size) { + let scaling = self.window_info.scale(); + let new_window_info = WindowInfo::from_logical_size(size, scaling); + + let _ = self.xcb_connection.conn.configure_window( + self.window_id, + &ConfigureWindowAux::new() + .width(new_window_info.physical_size().width) + .height(new_window_info.physical_size().height), + ); + let _ = self.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 + } +} diff --git a/src/window.rs b/src/window.rs index 6f93b447..5a4246f9 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,10 +1,9 @@ -use raw_window_handle::{HasRawWindowHandle, HasWindowHandle}; -use std::marker::PhantomData; -use std::process::Output; - -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, @@ -36,7 +35,7 @@ pub struct Window { impl Window { pub fn open_parented( parent: &impl HasWindowHandle, options: WindowOpenOptions, - build: impl for<'a> FnOnce(WindowContext<'a>) -> H<'a>, + build: impl for<'a> FnOnce(WindowContext<'a>) -> H, ) -> WindowHandle where H: for<'a> WindowHandler<'a>, From 7c80e3311516d553709e8fef8bbc191eafe7b062 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 00:14:38 +0200 Subject: [PATCH 05/16] wip --- src/platform/x11/drag_n_drop.rs | 73 ++++++++++--------- src/platform/x11/event_loop.rs | 35 ++++----- src/platform/x11/gl.rs | 6 +- src/platform/x11/mod.rs | 2 +- src/platform/x11/visual_info.rs | 8 +-- src/platform/x11/window.rs | 112 +++++++++++------------------ src/platform/x11/window_shared.rs | 66 ++++++++++++----- src/platform/x11/xcb_connection.rs | 14 +++- src/window.rs | 18 +++-- src/wrappers/xkbcommon.rs | 2 +- 10 files changed, 173 insertions(+), 163 deletions(-) diff --git a/src/platform/x11/drag_n_drop.rs b/src/platform/x11/drag_n_drop.rs index f826b6f1..ca4b21b2 100644 --- a/src/platform/x11/drag_n_drop.rs +++ b/src/platform/x11/drag_n_drop.rs @@ -107,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), )?; @@ -117,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. @@ -178,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)?; @@ -222,11 +220,11 @@ 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(Event::Mouse(MouseEvent::DragMoved { - position: position.to_logical(&window.window_info), + position: position.to_logical(&window.window_info.get()), data: data.clone(), // We don't get modifiers for drag n drop events. modifiers: Modifiers::empty(), @@ -239,7 +237,7 @@ impl DragNDropState { } pub fn handle_leave_event( - &mut self, handler: &mut dyn WindowHandler, event: &ClientMessageEvent, + &mut self, handler: &mut dyn WindowHandler<'static>, event: &ClientMessageEvent, ) { let data = event.data.as_data32(); let event_source_window = data[0] as xproto::Window; @@ -271,7 +269,7 @@ impl DragNDropState { } pub fn handle_drop_event( - &mut self, window: &WindowInner, handler: &mut dyn WindowHandler, + &mut self, window: &WindowInner, handler: &mut dyn WindowHandler<'static>, event: &ClientMessageEvent, ) -> Result<(), ConnectionError> { let data = event.data.as_data32(); @@ -374,7 +372,7 @@ impl DragNDropState { send_finished_event(event_source_window, window, requested_action); handler.on_event(Event::Mouse(MouseEvent::DragDropped { - position: position.to_logical(&window.window_info), + position: position.to_logical(&window.window_info.get()), data, // We don't get modifiers for drag n drop events. modifiers: Modifiers::empty(), @@ -386,7 +384,7 @@ impl DragNDropState { } pub fn handle_selection_notify_event( - &mut self, window: &WindowInner, handler: &mut dyn WindowHandler, + &mut self, window: &WindowInner, handler: &mut dyn WindowHandler<'static>, event: &SelectionNotifyEvent, ) -> Result<(), ConnectionError> { // Ignore the event if we weren't actually waiting for a selection notify event @@ -403,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(()); } @@ -428,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. @@ -484,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, }; @@ -503,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, }; @@ -526,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 _, }; @@ -545,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 _, }; @@ -567,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, @@ -587,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 6857c6fa..56706794 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,7 +30,7 @@ pub(crate) struct EventLoop { impl EventLoop { pub fn new( - window: WindowInner, handler: impl WindowHandler<'static>, + window: Rc, handler: impl WindowHandler<'static>, parent_handle: Option, xkb_state: Option, ) -> Self { Self { @@ -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(); @@ -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 { + } 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..cdcf2c69 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -35,7 +35,7 @@ impl From for GlError { pub struct GlContext { glx: Glx, window: c_ulong, - connection: Rc, + connection: Rc, context: GLXContext, } @@ -63,7 +63,7 @@ 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, + window: c_ulong, connection: Rc, config: FbConfig, ) -> Result { let glx = Glx::open()?; @@ -113,7 +113,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()?; diff --git a/src/platform/x11/mod.rs b/src/platform/x11/mod.rs index 2db7795f..4f9efc95 100644 --- a/src/platform/x11/mod.rs +++ b/src/platform/x11/mod.rs @@ -1,7 +1,7 @@ mod xcb_connection; use std::rc::Rc; -pub(crate) use xcb_connection::XcbConnection; +pub(crate) use xcb_connection::X11Connection; mod window; pub use window::*; diff --git a/src/platform/x11/visual_info.rs b/src/platform/x11/visual_info.rs index 943b0229..13aaa54f 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,7 +19,7 @@ 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) }; @@ -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 e34f1f85..cbfe2e27 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -1,12 +1,12 @@ -use std::cell::Cell; use std::error::Error; +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::{HasRawWindowHandle, HasWindowHandle, RawWindowHandle, XlibWindowHandle}; +use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use x11rb::connection::Connection; use x11rb::protocol::xproto::{ @@ -14,19 +14,16 @@ use x11rb::protocol::xproto::{ }; use x11rb::wrapper::ConnectionExt as _; -use super::XcbConnection; -use crate::{ - Event, MouseCursor, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, - WindowScalePolicy, -}; - +use super::X11Connection; +use super::{event_loop::EventLoop, visual_info::WindowVisualConfig}; +use crate::context::WindowContext; #[cfg(feature = "opengl")] use crate::gl::*; - -use super::{event_loop::EventLoop, visual_info::WindowVisualConfig}; +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, @@ -45,18 +42,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, @@ -67,7 +52,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), @@ -89,25 +74,20 @@ impl Drop for ParentHandle { pub struct Window; -// Hack to allow sending a RawWindowHandle between threads. Do not make public -struct SendableRwh(RawWindowHandle); - -unsafe impl Send for SendableRwh {} - -type WindowOpenResult = Result; +type WindowOpenResult = Result, ()>; impl Window { - pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle + pub fn open_parented( + parent: &impl HasWindowHandle, options: WindowOpenOptions, + build: impl for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, + ) -> WindowHandle where - P: HasWindowHandle, H: for<'a> WindowHandler<'a>, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, { // 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), }; @@ -119,16 +99,16 @@ impl Window { }); 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 for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, + ) where + H: for<'a> WindowHandler<'a>, { let (tx, rx) = mpsc::sync_channel::(1); @@ -143,18 +123,17 @@ impl Window { }); } - fn window_thread( - parent: Option, options: WindowOpenOptions, build: B, + fn window_thread( + parent: Option, options: WindowOpenOptions, + build: impl for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, tx: mpsc::SyncSender, parent_handle: Option, ) -> Result<(), Box> where - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, + H: for<'a> WindowHandler<'a>, { // 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); @@ -184,10 +163,13 @@ impl Window { #[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 @@ -214,13 +196,13 @@ impl Window { .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(), @@ -228,7 +210,7 @@ impl Window { 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], @@ -237,7 +219,7 @@ impl Window { // 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 @@ -253,7 +235,7 @@ impl Window { 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 = @@ -263,37 +245,27 @@ impl Window { GlContext::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(), - + visual_info.visual_id, #[cfg(feature = "opengl")] gl_context, - }; + )); - 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(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(()) } - - #[cfg(feature = "opengl")] - pub fn gl_context(&self) -> Option<&GlContext> { - self.inner.gl_context.as_ref() - } } pub fn copy_to_clipboard(_data: &str) { diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs index 43e88e5c..6bca9fc9 100644 --- a/src/platform/x11/window_shared.rs +++ b/src/platform/x11/window_shared.rs @@ -1,7 +1,9 @@ -use crate::gl::GlContext; -use crate::platform::XcbConnection; +use crate::platform::x11::visual_info::WindowVisualConfig; +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::{ @@ -13,32 +15,51 @@ use x11rb::CURRENT_TIME; pub(crate) struct WindowInner { // GlContext should be dropped **before** XcbConnection is dropped #[cfg(feature = "opengl")] - gl_context: Option, + gl_context: Option, - xcb_connection: Rc, - window_id: XWindow, - window_info: WindowInfo, + pub(crate) connection: Rc, + pub(crate) window_id: NonZero, + pub(crate) window_info: Cell, visual_id: Visualid, mouse_cursor: Cell, - close_requested: Cell, - is_focused: 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, + visual_id: Visualid, #[cfg(feature = "opengl")] gl_context: Option, + ) -> Self { + Self { + connection, + window_id, + window_info: window_info.into(), + visual_id, + 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.xcb_connection.get_cursor(mouse_cursor).unwrap(); + let xid = self.connection.get_cursor(mouse_cursor).unwrap(); if xid != 0 { - let _ = self.xcb_connection.conn.change_window_attributes( - self.window_id, + let _ = self.connection.conn.change_window_attributes( + self.window_id.get(), &ChangeWindowAttributesAux::new().cursor(xid), ); - let _ = self.xcb_connection.conn.flush(); + let _ = self.connection.conn.flush(); } self.mouse_cursor.set(mouse_cursor); @@ -53,27 +74,36 @@ impl WindowInner { } pub fn focus(&self) { - let _ = self.xcb_connection.conn.set_input_focus( + let _ = self.connection.conn.set_input_focus( InputFocus::POINTER_ROOT, self.window_id, CURRENT_TIME, ); - let _ = self.xcb_connection.conn.flush(); + let _ = self.connection.conn.flush(); } pub fn resize(&self, size: Size) { - let scaling = self.window_info.scale(); + let scaling = self.window_info.get().scale(); let new_window_info = WindowInfo::from_logical_size(size, scaling); - let _ = self.xcb_connection.conn.configure_window( - self.window_id, + 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.xcb_connection.conn.flush(); + 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) -> raw_window_handle::WindowHandle<'_> { + let handle = XcbWindowHandle::new(self.window_id); + unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) } + } + + pub fn display_handle(&self) -> DisplayHandle<'_> { + self.connection.display_handle() + } } 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 5a4246f9..2842154d 100644 --- a/src/window.rs +++ b/src/window.rs @@ -33,25 +33,23 @@ pub struct Window { } impl Window { - pub fn open_parented( + pub fn open_parented( parent: &impl HasWindowHandle, options: WindowOpenOptions, - build: impl for<'a> FnOnce(WindowContext<'a>) -> H, + build: impl for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, ) -> WindowHandle where H: for<'a> WindowHandler<'a>, - B: FnOnce(&mut Window) -> H, - B: Send + 'static, { - let window_handle = platform::Window::open_parented::(parent, options, build); + let window_handle = platform::Window::open_parented(parent, options, build); WindowHandle::new(window_handle) } - pub fn open_blocking(options: WindowOpenOptions, build: B) - where + pub fn open_blocking( + options: WindowOpenOptions, + build: impl for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, + ) where H: for<'a> WindowHandler<'a>, - B: FnOnce(WindowContext) -> H, - B: Send + 'static, { - platform::Window::open_blocking::(options, build) + platform::Window::open_blocking(options, build) } } 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()?; From 4d261d1b9d5b2855c233263dcd7a28d4297df6bc Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 00:36:55 +0200 Subject: [PATCH 06/16] wip --- examples/open_window/Cargo.toml | 2 +- examples/open_window/src/main.rs | 25 +++++++++++++------------ src/context.rs | 23 +++++++++++++++-------- src/handler.rs | 2 +- src/lib.rs | 1 + src/platform/x11/drag_n_drop.rs | 10 +++++----- src/platform/x11/event_loop.rs | 6 +++--- src/platform/x11/window.rs | 27 +++++++++------------------ src/window.rs | 18 ++++++------------ 9 files changed, 54 insertions(+), 60 deletions(-) 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..faa0202b 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,16 +19,16 @@ enum Message { struct OpenWindowExample { rx: RefCell>, - _ctx: softbuffer::Context, - surface: RefCell, - current_size: Cell, - mouse_pos: Cell, - is_cursor_inside: Cell, - damaged: Cell, + window_context: WindowContext, + surface: softbuffer::Surface, + current_size: WindowInfo, + mouse_pos: PhyPoint, + is_cursor_inside: bool, + damaged: bool, } impl WindowHandler for OpenWindowExample { - fn on_frame(&self, _window: &mut Window) { + fn on_frame(&self) { if !self.damaged.get() { return; } @@ -98,7 +98,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 +151,13 @@ 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 = unsafe { softbuffer::Context::new(window.clone()) }.unwrap(); + let mut surface = unsafe { softbuffer::Surface::new(&ctx, window.clone()) }.unwrap(); surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap(); OpenWindowExample { _ctx: ctx, + window_context: window, surface: surface.into(), rx: rx.into(), current_size: WindowInfo::from_physical_size(PhySize::new(512, 512), 1.0).into(), diff --git a/src/context.rs b/src/context.rs index 230e5428..573fb1bb 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,15 +1,16 @@ use crate::{platform, MouseCursor, Size}; -use raw_window_handle::{HandleError, HasWindowHandle, WindowHandle}; -use std::marker::PhantomData; +use raw_window_handle::{ + DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle, +}; -pub struct WindowContext<'a> { +#[derive(Clone)] +pub struct WindowContext { inner: platform::WindowContext, - _marker: PhantomData<&'a ()>, } -impl WindowContext<'_> { +impl WindowContext { pub(crate) fn new(inner: platform::WindowContext) -> Self { - Self { inner, _marker: PhantomData } + Self { inner } } pub fn set_mouse_cursor(&self, mouse_cursor: MouseCursor) { @@ -33,8 +34,14 @@ impl WindowContext<'_> { } } -impl HasWindowHandle for WindowContext<'_> { +impl HasWindowHandle for WindowContext { fn window_handle(&self) -> Result, HandleError> { - todo!() + Ok(self.inner.window_handle()) + } +} + +impl HasDisplayHandle for WindowContext { + fn display_handle(&self) -> Result, HandleError> { + Ok(self.inner.display_handle()) } } diff --git a/src/handler.rs b/src/handler.rs index ae2cab12..6728a09c 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,6 +1,6 @@ use crate::{Event, EventStatus}; -pub trait WindowHandler<'a>: 'a { +pub trait WindowHandler: 'static { fn on_frame(&mut self); fn on_event(&mut self, event: Event) -> EventStatus; } diff --git a/src/lib.rs b/src/lib.rs index bd793359..15190678 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ 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; diff --git a/src/platform/x11/drag_n_drop.rs b/src/platform/x11/drag_n_drop.rs index ca4b21b2..6cf2b950 100644 --- a/src/platform/x11/drag_n_drop.rs +++ b/src/platform/x11/drag_n_drop.rs @@ -90,7 +90,7 @@ pub(crate) enum DragNDropState { impl DragNDropState { pub fn handle_enter_event( - &mut self, window: &WindowInner, handler: &mut dyn WindowHandler<'static>, + &mut self, window: &WindowInner, handler: &mut dyn WindowHandler, event: &ClientMessageEvent, ) -> Result<(), GetPropertyError> { let data = event.data.as_data32(); @@ -140,7 +140,7 @@ impl DragNDropState { } pub fn handle_position_event( - &mut self, window: &WindowInner, handler: &mut dyn WindowHandler<'static>, + &mut self, window: &WindowInner, handler: &mut dyn WindowHandler, event: &ClientMessageEvent, ) -> Result<(), ReplyError> { let event_data = event.data.as_data32(); @@ -237,7 +237,7 @@ impl DragNDropState { } pub fn handle_leave_event( - &mut self, handler: &mut dyn WindowHandler<'static>, 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; @@ -269,7 +269,7 @@ impl DragNDropState { } pub fn handle_drop_event( - &mut self, window: &WindowInner, handler: &mut dyn WindowHandler<'static>, + &mut self, window: &WindowInner, handler: &mut dyn WindowHandler, event: &ClientMessageEvent, ) -> Result<(), ConnectionError> { let data = event.data.as_data32(); @@ -384,7 +384,7 @@ impl DragNDropState { } pub fn handle_selection_notify_event( - &mut self, window: &WindowInner, handler: &mut dyn WindowHandler<'static>, + &mut self, window: &WindowInner, handler: &mut dyn WindowHandler, event: &SelectionNotifyEvent, ) -> Result<(), ConnectionError> { // Ignore the event if we weren't actually waiting for a selection notify event diff --git a/src/platform/x11/event_loop.rs b/src/platform/x11/event_loop.rs index 56706794..4d14d7fd 100644 --- a/src/platform/x11/event_loop.rs +++ b/src/platform/x11/event_loop.rs @@ -15,7 +15,7 @@ use x11rb::connection::Connection; use x11rb::protocol::Event as XEvent; pub(crate) struct EventLoop { - handler: Box>, + handler: Box, window: Rc, parent_handle: Option, @@ -30,8 +30,8 @@ pub(crate) struct EventLoop { impl EventLoop { pub fn new( - window: Rc, handler: impl WindowHandler<'static>, - parent_handle: Option, xkb_state: Option, + window: Rc, handler: impl WindowHandler, parent_handle: Option, + xkb_state: Option, ) -> Self { Self { window, diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index cbfe2e27..975209a8 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -77,13 +77,10 @@ pub struct Window; type WindowOpenResult = Result, ()>; impl Window { - pub fn open_parented( + pub fn open_parented( parent: &impl HasWindowHandle, options: WindowOpenOptions, - build: impl for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, - ) -> WindowHandle - where - H: for<'a> WindowHandler<'a>, - { + build: impl FnOnce(WindowContext) -> H + Send + 'static, + ) -> WindowHandle { // Convert parent into something that X understands let parent_id = match parent.window_handle().unwrap().as_raw() { RawWindowHandle::Xlib(h) => h.window as u32, @@ -104,12 +101,9 @@ impl Window { window_handle } - pub fn open_blocking( - options: WindowOpenOptions, - build: impl for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, - ) where - H: for<'a> WindowHandler<'a>, - { + 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 || { @@ -123,14 +117,11 @@ impl Window { }); } - fn window_thread( + fn window_thread( parent: Option, options: WindowOpenOptions, - build: impl for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, + build: impl FnOnce(WindowContext) -> H + Send + 'static, tx: mpsc::SyncSender, parent_handle: Option, - ) -> Result<(), Box> - where - H: for<'a> WindowHandler<'a>, - { + ) -> Result<(), Box> { // Connect to the X server // FIXME: baseview error type instead of unwrap() let xcb_connection = X11Connection::new()?; diff --git a/src/window.rs b/src/window.rs index 2842154d..5f4a947a 100644 --- a/src/window.rs +++ b/src/window.rs @@ -33,23 +33,17 @@ pub struct Window { } impl Window { - pub fn open_parented( + pub fn open_parented( parent: &impl HasWindowHandle, options: WindowOpenOptions, - build: impl for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, - ) -> WindowHandle - where - H: for<'a> WindowHandler<'a>, - { + build: impl for<'a> 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: impl for<'a> FnOnce(WindowContext<'a>) -> H + Send + 'static, - ) where - H: for<'a> WindowHandler<'a>, - { + pub fn open_blocking( + options: WindowOpenOptions, build: impl FnOnce(WindowContext) -> H + Send + 'static, + ) { platform::Window::open_blocking(options, build) } } From 7a1be4f005f4b0217296f21469c8069b3288a5e5 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 01:32:31 +0200 Subject: [PATCH 07/16] update all examples --- examples/open_parented/Cargo.toml | 3 +-- examples/open_parented/src/main.rs | 40 +++++++++++++---------------- examples/render_femtovg/Cargo.toml | 2 +- examples/render_femtovg/src/main.rs | 30 ++++++++++++---------- src/context.rs | 5 ++++ src/gl.rs | 11 ++++---- src/platform/x11/gl.rs | 15 ++++++----- src/platform/x11/visual_info.rs | 2 +- src/platform/x11/window.rs | 6 ++--- src/platform/x11/window_shared.rs | 10 +++++--- 10 files changed, 66 insertions(+), 58 deletions(-) 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..3b734fae 100644 --- a/examples/open_parented/src/main.rs +++ b/examples/open_parented/src/main.rs @@ -1,34 +1,32 @@ 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, - current_size: Cell, - damaged: Cell, + surface: softbuffer::Surface, + current_size: PhySize, + damaged: bool, _child_window: Option, } 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, - current_size: Cell, - damaged: Cell, + surface: softbuffer::Surface, + current_size: PhySize, + damaged: bool, } 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/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 b7fa64bd..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,8 +102,8 @@ 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 && !_window.has_focus() { - _window.focus() + 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 index 573fb1bb..e7f4716c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -32,6 +32,11 @@ impl WindowContext { pub fn resize(&mut self, size: Size) { self.inner.resize(size); } + + #[cfg(feature = "opengl")] + pub fn gl_context(&self) -> Option { + self.inner.gl_context() + } } impl HasWindowHandle for WindowContext { diff --git a/src/gl.rs b/src/gl.rs index 2e2d1b45..a4ffdd00 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 { @@ -51,16 +50,16 @@ pub enum GlError { 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/platform/x11/gl.rs b/src/platform/x11/gl.rs index cdcf2c69..b874b5af 100644 --- a/src/platform/x11/gl.rs +++ b/src/platform/x11/gl.rs @@ -32,7 +32,9 @@ impl From for GlError { } } -pub struct GlContext { +pub type GlContext = Rc; + +pub struct GlContextInner { glx: Glx, window: c_ulong, connection: Rc, @@ -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 @@ -64,7 +66,7 @@ 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 { + ) -> 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( @@ -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/visual_info.rs b/src/platform/x11/visual_info.rs index 13aaa54f..f45fb8d3 100644 --- a/src/platform/x11/visual_info.rs +++ b/src/platform/x11/visual_info.rs @@ -24,7 +24,7 @@ impl WindowVisualConfig { 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 { diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index 975209a8..e859a31f 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -17,8 +17,6 @@ use x11rb::wrapper::ConnectionExt as _; use super::X11Connection; use super::{event_loop::EventLoop, visual_info::WindowVisualConfig}; use crate::context::WindowContext; -#[cfg(feature = "opengl")] -use crate::gl::*; use crate::platform::x11::window_shared::WindowInner; use crate::{Event, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy}; @@ -230,10 +228,10 @@ impl Window { // 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 inner = Rc::new(WindowInner::new( diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs index 6bca9fc9..6348a530 100644 --- a/src/platform/x11/window_shared.rs +++ b/src/platform/x11/window_shared.rs @@ -1,4 +1,3 @@ -use crate::platform::x11::visual_info::WindowVisualConfig; use crate::platform::X11Connection; use crate::{MouseCursor, Size, WindowInfo}; use raw_window_handle::{DisplayHandle, XcbWindowHandle}; @@ -15,7 +14,7 @@ use x11rb::CURRENT_TIME; pub(crate) struct WindowInner { // GlContext should be dropped **before** XcbConnection is dropped #[cfg(feature = "opengl")] - gl_context: Option, + gl_context: Option, pub(crate) connection: Rc, pub(crate) window_id: NonZero, @@ -30,7 +29,7 @@ pub(crate) struct WindowInner { impl WindowInner { pub(crate) fn new( connection: Rc, window_id: NonZero, window_info: WindowInfo, - visual_id: Visualid, #[cfg(feature = "opengl")] gl_context: Option, + visual_id: Visualid, #[cfg(feature = "opengl")] gl_context: Option, ) -> Self { Self { connection, @@ -106,4 +105,9 @@ impl WindowInner { 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()?))) + } } From 8a615e1d41184697528787e593299cbca67e867e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 05:07:42 +0200 Subject: [PATCH 08/16] post-rebase fixes --- examples/open_parented/src/main.rs | 12 ++++++------ examples/open_window/src/main.rs | 11 +++++------ src/gl.rs | 2 +- src/handler.rs | 4 ++-- src/platform/win/gl.rs | 18 +++--------------- src/platform/win/window.rs | 6 +----- src/platform/x11/window.rs | 1 + src/wrappers/win32/window/handle.rs | 6 +++--- src/wrappers/xlib/xlib_xcb.rs | 5 ----- 9 files changed, 22 insertions(+), 43 deletions(-) diff --git a/examples/open_parented/src/main.rs b/examples/open_parented/src/main.rs index 3b734fae..7f12deab 100644 --- a/examples/open_parented/src/main.rs +++ b/examples/open_parented/src/main.rs @@ -6,9 +6,9 @@ use std::cell::{Cell, RefCell}; use std::num::NonZeroU32; struct ParentWindowHandler { - surface: softbuffer::Surface, - current_size: PhySize, - damaged: bool, + surface: RefCell>, + current_size: Cell, + damaged: Cell, _child_window: Option, } @@ -70,9 +70,9 @@ impl WindowHandler for ParentWindowHandler { } struct ChildWindowHandler { - surface: softbuffer::Surface, - current_size: PhySize, - damaged: bool, + surface: RefCell>, + current_size: Cell, + damaged: Cell, } impl ChildWindowHandler { diff --git a/examples/open_window/src/main.rs b/examples/open_window/src/main.rs index faa0202b..681ec093 100644 --- a/examples/open_window/src/main.rs +++ b/examples/open_window/src/main.rs @@ -20,11 +20,11 @@ struct OpenWindowExample { rx: RefCell>, window_context: WindowContext, - surface: softbuffer::Surface, - current_size: WindowInfo, - mouse_pos: PhyPoint, - is_cursor_inside: bool, - damaged: bool, + surface: RefCell>, + current_size: Cell, + mouse_pos: Cell, + is_cursor_inside: Cell, + damaged: Cell, } impl WindowHandler for OpenWindowExample { @@ -156,7 +156,6 @@ fn main() { surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap(); OpenWindowExample { - _ctx: ctx, window_context: window, surface: surface.into(), rx: rx.into(), diff --git a/src/gl.rs b/src/gl.rs index a4ffdd00..4b020535 100644 --- a/src/gl.rs +++ b/src/gl.rs @@ -44,8 +44,8 @@ pub enum Profile { } #[derive(Debug)] +#[non_exhaustive] pub enum GlError { - InvalidWindowHandle, VersionNotSupported, CreationFailed(CreationFailedError), } diff --git a/src/handler.rs b/src/handler.rs index 6728a09c..21db66e8 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,6 +1,6 @@ use crate::{Event, EventStatus}; pub trait WindowHandler: 'static { - fn on_frame(&mut self); - fn on_event(&mut self, event: Event) -> EventStatus; + fn on_frame(&self); + fn on_event(&self, event: Event) -> EventStatus; } diff --git a/src/platform/win/gl.rs b/src/platform/win/gl.rs index a173daf9..ba280841 100644 --- a/src/platform/win/gl.rs +++ b/src/platform/win/gl.rs @@ -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; @@ -87,17 +85,7 @@ extern "C" { } 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); - } - + 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 +176,7 @@ impl GlContext { // Create actual context - let hwnd = handle.hwnd; + let hwnd = window.as_raw(); let hdc = GetDC(hwnd); diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 3209ed08..ca895296 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -154,11 +154,7 @@ 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::GlContext::create(window, gl_config) } .expect("Could not create OpenGL context"); let Ok(()) = self.window_state.gl_context.set(crate::gl::GlContext::new(gl_context)) diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index e859a31f..5da98593 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -1,3 +1,4 @@ +use std::cell::Cell; use std::error::Error; use std::num::NonZero; use std::rc::Rc; diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 45de8a65..34427ccb 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -25,11 +25,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/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 } From b9f58c2b5310332c0d0c89a0d8a46bf7cdd8dbeb Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 05:34:26 +0200 Subject: [PATCH 09/16] win32 impl --- src/platform/win/drop_target.rs | 3 +- src/platform/win/gl.rs | 14 +- src/platform/win/mod.rs | 4 + src/platform/win/window.rs | 221 ++++-------------------------- src/platform/win/window_state.rs | 141 +++++++++++++++++++ src/platform/x11/window.rs | 1 - src/platform/x11/window_shared.rs | 7 +- 7 files changed, 185 insertions(+), 206 deletions(-) create mode 100644 src/platform/win/window_state.rs 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 ba280841..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::{ @@ -73,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, @@ -84,8 +86,8 @@ extern "C" { static __ImageBase: IMAGE_DOS_HEADER; } -impl GlContext { - pub unsafe fn create(window: HWnd, config: GlConfig) -> Result { +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()); @@ -283,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) { @@ -315,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 ca895296..4caa405e 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -13,28 +13,24 @@ 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 +71,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 +81,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,19 +137,17 @@ impl WindowImpl for BaseviewWindow { #[cfg(feature = "opengl")] if let Some(gl_config) = self.gl_config.clone() { - let gl_context = unsafe { gl::GlContext::create(window, 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!() }; @@ -431,122 +412,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() - } +pub struct Window; - fn send_resized(&self) { - self.handle_event(Event::Window(WindowEvent::Resized(self.window_info()))); - } -} - -pub struct Window<'a> { - state: &'a WindowState, -} - -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); @@ -611,62 +504,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..ffaa29ee --- /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 std::rc::Rc; +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 { + Some(crate::gl::GlContext::new(Rc::clone(self.gl_context.get()?))) + } + + pub fn window_handle(&self) -> raw_window_handle::WindowHandle<'_> { + let Some(hwnd) = NonZeroIsize::new(self.hwnd as _) else { unreachable!() }; + let handle = Win32WindowHandle::new(hwnd); + // TODO: add HINSTANCE + unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) } + } + + pub fn display_handle(&self) -> DisplayHandle<'_> { + DisplayHandle::windows() + } +} diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index 5da98593..832fa65d 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -239,7 +239,6 @@ impl Window { xcb_connection, window_id, window_info, - visual_info.visual_id, #[cfg(feature = "opengl")] gl_context, )); diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs index 6348a530..d2d370ee 100644 --- a/src/platform/x11/window_shared.rs +++ b/src/platform/x11/window_shared.rs @@ -6,8 +6,7 @@ use std::num::NonZero; use std::rc::Rc; use x11rb::connection::Connection; use x11rb::protocol::xproto::{ - ChangeWindowAttributesAux, ConfigureWindowAux, ConnectionExt, InputFocus, Visualid, - Window as XWindow, + ChangeWindowAttributesAux, ConfigureWindowAux, ConnectionExt, InputFocus, Window as XWindow, }; use x11rb::CURRENT_TIME; @@ -19,7 +18,6 @@ pub(crate) struct WindowInner { pub(crate) connection: Rc, pub(crate) window_id: NonZero, pub(crate) window_info: Cell, - visual_id: Visualid, mouse_cursor: Cell, pub(crate) close_requested: Cell, @@ -29,13 +27,12 @@ pub(crate) struct WindowInner { impl WindowInner { pub(crate) fn new( connection: Rc, window_id: NonZero, window_info: WindowInfo, - visual_id: Visualid, #[cfg(feature = "opengl")] gl_context: Option, + #[cfg(feature = "opengl")] gl_context: Option, ) -> Self { Self { connection, window_id, window_info: window_info.into(), - visual_id, mouse_cursor: MouseCursor::default().into(), close_requested: false.into(), From 21c81f1c48e9d1512f8c0acba7e4d067a81e6457 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 11:25:08 +0200 Subject: [PATCH 10/16] wip --- src/platform/macos/context.rs | 70 ++++++++++++++++++++++ src/platform/macos/gl.rs | 1 + src/platform/macos/mod.rs | 2 + src/platform/macos/view.rs | 22 +++---- src/platform/macos/window.rs | 109 ++++------------------------------ src/window.rs | 2 +- src/wrappers/appkit.rs | 15 ++--- src/wrappers/appkit/view.rs | 26 ++++---- 8 files changed, 116 insertions(+), 131 deletions(-) create mode 100644 src/platform/macos/context.rs diff --git a/src/platform/macos/context.rs b/src/platform/macos/context.rs new file mode 100644 index 00000000..96108a67 --- /dev/null +++ b/src/platform/macos/context.rs @@ -0,0 +1,70 @@ +use crate::gl::GlContext; +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; + +#[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(GlContext::new(self.view.load()?.inner().gl_context.get()?.clone())) + } +} 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..e2ffb3fe 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -10,11 +10,11 @@ use objc2_app_kit::{ use objc2_foundation::NSString; use raw_window_handle::{ AppKitDisplayHandle, AppKitWindowHandle, HasRawDisplayHandle, HasRawWindowHandle, - RawDisplayHandle, RawWindowHandle, + HasWindowHandle, RawDisplayHandle, RawWindowHandle, }; use super::cursor::Cursor; -use crate::{MouseCursor, Size, WindowHandler, WindowInfo, WindowOpenOptions}; +use crate::{MouseCursor, Size, WindowContext, WindowHandler, WindowInfo, WindowOpenOptions}; #[cfg(feature = "opengl")] use crate::gl::GlContext; @@ -40,40 +40,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, -} +pub struct Window; -impl<'a> From> for crate::Window<'a> { - fn from(value: ViewRef<'a, BaseviewView>) -> Self { - crate::Window::new(Window { view: value.view, inner: value.inner }) - } -} - -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 +62,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 +92,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 +109,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/window.rs b/src/window.rs index 5f4a947a..275199ca 100644 --- a/src/window.rs +++ b/src/window.rs @@ -35,7 +35,7 @@ pub struct Window { impl Window { pub fn open_parented( parent: &impl HasWindowHandle, options: WindowOpenOptions, - build: impl for<'a> FnOnce(WindowContext) -> H + Send + 'static, + build: impl FnOnce(WindowContext) -> H + Send + 'static, ) -> WindowHandle { let window_handle = platform::Window::open_parented(parent, options, build); WindowHandle::new(window_handle) diff --git a/src/wrappers/appkit.rs b/src/wrappers/appkit.rs index 0db5d6c6..c7224b35 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,12 @@ 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) }; + let parent_view = unsafe { Retained::retain(handle.ns_view.as_ptr() as *mut NSView) }; - (parent_window, parent_view) + parent_view } diff --git a/src/wrappers/appkit/view.rs b/src/wrappers/appkit/view.rs index 3c047778..21234bce 100644 --- a/src/wrappers/appkit/view.rs +++ b/src/wrappers/appkit/view.rs @@ -4,10 +4,11 @@ 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 +84,13 @@ impl View { pub fn inner_ref(&self) -> ViewRef<'_, V> { ViewRef { view: self, inner: self.inner() } } + + pub fn window_handle(&self) -> WindowHandle { + let ns_view = NonNull::from_ref(&self.parent).cast(); + let handle = AppKitWindowHandle::new(ns_view); + + unsafe { WindowHandle::borrow_raw(handle.into()) } + } } pub struct ViewInner { @@ -94,6 +102,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 +159,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() - } -} From 7138ea7170e860561e1cf3f0dcbce6a5bec09287 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 23:31:16 +0200 Subject: [PATCH 11/16] finish macOS impl --- src/context.rs | 2 +- src/platform/macos/context.rs | 9 +++++++++ src/platform/win/window_state.rs | 4 ++-- src/platform/x11/window_shared.rs | 4 ++-- src/wrappers/appkit/view.rs | 8 +++++--- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/context.rs b/src/context.rs index e7f4716c..687f5e86 100644 --- a/src/context.rs +++ b/src/context.rs @@ -41,7 +41,7 @@ impl WindowContext { impl HasWindowHandle for WindowContext { fn window_handle(&self) -> Result, HandleError> { - Ok(self.inner.window_handle()) + self.inner.window_handle().ok_or(HandleError::Unavailable) } } diff --git a/src/platform/macos/context.rs b/src/platform/macos/context.rs index 96108a67..79ff33cb 100644 --- a/src/platform/macos/context.rs +++ b/src/platform/macos/context.rs @@ -6,6 +6,7 @@ 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 { @@ -67,4 +68,12 @@ impl WindowContext { pub fn gl_context(&self) -> Option { Some(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/win/window_state.rs b/src/platform/win/window_state.rs index ffaa29ee..337a7d6c 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -128,11 +128,11 @@ impl WindowState { Some(crate::gl::GlContext::new(Rc::clone(self.gl_context.get()?))) } - pub fn window_handle(&self) -> raw_window_handle::WindowHandle<'_> { + pub fn window_handle(&self) -> Option> { let Some(hwnd) = NonZeroIsize::new(self.hwnd as _) else { unreachable!() }; let handle = Win32WindowHandle::new(hwnd); // TODO: add HINSTANCE - unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) } + Some(unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) }) } pub fn display_handle(&self) -> DisplayHandle<'_> { diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs index d2d370ee..2ba0bcb1 100644 --- a/src/platform/x11/window_shared.rs +++ b/src/platform/x11/window_shared.rs @@ -94,9 +94,9 @@ impl WindowInner { // and notify the window handler about it } - pub fn window_handle(&self) -> raw_window_handle::WindowHandle<'_> { + pub fn window_handle(&self) -> Option> { let handle = XcbWindowHandle::new(self.window_id); - unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) } + Some(unsafe { raw_window_handle::WindowHandle::borrow_raw(handle.into()) }) } pub fn display_handle(&self) -> DisplayHandle<'_> { diff --git a/src/wrappers/appkit/view.rs b/src/wrappers/appkit/view.rs index 21234bce..b932c389 100644 --- a/src/wrappers/appkit/view.rs +++ b/src/wrappers/appkit/view.rs @@ -1,4 +1,5 @@ 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}; @@ -85,11 +86,12 @@ impl View { ViewRef { view: self, inner: self.inner() } } - pub fn window_handle(&self) -> WindowHandle { - let ns_view = NonNull::from_ref(&self.parent).cast(); + pub fn window_handle_from_weak(this: &Weak) -> Option { + let view = this.load()?; + let ns_view = NonNull::from_ref(&view.parent).cast(); let handle = AppKitWindowHandle::new(ns_view); - unsafe { WindowHandle::borrow_raw(handle.into()) } + Some(unsafe { WindowHandle::borrow_raw(handle.into()) }) } } From eb8a0ac3a1369c7c7e1c61a28f2533b50138886e Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 23:38:25 +0200 Subject: [PATCH 12/16] cleanup --- src/platform/macos/context.rs | 9 ++++----- src/platform/macos/window.rs | 14 +++----------- src/wrappers/appkit.rs | 4 +--- src/wrappers/appkit/view.rs | 4 ++-- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/platform/macos/context.rs b/src/platform/macos/context.rs index 79ff33cb..432f8c1a 100644 --- a/src/platform/macos/context.rs +++ b/src/platform/macos/context.rs @@ -1,4 +1,3 @@ -use crate::gl::GlContext; use crate::platform::macos::cursor::Cursor; use crate::platform::macos::view::BaseviewView; use crate::wrappers::appkit::{View, ViewRef}; @@ -65,15 +64,15 @@ impl WindowContext { } #[cfg(feature = "opengl")] - pub fn gl_context(&self) -> Option { - Some(GlContext::new(self.view.load()?.inner().gl_context.get()?.clone())) + 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 { + pub fn window_handle(&self) -> Option> { View::window_handle_from_weak(&self.view) } - pub fn display_handle(&self) -> DisplayHandle { + pub fn display_handle(&self) -> DisplayHandle<'_> { DisplayHandle::appkit() } } diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index e2ffb3fe..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, - HasWindowHandle, RawDisplayHandle, RawWindowHandle, -}; - -use super::cursor::Cursor; -use crate::{MouseCursor, Size, WindowContext, 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>>>, diff --git a/src/wrappers/appkit.rs b/src/wrappers/appkit.rs index c7224b35..08f032e5 100644 --- a/src/wrappers/appkit.rs +++ b/src/wrappers/appkit.rs @@ -31,7 +31,5 @@ pub fn extract_raw_window_handle(handle: WindowHandle) -> Option View { ViewRef { view: self, inner: self.inner() } } - pub fn window_handle_from_weak(this: &Weak) -> Option { + pub fn window_handle_from_weak(this: &Weak) -> Option> { let view = this.load()?; - let ns_view = NonNull::from_ref(&view.parent).cast(); + let ns_view = NonNull::from(&view.parent).cast(); let handle = AppKitWindowHandle::new(ns_view); Some(unsafe { WindowHandle::borrow_raw(handle.into()) }) From 70e331e0e13e7b7d79b1c1c91c5a5b25b1617d22 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 23:41:06 +0200 Subject: [PATCH 13/16] more fixes --- examples/open_window/src/main.rs | 6 ++---- src/window_open_options.rs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/open_window/src/main.rs b/examples/open_window/src/main.rs index 681ec093..4c97a1ab 100644 --- a/examples/open_window/src/main.rs +++ b/examples/open_window/src/main.rs @@ -19,7 +19,6 @@ enum Message { struct OpenWindowExample { rx: RefCell>, - window_context: WindowContext, surface: RefCell>, current_size: Cell, mouse_pos: Cell, @@ -151,12 +150,11 @@ fn main() { }); Window::open_blocking(window_open_options, |window| { - let ctx = unsafe { softbuffer::Context::new(window.clone()) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&ctx, window.clone()) }.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 { - window_context: window, surface: surface.into(), rx: rx.into(), current_size: WindowInfo::from_physical_size(PhySize::new(512, 512), 1.0).into(), 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")] From 66df850963d95f8e8737e58030e190f578146015 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 23:45:21 +0200 Subject: [PATCH 14/16] more fixes --- src/platform/win/window.rs | 1 - src/wrappers/win32/window/handle.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index 4caa405e..b82b7381 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -16,7 +16,6 @@ use windows_sys::Win32::{ use std::cell::Cell; use std::num::NonZeroUsize; use std::ptr::null_mut; -use std::rc::Rc; use raw_window_handle::{HasWindowHandle, RawWindowHandle}; diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 34427ccb..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; From a9d5ccf6a30ec68f44612954f013889b5c649454 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Thu, 18 Jun 2026 23:48:19 +0200 Subject: [PATCH 15/16] more fixes --- src/platform/win/window_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/win/window_state.rs b/src/platform/win/window_state.rs index 337a7d6c..f6afd46d 100644 --- a/src/platform/win/window_state.rs +++ b/src/platform/win/window_state.rs @@ -9,7 +9,6 @@ use crate::{ use raw_window_handle::{DisplayHandle, Win32WindowHandle}; use std::cell::{Cell, OnceCell, Ref, RefCell}; use std::num::NonZeroIsize; -use std::rc::Rc; use windows_sys::Win32::Foundation::HWND; use windows_sys::Win32::UI::WindowsAndMessaging::PostMessageW; @@ -125,6 +124,7 @@ impl WindowState { #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option { + use std::rc::Rc; Some(crate::gl::GlContext::new(Rc::clone(self.gl_context.get()?))) } From dff81b25386826f9ea886f2bdce15064046e7415 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 19 Jun 2026 00:46:17 +0200 Subject: [PATCH 16/16] more fixes --- src/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context.rs b/src/context.rs index 687f5e86..3cd18569 100644 --- a/src/context.rs +++ b/src/context.rs @@ -29,7 +29,7 @@ impl WindowContext { self.inner.focus(); } - pub fn resize(&mut self, size: Size) { + pub fn resize(&self, size: Size) { self.inner.resize(size); }