diff --git a/.rustfmt.toml b/.rustfmt.toml index badd76dd..e754330a 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,3 +1,3 @@ -fn_args_layout = "Compressed" +fn_params_layout = "Compressed" use_small_heuristics = "Max" use_field_init_shorthand = true diff --git a/Cargo.toml b/Cargo.toml index 162fe608..bd8d9d83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ opengl = ["uuid", "x11/glx"] [dependencies] keyboard-types = { version = "0.6.1", default-features = false } -raw-window-handle = "0.4.2" +raw-window-handle = "0.5" [target.'cfg(target_os="linux")'.dependencies] xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] } diff --git a/examples/open_window.rs b/examples/open_window.rs index 0d285ae2..1ef4320f 100644 --- a/examples/open_window.rs +++ b/examples/open_window.rs @@ -29,7 +29,7 @@ impl WindowHandler for OpenWindowExample { #[cfg(target_os = "macos")] match e { - MouseEvent::ButtonPressed { button, modifiers } => { + MouseEvent::ButtonPressed { .. } => { copy_to_clipboard(&"This is a test!") } _ => (), diff --git a/src/event.rs b/src/event.rs index 04d4908d..64431a09 100644 --- a/src/event.rs +++ b/src/event.rs @@ -157,6 +157,6 @@ pub enum EventStatus { /// plugin window is in focus. Ignored, /// We are prepared to handle the data in the drag and dropping will - /// result in [DropEffect] + /// result in [DropEffect] AcceptDrop(DropEffect), } diff --git a/src/gl/macos.rs b/src/gl/macos.rs index 215dd94c..c9d84963 100644 --- a/src/gl/macos.rs +++ b/src/gl/macos.rs @@ -1,7 +1,7 @@ use std::ffi::c_void; use std::str::FromStr; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::RawWindowHandle; use cocoa::appkit::{ NSOpenGLContext, NSOpenGLContextParameter, NSOpenGLPFAAccelerated, NSOpenGLPFAAlphaSize, @@ -28,10 +28,8 @@ pub struct GlContext { } impl GlContext { - pub unsafe fn create( - parent: &impl HasRawWindowHandle, config: GlConfig, - ) -> Result { - let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() { + pub unsafe fn create(parent: &RawWindowHandle, config: GlConfig) -> Result { + let handle = if let RawWindowHandle::AppKit(handle) = parent { handle } else { return Err(GlError::InvalidWindowHandle); diff --git a/src/gl/mod.rs b/src/gl/mod.rs index adfc12dd..488cfd7f 100644 --- a/src/gl/mod.rs +++ b/src/gl/mod.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; // On X11 creating the context is a two step process #[cfg(not(target_os = "linux"))] -use raw_window_handle::HasRawWindowHandle; +use raw_window_handle::RawWindowHandle; #[cfg(target_os = "windows")] mod win; @@ -77,7 +77,7 @@ pub struct GlContext { impl GlContext { #[cfg(not(target_os = "linux"))] pub(crate) unsafe fn create( - parent: &impl HasRawWindowHandle, config: GlConfig, + parent: &RawWindowHandle, config: GlConfig, ) -> Result { platform::GlContext::create(parent, config) .map(|context| GlContext { context, phantom: PhantomData }) diff --git a/src/gl/win.rs b/src/gl/win.rs index f98734ff..655a8071 100644 --- a/src/gl/win.rs +++ b/src/gl/win.rs @@ -1,7 +1,7 @@ use std::ffi::{c_void, CString, OsStr}; use std::os::windows::ffi::OsStrExt; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::RawWindowHandle; use winapi::shared::minwindef::{HINSTANCE, HMODULE}; use winapi::shared::ntdef::WCHAR; @@ -77,10 +77,8 @@ extern "C" { } impl GlContext { - pub unsafe fn create( - parent: &impl HasRawWindowHandle, config: GlConfig, - ) -> Result { - let handle = if let RawWindowHandle::Win32(handle) = parent.raw_window_handle() { + pub unsafe fn create(parent: &RawWindowHandle, config: GlConfig) -> Result { + let handle = if let RawWindowHandle::Win32(handle) = parent { handle } else { return Err(GlError::InvalidWindowHandle); diff --git a/src/gl/x11.rs b/src/gl/x11.rs index f62d318a..5e90f8de 100644 --- a/src/gl/x11.rs +++ b/src/gl/x11.rs @@ -1,8 +1,6 @@ use std::ffi::{c_void, CString}; use std::os::raw::{c_int, c_ulong}; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; - use x11::glx; use x11::xlib; @@ -80,20 +78,12 @@ impl GlContext { /// /// Use [Self::get_fb_config_and_visual] to create both of these things. pub unsafe fn create( - parent: &impl HasRawWindowHandle, config: FbConfig, + window: c_ulong, display: *mut xlib::_XDisplay, config: FbConfig, ) -> Result { - let handle = if let RawWindowHandle::Xlib(handle) = parent.raw_window_handle() { - handle - } else { - return Err(GlError::InvalidWindowHandle); - }; - - if handle.display.is_null() { + if display.is_null() { return Err(GlError::InvalidWindowHandle); } - let display = handle.display as *mut xlib::_XDisplay; - errors::XErrorHandler::handle(display, |error_handler| { #[allow(non_snake_case)] let glXCreateContextAttribsARB: GlXCreateContextAttribsARB = { @@ -144,13 +134,13 @@ impl GlContext { return Err(GlError::CreationFailed(CreationFailedError::ContextCreationFailed)); } - let res = glx::glXMakeCurrent(display, handle.window, context); + let res = glx::glXMakeCurrent(display, window, context); error_handler.check()?; if res == 0 { return Err(GlError::CreationFailed(CreationFailedError::MakeCurrentFailed)); } - glXSwapIntervalEXT(display, handle.window, config.gl_config.vsync as i32); + glXSwapIntervalEXT(display, window, config.gl_config.vsync as i32); error_handler.check()?; if glx::glXMakeCurrent(display, 0, std::ptr::null_mut()) == 0 { @@ -158,7 +148,7 @@ impl GlContext { return Err(GlError::CreationFailed(CreationFailedError::MakeCurrentFailed)); } - Ok(GlContext { window: handle.window, display, context }) + Ok(GlContext { window, display, context }) }) } diff --git a/src/macos/keyboard.rs b/src/macos/keyboard.rs index ba2bdc42..18f3bfe5 100644 --- a/src/macos/keyboard.rs +++ b/src/macos/keyboard.rs @@ -18,6 +18,8 @@ //! Conversion of platform keyboard event into cross-platform event. +use std::cell::Cell; + use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventType}; use cocoa::base::id; use cocoa::foundation::NSString; @@ -44,7 +46,7 @@ pub(crate) fn from_nsstring(s: id) -> String { /// Most of the logic in this module is adapted from Mozilla, and in particular /// TextInputHandler.mm. pub(crate) struct KeyboardState { - last_mods: NSEventModifierFlags, + last_mods: Cell, } /// Convert a macOS platform key code (keyCode field of NSEvent). @@ -269,11 +271,15 @@ fn is_modifier_code(code: Code) -> bool { impl KeyboardState { pub(crate) fn new() -> KeyboardState { - let last_mods = NSEventModifierFlags::empty(); + let last_mods = Cell::new(NSEventModifierFlags::empty()); KeyboardState { last_mods } } - pub(crate) fn process_native_event(&mut self, event: id) -> Option { + pub(crate) fn last_mods(&self) -> NSEventModifierFlags { + self.last_mods.get() + } + + pub(crate) fn process_native_event(&self, event: id) -> Option { unsafe { let event_type = event.eventType(); let key_code = event.keyCode(); @@ -288,8 +294,8 @@ impl KeyboardState { // We use `bits` here because we want to distinguish the // device dependent bits (when both left and right keys // may be pressed, for example). - let any_down = raw_mods.bits() & !self.last_mods.bits(); - self.last_mods = raw_mods; + let any_down = raw_mods.bits() & !self.last_mods.get().bits(); + self.last_mods.set(raw_mods); if is_modifier_code(code) { if any_down == 0 { KeyState::Up diff --git a/src/macos/mod.rs b/src/macos/mod.rs index e1fa7b87..02a0e36f 100644 --- a/src/macos/mod.rs +++ b/src/macos/mod.rs @@ -3,3 +3,15 @@ mod view; mod window; pub use window::*; + +#[allow(non_upper_case_globals)] +mod consts { + use cocoa::foundation::NSUInteger; + + pub const NSDragOperationNone: NSUInteger = 0; + pub const NSDragOperationCopy: NSUInteger = 1; + pub const NSDragOperationLink: NSUInteger = 2; + pub const NSDragOperationGeneric: NSUInteger = 4; + pub const NSDragOperationMove: NSUInteger = 16; +} +use consts::*; diff --git a/src/macos/view.rs b/src/macos/view.rs index 52c93110..66dd57ad 100644 --- a/src/macos/view.rs +++ b/src/macos/view.rs @@ -1,8 +1,8 @@ use std::ffi::c_void; -use cocoa::appkit::{NSEvent, NSView, NSWindow}; +use cocoa::appkit::{NSEvent, NSFilenamesPboardType, NSView, NSWindow}; use cocoa::base::{id, nil, BOOL, NO, YES}; -use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize}; +use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize, NSUInteger}; use objc::{ class, @@ -15,12 +15,16 @@ use uuid::Uuid; use crate::MouseEvent::{ButtonPressed, ButtonReleased}; use crate::{ - Event, EventStatus, MouseButton, MouseEvent, Point, ScrollDelta, Size, WindowEvent, WindowInfo, - WindowOpenOptions, + DropData, DropEffect, Event, EventStatus, MouseButton, MouseEvent, Point, ScrollDelta, Size, + WindowEvent, WindowInfo, WindowOpenOptions, }; -use super::keyboard::make_modifiers; +use super::keyboard::{from_nsstring, make_modifiers}; use super::window::WindowState; +use super::{ + NSDragOperationCopy, NSDragOperationGeneric, NSDragOperationLink, NSDragOperationMove, + NSDragOperationNone, +}; /// Name of the field used to store the `WindowState` pointer. pub(super) const BASEVIEW_STATE_IVAR: &str = "baseview_state"; @@ -29,9 +33,7 @@ macro_rules! add_simple_mouse_class_method { ($class:ident, $sel:ident, $event:expr) => { #[allow(non_snake_case)] extern "C" fn $sel(this: &Object, _: Sel, _: id){ - let state: &mut WindowState = unsafe { - WindowState::from_field(this) - }; + let state = unsafe { WindowState::from_view(this) }; state.trigger_event(Event::Mouse($event)); } @@ -49,9 +51,7 @@ macro_rules! add_mouse_button_class_method { ($class:ident, $sel:ident, $event_ty:ident, $button:expr) => { #[allow(non_snake_case)] extern "C" fn $sel(this: &Object, _: Sel, event: id){ - let state: &mut WindowState = unsafe { - WindowState::from_field(this) - }; + let state = unsafe { WindowState::from_view(this) }; let modifiers = unsafe { NSEvent::modifierFlags(event) }; @@ -72,9 +72,7 @@ macro_rules! add_simple_keyboard_class_method { ($class:ident, $sel:ident) => { #[allow(non_snake_case)] extern "C" fn $sel(this: &Object, _: Sel, event: id){ - let state: &mut WindowState = unsafe { - WindowState::from_field(this) - }; + let state = unsafe { WindowState::from_view(this) }; if let Some(key_event) = state.process_native_key_event(event){ let status = state.trigger_event(Event::Keyboard(key_event)); @@ -105,6 +103,11 @@ pub(super) unsafe fn create_view(window_options: &WindowOpenOptions) -> id { view.initWithFrame_(NSRect::new(NSPoint::new(0., 0.), NSSize::new(size.width, size.height))); + let _: id = msg_send![ + view, + registerForDraggedTypes: NSArray::arrayWithObjects(nil, &[NSFilenamesPboardType]) + ]; + view } @@ -131,7 +134,10 @@ unsafe fn create_view_class() -> &'static Class { accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL, ); - class.add_method(sel!(release), release as extern "C" fn(&mut Object, Sel)); + class.add_method( + sel!(windowShouldClose:), + window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL, + ); class.add_method(sel!(dealloc), dealloc as extern "C" fn(&mut Object, Sel)); class.add_method( sel!(viewWillMoveToWindow:), @@ -154,6 +160,24 @@ unsafe fn create_view_class() -> &'static Class { view_did_change_backing_properties as extern "C" fn(&Object, Sel, id), ); + class.add_method( + sel!(draggingEntered:), + dragging_entered as extern "C" fn(&Object, Sel, id) -> NSUInteger, + ); + class.add_method( + sel!(prepareForDragOperation:), + prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + class.add_method( + sel!(performDragOperation:), + perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + class.add_method( + sel!(draggingUpdated:), + dragging_updated as extern "C" fn(&Object, Sel, id) -> NSUInteger, + ); + class.add_method(sel!(draggingExited:), dragging_exited as extern "C" fn(&Object, Sel, id)); + add_mouse_button_class_method!(class, mouseDown, ButtonPressed, MouseButton::Left); add_mouse_button_class_method!(class, mouseUp, ButtonReleased, MouseButton::Left); add_mouse_button_class_method!(class, rightMouseDown, ButtonPressed, MouseButton::Right); @@ -184,37 +208,14 @@ extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL YES } -extern "C" fn release(this: &mut Object, _sel: Sel) { - // Hack for breaking circular references. We store the value of retainCount - // after build(), and then when retainCount drops back to that value, we - // drop the WindowState, hoping that any circular references it holds back - // to the NSView (e.g. wgpu surfaces) get released. - // - // This is definitely broken, since it can be thwarted by e.g. creating a - // wgpu surface at some point after build() (which will mean the NSView - // never gets dealloced) or dropping a wgpu surface at some point before - // drop() (which will mean the WindowState gets dropped early). - // - // TODO: Find a better solution for circular references. - - unsafe { - let retain_count: usize = msg_send![this, retainCount]; +extern "C" fn window_should_close(this: &Object, _: Sel, _sender: id) -> BOOL { + let state = unsafe { WindowState::from_view(this) }; - let state_ptr: *mut c_void = *this.get_ivar(BASEVIEW_STATE_IVAR); + state.trigger_event(Event::Window(WindowEvent::WillClose)); - if !state_ptr.is_null() { - let retain_count_after_build = WindowState::from_field(this).retain_count_after_build; + state.window_inner.close(); - if retain_count <= retain_count_after_build { - WindowState::stop_and_free(this); - } - } - } - - unsafe { - let superclass = msg_send![this, superclass]; - let () = msg_send![super(this, superclass), release]; - } + NO } extern "C" fn dealloc(this: &mut Object, _sel: Sel) { @@ -236,7 +237,7 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel, _: id) { let scale_factor: f64 = if ns_window.is_null() { 1.0 } else { NSWindow::backingScaleFactor(ns_window) }; - let state: &mut WindowState = WindowState::from_field(this); + let state = WindowState::from_view(this); let bounds: NSRect = msg_send![this, bounds]; @@ -245,10 +246,12 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel, _: id) { scale_factor, ); + let window_info = state.window_info.get(); + // Only send the event when the window's size has actually changed to be in line with the // other platform implementations - if new_window_info.physical_size() != state.window_info.physical_size() { - state.window_info = new_window_info; + if new_window_info.physical_size() != window_info.physical_size() { + state.window_info.set(new_window_info); state.trigger_event(Event::Window(WindowEvent::Resized(new_window_info))); } } @@ -334,7 +337,7 @@ extern "C" fn update_tracking_areas(this: &Object, _self: Sel, _: id) { } extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) { - let state: &mut WindowState = unsafe { WindowState::from_field(this) }; + let state = unsafe { WindowState::from_view(this) }; let point: NSPoint = unsafe { let point = NSEvent::locationInWindow(event); @@ -352,7 +355,7 @@ extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) { } extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) { - let state: &mut WindowState = unsafe { WindowState::from_field(this) }; + let state = unsafe { WindowState::from_view(this) }; let delta = unsafe { let x = NSEvent::scrollingDeltaX(event) as f32; @@ -372,3 +375,101 @@ extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) { modifiers: make_modifiers(modifiers), })); } + +fn get_drag_position(sender: id) -> Point { + let point: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + Point::new(point.x, point.y) +} + +fn get_drop_data(sender: id) -> DropData { + if sender == nil { + return DropData::None; + } + + unsafe { + let pasteboard: id = msg_send![sender, draggingPasteboard]; + let file_list: id = msg_send![pasteboard, propertyListForType: NSFilenamesPboardType]; + + if file_list == nil { + return DropData::None; + } + + let mut files = vec![]; + for i in 0..NSArray::count(file_list) { + let data = NSArray::objectAtIndex(file_list, i); + files.push(from_nsstring(data).into()); + } + + DropData::Files(files) + } +} + +fn on_event(window_state: &WindowState, event: MouseEvent) -> NSUInteger { + let event_status = window_state.trigger_event(Event::Mouse(event)); + match event_status { + EventStatus::AcceptDrop(DropEffect::Copy) => NSDragOperationCopy, + EventStatus::AcceptDrop(DropEffect::Move) => NSDragOperationMove, + EventStatus::AcceptDrop(DropEffect::Link) => NSDragOperationLink, + EventStatus::AcceptDrop(DropEffect::Scroll) => NSDragOperationGeneric, + _ => NSDragOperationNone, + } +} + +extern "C" fn dragging_entered(this: &Object, _sel: Sel, sender: id) -> NSUInteger { + let state = unsafe { WindowState::from_view(this) }; + let modifiers = state.keyboard_state().last_mods(); + let drop_data = get_drop_data(sender); + + let event = MouseEvent::DragEntered { + position: get_drag_position(sender), + modifiers: make_modifiers(modifiers), + data: drop_data, + }; + + on_event(&state, event) +} + +extern "C" fn dragging_updated(this: &Object, _sel: Sel, sender: id) -> NSUInteger { + let state = unsafe { WindowState::from_view(this) }; + let modifiers = state.keyboard_state().last_mods(); + let drop_data = get_drop_data(sender); + + let event = MouseEvent::DragMoved { + position: get_drag_position(sender), + modifiers: make_modifiers(modifiers), + data: drop_data, + }; + + on_event(&state, event) +} + +extern "C" fn prepare_for_drag_operation(_this: &Object, _sel: Sel, _sender: id) -> BOOL { + // Always accept drag operation if we get this far + // This function won't be called unless dragging_entered/updated + // has returned an acceptable operation + YES +} + +extern "C" fn perform_drag_operation(this: &Object, _sel: Sel, sender: id) -> BOOL { + let state = unsafe { WindowState::from_view(this) }; + let modifiers = state.keyboard_state().last_mods(); + let drop_data = get_drop_data(sender); + + let event = MouseEvent::DragDropped { + position: get_drag_position(sender), + modifiers: make_modifiers(modifiers), + data: drop_data, + }; + + let event_status = state.trigger_event(Event::Mouse(event)); + match event_status { + EventStatus::AcceptDrop(_) => YES, + _ => NO, + } +} + +extern "C" fn dragging_exited(this: &Object, _sel: Sel, _sender: id) { + let state = unsafe { WindowState::from_view(this) }; + + on_event(&state, MouseEvent::DragLeft); +} diff --git a/src/macos/window.rs b/src/macos/window.rs index 54046ddd..c0412525 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -1,8 +1,7 @@ +use std::cell::{Cell, RefCell}; use std::ffi::c_void; -use std::marker::PhantomData; use std::ptr; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::rc::Rc; use cocoa::appkit::{ NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, @@ -17,10 +16,13 @@ use keyboard_types::KeyboardEvent; use objc::{msg_send, runtime::Object, sel, sel_impl}; -use raw_window_handle::{AppKitHandle, HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::{ + AppKitDisplayHandle, AppKitWindowHandle, HasRawDisplayHandle, HasRawWindowHandle, + RawDisplayHandle, RawWindowHandle, +}; use crate::{ - Event, EventStatus, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, + Event, EventStatus, MouseCursor, Size, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; @@ -28,143 +30,102 @@ use super::keyboard::KeyboardState; use super::view::{create_view, BASEVIEW_STATE_IVAR}; #[cfg(feature = "opengl")] -use crate::{ - gl::{GlConfig, GlContext}, - window::RawWindowHandleWrapper, -}; +use crate::gl::{GlConfig, GlContext}; pub struct WindowHandle { - raw_window_handle: Option, - close_requested: Arc, - is_open: Arc, - - // Ensure handle is !Send - _phantom: PhantomData<*mut ()>, + state: Rc, } impl WindowHandle { pub fn close(&mut self) { - if self.raw_window_handle.take().is_some() { - self.close_requested.store(true, Ordering::Relaxed); - } + self.state.window_inner.close(); } pub fn is_open(&self) -> bool { - self.is_open.load(Ordering::Relaxed) + self.state.window_inner.open.get() } } 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::AppKit(AppKitHandle::empty()) - } -} - -struct ParentHandle { - _close_requested: Arc, - is_open: Arc, -} - -impl ParentHandle { - pub fn new(raw_window_handle: RawWindowHandle) -> (Self, WindowHandle) { - let close_requested = Arc::new(AtomicBool::new(false)); - let is_open = Arc::new(AtomicBool::new(true)); - - let handle = WindowHandle { - raw_window_handle: Some(raw_window_handle), - close_requested: Arc::clone(&close_requested), - is_open: Arc::clone(&is_open), - _phantom: PhantomData::default(), - }; - - (Self { _close_requested: close_requested, is_open }, handle) - } - - /* - pub fn parent_did_drop(&self) -> bool { - self.close_requested.load(Ordering::Relaxed) + self.state.window_inner.raw_window_handle() } - */ } -impl Drop for ParentHandle { - fn drop(&mut self) { - self.is_open.store(false, Ordering::Relaxed); - } -} +pub(super) struct WindowInner { + open: Cell, -pub struct Window { /// Only set if we created the parent window, i.e. we are running in /// parentless mode - ns_app: Option, + ns_app: Cell>, /// Only set if we created the parent window, i.e. we are running in /// parentless mode - ns_window: Option, + ns_window: Cell>, /// Our subclassed NSView ns_view: id, - close_requested: bool, #[cfg(feature = "opengl")] gl_context: Option, } -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 pool = unsafe { NSAutoreleasePool::new(nil) }; +impl WindowInner { + pub(super) fn close(&self) { + if self.open.get() { + self.open.set(false); - let scaling = match options.scale { - WindowScalePolicy::ScaleFactor(scale) => scale, - WindowScalePolicy::SystemScaleFactor => 1.0, - }; + unsafe { + // Take back ownership of the NSView's Rc + let state_ptr: *const c_void = *(*self.ns_view).get_ivar(BASEVIEW_STATE_IVAR); + let window_state = Rc::from_raw(state_ptr as *mut WindowState); - let window_info = WindowInfo::from_logical_size(options.size, scaling); + // Cancel the frame timer + if let Some(frame_timer) = window_state.frame_timer.take() { + CFRunLoop::get_current().remove_timer(&frame_timer, kCFRunLoopDefaultMode); + } - let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() { - handle - } else { - panic!("Not a macOS window"); - }; + drop(window_state); - let ns_view = unsafe { create_view(&options) }; + // Close the window if in non-parented mode + if let Some(ns_window) = self.ns_window.take() { + ns_window.close(); + } - let window = Window { - ns_app: None, - ns_window: None, - ns_view, - close_requested: false, + // Ensure that the NSView is detached from the parent window + self.ns_view.removeFromSuperview(); + let () = msg_send![self.ns_view as id, release]; - #[cfg(feature = "opengl")] - gl_context: options - .gl_config - .map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)), - }; + // If in non-parented mode, we want to also quit the app altogether + let app = self.ns_app.take(); + if let Some(app) = app { + app.stop_(app); + } + } + } + } - let window_handle = Self::init(true, window, window_info, build); + fn raw_window_handle(&self) -> RawWindowHandle { + if self.open.get() { + let ns_window = self.ns_window.get().unwrap_or(ptr::null_mut()) as *mut c_void; - unsafe { - let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view]; - let () = msg_send![ns_view as id, release]; + let mut handle = AppKitWindowHandle::empty(); + handle.ns_window = ns_window; + handle.ns_view = self.ns_view as *mut c_void; - let () = msg_send![pool, drain]; + return RawWindowHandle::AppKit(handle); } - window_handle + RawWindowHandle::AppKit(AppKitWindowHandle::empty()) } +} - pub fn open_as_if_parented(options: WindowOpenOptions, build: B) -> WindowHandle +pub struct Window<'a> { + inner: &'a WindowInner, +} + +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, @@ -178,13 +139,19 @@ impl Window { let window_info = WindowInfo::from_logical_size(options.size, scaling); + let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() { + handle + } else { + panic!("Not a macOS window"); + }; + let ns_view = unsafe { create_view(&options) }; - let window = Window { - ns_app: None, - ns_window: None, + let window_inner = WindowInner { + open: Cell::new(true), + ns_app: Cell::new(None), + ns_window: Cell::new(None), ns_view, - close_requested: false, #[cfg(feature = "opengl")] gl_context: options @@ -192,9 +159,11 @@ impl Window { .map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)), }; - let window_handle = Self::init(true, window, window_info, build); + let window_handle = Self::init(window_inner, window_info, build); unsafe { + let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view]; + let () = msg_send![pool, drain]; } @@ -253,11 +222,11 @@ impl Window { let ns_view = unsafe { create_view(&options) }; - let window = Window { - ns_app: Some(app), - ns_window: Some(ns_window), + let window_inner = WindowInner { + open: Cell::new(true), + ns_app: Cell::new(Some(app)), + ns_window: Cell::new(Some(ns_window)), ns_view, - close_requested: false, #[cfg(feature = "opengl")] gl_context: options @@ -265,165 +234,144 @@ impl Window { .map(|gl_config| Self::create_gl_context(Some(ns_window), ns_view, gl_config)), }; - let _ = Self::init(false, window, window_info, build); + let _ = Self::init(window_inner, window_info, build); unsafe { ns_window.setContentView_(ns_view); + ns_window.setDelegate_(ns_view); - let () = msg_send![ns_view as id, release]; let () = msg_send![pool, drain]; app.run(); } } - fn init( - parented: bool, mut window: Window, window_info: WindowInfo, build: B, - ) -> WindowHandle + fn init(window_inner: WindowInner, window_info: WindowInfo, build: B) -> WindowHandle where H: WindowHandler + 'static, B: FnOnce(&mut crate::Window) -> H, B: Send + 'static, { - let window_handler = Box::new(build(&mut crate::Window::new(&mut window))); + let mut window = crate::Window::new(Window { inner: &window_inner }); + let window_handler = Box::new(build(&mut window)); - let (parent_handle, window_handle) = ParentHandle::new(window.raw_window_handle()); - let parent_handle = if parented { Some(parent_handle) } else { None }; + let ns_view = window_inner.ns_view; - let retain_count_after_build: usize = unsafe { msg_send![window.ns_view, retainCount] }; - - let window_state_ptr = Box::into_raw(Box::new(WindowState { - window, - window_handler, + let window_state = Rc::new(WindowState { + window_inner, + window_handler: RefCell::new(window_handler), keyboard_state: KeyboardState::new(), - frame_timer: None, - retain_count_after_build, - window_info, - _parent_handle: parent_handle, - })); + frame_timer: Cell::new(None), + window_info: Cell::new(window_info), + }); + + let window_state_ptr = Rc::into_raw(Rc::clone(&window_state)); unsafe { - (*(*window_state_ptr).window.ns_view) - .set_ivar(BASEVIEW_STATE_IVAR, window_state_ptr as *mut c_void); + (*ns_view).set_ivar(BASEVIEW_STATE_IVAR, window_state_ptr as *const c_void); WindowState::setup_timer(window_state_ptr); } - window_handle + WindowHandle { state: window_state } } pub fn close(&mut self) { - self.close_requested = true; + self.inner.close(); } pub fn resize(&mut self, size: Size) { - // NOTE: macOS gives you a personal rave if you pass in fractional pixels here. Even though - // the size is in fractional pixels. - let size = NSSize::new(size.width.round(), size.height.round()); + if self.inner.open.get() { + // NOTE: macOS gives you a personal rave if you pass in fractional pixels here. Even + // though the size is in fractional pixels. + let size = NSSize::new(size.width.round(), size.height.round()); - unsafe { NSView::setFrameSize(self.ns_view, size) }; - unsafe { - let _: () = msg_send![self.ns_view, setNeedsDisplay: YES]; - } + unsafe { NSView::setFrameSize(self.inner.ns_view, size) }; + unsafe { + let _: () = msg_send![self.inner.ns_view, setNeedsDisplay: YES]; + } - // When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because macOS. - #[cfg(feature = "opengl")] - if let Some(gl_context) = &self.gl_context { - gl_context.resize(size); - } + // When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because + // macOS. + #[cfg(feature = "opengl")] + if let Some(gl_context) = &self.inner.gl_context { + gl_context.resize(size); + } - // If this is a standalone window then we'll also need to resize the window itself - if let Some(ns_window) = self.ns_window { - unsafe { NSWindow::setContentSize_(ns_window, size) }; + // If this is a standalone window then we'll also need to resize the window itself + if let Some(ns_window) = self.inner.ns_window.get() { + unsafe { NSWindow::setContentSize_(ns_window, size) }; + } } } + pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) { + todo!() + } + #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option<&GlContext> { - self.gl_context.as_ref() + self.inner.gl_context.as_ref() } #[cfg(feature = "opengl")] fn create_gl_context(ns_window: Option, ns_view: id, config: GlConfig) -> GlContext { - let mut handle = AppKitHandle::empty(); + let mut handle = AppKitWindowHandle::empty(); handle.ns_window = ns_window.unwrap_or(ptr::null_mut()) as *mut c_void; handle.ns_view = ns_view as *mut c_void; - let handle = RawWindowHandleWrapper { handle: RawWindowHandle::AppKit(handle) }; + let handle = RawWindowHandle::AppKit(handle); unsafe { GlContext::create(&handle, config).expect("Could not create OpenGL context") } } } pub(super) struct WindowState { - window: Window, - window_handler: Box, + pub(super) window_inner: WindowInner, + window_handler: RefCell>, keyboard_state: KeyboardState, - frame_timer: Option, - _parent_handle: Option, - pub retain_count_after_build: usize, + frame_timer: Cell>, /// The last known window info for this window. - pub window_info: WindowInfo, + pub window_info: Cell, } impl WindowState { - /// Returns a mutable reference to a WindowState from an Objective-C field + /// Gets the `WindowState` held by a given `NSView`. /// - /// Don't use this to create two simulataneous references to a single - /// WindowState. Apparently, macOS blocks for the duration of an event, - /// callback, meaning that this shouldn't be a problem in practice. - pub(super) unsafe fn from_field(obj: &Object) -> &mut Self { - let state_ptr: *mut c_void = *obj.get_ivar(BASEVIEW_STATE_IVAR); + /// This method returns a cloned `Rc` rather than just a `&WindowState`, since the + /// original `Rc` owned by the `NSView` can be dropped at any time + /// (including during an event handler). + pub(super) unsafe fn from_view(view: &Object) -> Rc { + let state_ptr: *const c_void = *view.get_ivar(BASEVIEW_STATE_IVAR); - &mut *(state_ptr as *mut Self) - } + let state_rc = Rc::from_raw(state_ptr as *const WindowState); + let state = Rc::clone(&state_rc); + let _ = Rc::into_raw(state_rc); - pub(super) fn trigger_event(&mut self, event: Event) -> EventStatus { - self.window_handler.on_event(&mut crate::Window::new(&mut self.window), event) + state } - pub(super) fn trigger_frame(&mut self) { - self.window_handler.on_frame(&mut crate::Window::new(&mut self.window)); - - let mut do_close = false; - - /* FIXME: Is it even necessary to check if the parent dropped the handle - // in MacOS? - // Check if the parent handle was dropped - if let Some(parent_handle) = &self.parent_handle { - if parent_handle.parent_did_drop() { - do_close = true; - self.window.close_requested = false; - } - } - */ + pub(super) fn trigger_event(&self, event: Event) -> EventStatus { + let mut window = crate::Window::new(Window { inner: &self.window_inner }); + self.window_handler.borrow_mut().on_event(&mut window, event) + } - // Check if the user requested the window to close - if self.window.close_requested { - do_close = true; - self.window.close_requested = false; - } + pub(super) fn trigger_frame(&self) { + let mut window = crate::Window::new(Window { inner: &self.window_inner }); + self.window_handler.borrow_mut().on_frame(&mut window); + } - if do_close { - unsafe { - if let Some(ns_window) = self.window.ns_window.take() { - ns_window.close(); - } else { - // FIXME: How do we close a non-parented window? Is this even - // possible in a DAW host usecase? - } - } - } + pub(super) fn keyboard_state(&self) -> &KeyboardState { + &self.keyboard_state } - pub(super) fn process_native_key_event(&mut self, event: *mut Object) -> Option { + pub(super) fn process_native_key_event(&self, event: *mut Object) -> Option { self.keyboard_state.process_native_event(event) } - /// Don't call until WindowState pointer is stored in view - unsafe fn setup_timer(window_state_ptr: *mut WindowState) { + unsafe fn setup_timer(window_state_ptr: *const WindowState) { extern "C" fn timer_callback(_: *mut __CFRunLoopTimer, window_state_ptr: *mut c_void) { unsafe { - let window_state = &mut *(window_state_ptr as *mut WindowState); + let window_state = &*(window_state_ptr as *const WindowState); window_state.trigger_frame(); } @@ -441,46 +389,19 @@ impl WindowState { CFRunLoop::get_current().add_timer(&timer, kCFRunLoopDefaultMode); - let window_state = &mut *(window_state_ptr); - - window_state.frame_timer = Some(timer); - } - - /// Call when freeing view - pub(super) unsafe fn stop_and_free(ns_view_obj: &mut Object) { - let state_ptr: *mut c_void = *ns_view_obj.get_ivar(BASEVIEW_STATE_IVAR); - - // Take back ownership of Box so that it gets dropped - // when it goes out of scope - let mut window_state = Box::from_raw(state_ptr as *mut WindowState); - - if let Some(frame_timer) = window_state.frame_timer.take() { - CFRunLoop::get_current().remove_timer(&frame_timer, kCFRunLoopDefaultMode); - } - - // Clear ivar before triggering WindowEvent::WillClose. Otherwise, if the - // handler of the event causes another call to release, this function could be - // called again, leading to a double free. - ns_view_obj.set_ivar(BASEVIEW_STATE_IVAR, ptr::null() as *const c_void); - - window_state.trigger_event(Event::Window(WindowEvent::WillClose)); - - // If in non-parented mode, we want to also quit the app altogether - if let Some(app) = window_state.window.ns_app.take() { - app.stop_(app); - } + (*window_state_ptr).frame_timer.set(Some(timer)); } } -unsafe impl HasRawWindowHandle for Window { +unsafe impl<'a> HasRawWindowHandle for Window<'a> { fn raw_window_handle(&self) -> RawWindowHandle { - let ns_window = self.ns_window.unwrap_or(ptr::null_mut()) as *mut c_void; - - let mut handle = AppKitHandle::empty(); - handle.ns_window = ns_window; - handle.ns_view = self.ns_view as *mut c_void; + self.inner.raw_window_handle() + } +} - RawWindowHandle::AppKit(handle) +unsafe impl<'a> HasRawDisplayHandle for Window<'a> { + fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()) } } diff --git a/src/win/drop_target.rs b/src/win/drop_target.rs index bcfd0367..fd1c55bb 100644 --- a/src/win/drop_target.rs +++ b/src/win/drop_target.rs @@ -2,31 +2,51 @@ use std::ffi::OsString; use std::mem::transmute; use std::os::windows::prelude::OsStringExt; use std::ptr::null_mut; -use std::rc::{Weak, Rc}; +use std::rc::{Rc, Weak}; -use winapi::Interface; -use winapi::shared::guiddef::{REFIID, IsEqualIID}; +use winapi::shared::guiddef::{IsEqualIID, REFIID}; use winapi::shared::minwindef::{DWORD, WPARAM}; use winapi::shared::ntdef::{HRESULT, ULONG}; use winapi::shared::windef::POINTL; -use winapi::shared::winerror::{S_OK, E_NOINTERFACE, E_UNEXPECTED}; +use winapi::shared::winerror::{E_NOINTERFACE, E_UNEXPECTED, S_OK}; use winapi::shared::wtypes::DVASPECT_CONTENT; -use winapi::um::objidl::{IDataObject, FORMATETC, TYMED_HGLOBAL, STGMEDIUM}; -use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl, DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK, DROPEFFECT_SCROLL, DROPEFFECT_NONE}; +use winapi::um::objidl::{IDataObject, FORMATETC, STGMEDIUM, TYMED_HGLOBAL}; +use winapi::um::oleidl::{ + IDropTarget, IDropTargetVtbl, DROPEFFECT_COPY, DROPEFFECT_LINK, DROPEFFECT_MOVE, + DROPEFFECT_NONE, DROPEFFECT_SCROLL, +}; use winapi::um::shellapi::DragQueryFileW; -use winapi::um::unknwnbase::{IUnknownVtbl, IUnknown}; +use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; use winapi::um::winuser::CF_HDROP; +use winapi::Interface; -use crate::{Point, DropData, MouseEvent, Event, EventStatus, DropEffect, PhyPoint}; +use crate::{DropData, DropEffect, Event, EventStatus, MouseEvent, PhyPoint, Point}; use super::WindowState; // These function pointers have to be stored in a (const) variable before they can be transmuted // Transmuting is needed because winapi has a bug where the pt parameter has an incorrect // type `*const POINTL` -const DRAG_ENTER_PTR: unsafe extern "system" fn(this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drag_enter; -const DRAG_OVER_PTR: unsafe extern "system" fn(this: *mut IDropTarget, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drag_over; -const DROP_PTR: unsafe extern "system" fn(this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drop; +const DRAG_ENTER_PTR: unsafe extern "system" fn( + this: *mut IDropTarget, + pDataObj: *const IDataObject, + grfKeyState: DWORD, + pt: POINTL, + pdwEffect: *mut DWORD, +) -> HRESULT = DropTarget::drag_enter; +const DRAG_OVER_PTR: unsafe extern "system" fn( + this: *mut IDropTarget, + grfKeyState: DWORD, + pt: POINTL, + pdwEffect: *mut DWORD, +) -> HRESULT = DropTarget::drag_over; +const DROP_PTR: unsafe extern "system" fn( + this: *mut IDropTarget, + pDataObj: *const IDataObject, + grfKeyState: DWORD, + pt: POINTL, + pdwEffect: *mut DWORD, +) -> HRESULT = DropTarget::drop; const DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl { parent: IUnknownVtbl { QueryInterface: DropTarget::query_interface, @@ -63,17 +83,18 @@ impl DropTarget { } } + #[allow(non_snake_case)] fn on_event(&self, pdwEffect: Option<*mut DWORD>, event: MouseEvent) { let Some(window_state) = self.window_state.upgrade() else { return; }; unsafe { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); - + let mut window = crate::Window::new(window_state.create_window()); + let event = Event::Mouse(event); - let event_status = window_state.handler_mut().as_mut().unwrap().on_event(&mut window, event); + let event_status = + window_state.handler_mut().as_mut().unwrap().on_event(&mut window, event); if let Some(pdwEffect) = pdwEffect { match event_status { @@ -82,8 +103,8 @@ impl DropTarget { EventStatus::AcceptDrop(DropEffect::Link) => *pdwEffect = DROPEFFECT_LINK, EventStatus::AcceptDrop(DropEffect::Scroll) => *pdwEffect = DROPEFFECT_SCROLL, _ => *pdwEffect = DROPEFFECT_NONE, - } - } + } + } } } @@ -105,11 +126,7 @@ impl DropTarget { tymed: TYMED_HGLOBAL, }; - let mut medium = STGMEDIUM { - tymed: 0, - u: null_mut(), - pUnkForRelease: null_mut(), - }; + let mut medium = STGMEDIUM { tymed: 0, u: null_mut(), pUnkForRelease: null_mut() }; unsafe { let hresult = data_object.GetData(&format, &mut medium); @@ -119,13 +136,13 @@ impl DropTarget { } let hdrop = transmute((*medium.u).hGlobal()); - + let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, null_mut(), 0); if item_count == 0 { self.drop_data = DropData::None; return; } - + let mut paths = Vec::with_capacity(item_count as usize); for i in 0..item_count { @@ -133,7 +150,12 @@ impl DropTarget { let buffer_size = characters as usize + 1; let mut buffer = Vec::::with_capacity(buffer_size); - DragQueryFileW(hdrop, i, transmute(buffer.spare_capacity_mut().as_mut_ptr()), buffer_size as u32); + DragQueryFileW( + hdrop, + i, + transmute(buffer.spare_capacity_mut().as_mut_ptr()), + buffer_size as u32, + ); buffer.set_len(buffer_size); paths.push(OsString::from_wide(&buffer[..characters as usize]).into()) @@ -143,21 +165,19 @@ impl DropTarget { } } + #[allow(non_snake_case)] unsafe extern "system" fn query_interface( - this: *mut IUnknown, - riid: REFIID, - ppvObject: *mut *mut winapi::ctypes::c_void, - ) -> HRESULT - { - if IsEqualIID(&*riid, &IUnknown::uuidof()) || IsEqualIID(&*riid, &IDropTarget::uuidof()){ + this: *mut IUnknown, riid: REFIID, ppvObject: *mut *mut winapi::ctypes::c_void, + ) -> HRESULT { + if IsEqualIID(&*riid, &IUnknown::uuidof()) || IsEqualIID(&*riid, &IDropTarget::uuidof()) { Self::add_ref(this); *ppvObject = this as *mut winapi::ctypes::c_void; return S_OK; } - + return E_NOINTERFACE; } - + unsafe extern "system" fn add_ref(this: *mut IUnknown) -> ULONG { let arc = Rc::from_raw(this); let result = Rc::strong_count(&arc) + 1; @@ -167,7 +187,7 @@ impl DropTarget { result as ULONG } - + unsafe extern "system" fn release(this: *mut IUnknown) -> ULONG { let arc = Rc::from_raw(this); let result = Rc::strong_count(&arc) - 1; @@ -177,21 +197,19 @@ impl DropTarget { result as ULONG } - + + #[allow(non_snake_case)] unsafe extern "system" fn drag_enter( - this: *mut IDropTarget, - pDataObj: *const IDataObject, - grfKeyState: DWORD, - pt: POINTL, + this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD, - ) -> HRESULT - { + ) -> HRESULT { let drop_target = &mut *(this as *mut DropTarget); let Some(window_state) = drop_target.window_state.upgrade() else { return E_UNEXPECTED; }; - let modifiers = window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM); + let modifiers = + window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM); drop_target.parse_coordinates(pt); drop_target.parse_drop_data(&*pDataObj); @@ -205,20 +223,18 @@ impl DropTarget { drop_target.on_event(Some(pdwEffect), event); S_OK } - + + #[allow(non_snake_case)] unsafe extern "system" fn drag_over( - this: *mut IDropTarget, - grfKeyState: DWORD, - pt: POINTL, - pdwEffect: *mut DWORD, - ) -> HRESULT - { + this: *mut IDropTarget, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD, + ) -> HRESULT { let drop_target = &mut *(this as *mut DropTarget); let Some(window_state) = drop_target.window_state.upgrade() else { return E_UNEXPECTED; }; - let modifiers = window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM); + let modifiers = + window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM); drop_target.parse_coordinates(pt); @@ -231,27 +247,25 @@ impl DropTarget { drop_target.on_event(Some(pdwEffect), event); S_OK } - + unsafe extern "system" fn drag_leave(this: *mut IDropTarget) -> HRESULT { let drop_target = &mut *(this as *mut DropTarget); drop_target.on_event(None, MouseEvent::DragLeft); S_OK } - + + #[allow(non_snake_case)] unsafe extern "system" fn drop( - this: *mut IDropTarget, - pDataObj: *const IDataObject, - grfKeyState: DWORD, - pt: POINTL, + this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD, - ) -> HRESULT - { + ) -> HRESULT { let drop_target = &mut *(this as *mut DropTarget); let Some(window_state) = drop_target.window_state.upgrade() else { return E_UNEXPECTED; }; - let modifiers = window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM); + let modifiers = + window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM); drop_target.parse_coordinates(pt); drop_target.parse_drop_data(&*pDataObj); diff --git a/src/win/window.rs b/src/win/window.rs index 0a92ba80..ca8ec4cb 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -21,17 +21,19 @@ use winapi::um::winuser::{ use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::VecDeque; use std::ffi::{c_void, OsStr}; -use std::marker::PhantomData; use std::os::windows::ffi::OsStrExt; use std::ptr::null_mut; use std::rc::Rc; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle}; +use raw_window_handle::{ + HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, Win32WindowHandle, + WindowsDisplayHandle, +}; const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1; use crate::{ - Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent, + Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; @@ -39,7 +41,7 @@ use super::drop_target::DropTarget; use super::keyboard::KeyboardState; #[cfg(feature = "opengl")] -use crate::{gl::GlContext, window::RawWindowHandleWrapper}; +use crate::gl::GlContext; unsafe fn generate_guid() -> String { let mut guid: GUID = std::mem::zeroed(); @@ -65,9 +67,6 @@ const WIN_FRAME_TIMER: usize = 4242; pub struct WindowHandle { hwnd: Option, is_open: Rc>, - - // Ensure handle is !Send - _phantom: PhantomData<*mut ()>, } impl WindowHandle { @@ -87,12 +86,12 @@ impl WindowHandle { unsafe impl HasRawWindowHandle for WindowHandle { fn raw_window_handle(&self) -> RawWindowHandle { if let Some(hwnd) = self.hwnd { - let mut handle = Win32Handle::empty(); + let mut handle = Win32WindowHandle::empty(); handle.hwnd = hwnd as *mut c_void; RawWindowHandle::Win32(handle) } else { - RawWindowHandle::Win32(Win32Handle::empty()) + RawWindowHandle::Win32(Win32WindowHandle::empty()) } } } @@ -105,11 +104,7 @@ impl ParentHandle { pub fn new(hwnd: HWND) -> (Self, WindowHandle) { let is_open = Rc::new(Cell::new(true)); - let handle = WindowHandle { - hwnd: Some(hwnd), - is_open: Rc::clone(&is_open), - _phantom: PhantomData::default(), - }; + let handle = WindowHandle { hwnd: Some(hwnd), is_open: Rc::clone(&is_open) }; (Self { is_open }, handle) } @@ -173,10 +168,7 @@ unsafe fn wnd_proc_inner( ) -> Option { match msg { WM_MOUSEMOVE => { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); - let mut handler = window_state.handler.borrow_mut(); - let handler = handler.as_mut().unwrap(); + let mut window = crate::Window::new(window_state.create_window()); let mut mouse_was_outside_window = window_state.mouse_was_outside_window.borrow_mut(); if *mouse_was_outside_window { @@ -194,7 +186,7 @@ unsafe fn wnd_proc_inner( *mouse_was_outside_window = false; let enter_event = Event::Mouse(MouseEvent::CursorEntered); - handler.on_event(&mut window, enter_event); + window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, enter_event); } let x = (lparam & 0xFFFF) as i16 as i32; @@ -209,13 +201,12 @@ unsafe fn wnd_proc_inner( .borrow() .get_modifiers_from_mouse_wparam(wparam), }); - handler.on_event(&mut window, move_event); + window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, move_event); Some(0) } WM_MOUSELEAVE => { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); + let mut window = crate::Window::new(window_state.create_window()); let event = Event::Mouse(MouseEvent::CursorLeft); window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event); @@ -223,8 +214,7 @@ unsafe fn wnd_proc_inner( Some(0) } WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); + let mut window = crate::Window::new(window_state.create_window()); let value = (wparam >> 16) as i16; let value = value as i32; @@ -248,8 +238,7 @@ unsafe fn wnd_proc_inner( } WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); + let mut window = crate::Window::new(window_state.create_window()); let mut mouse_button_counter = window_state.mouse_button_counter.get(); @@ -312,8 +301,7 @@ unsafe fn wnd_proc_inner( None } WM_TIMER => { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); + let mut window = crate::Window::new(window_state.create_window()); if wparam == WIN_FRAME_TIMER { window_state.handler.borrow_mut().as_mut().unwrap().on_frame(&mut window); @@ -324,8 +312,7 @@ unsafe fn wnd_proc_inner( WM_CLOSE => { // Make sure to release the borrow before the DefWindowProc call { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); + let mut window = crate::Window::new(window_state.create_window()); window_state .handler @@ -341,8 +328,7 @@ unsafe fn wnd_proc_inner( } WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP | WM_INPUTLANGCHANGE => { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); + let mut window = crate::Window::new(window_state.create_window()); let opt_event = window_state.keyboard_state.borrow_mut().process_message(hwnd, msg, wparam, lparam); @@ -363,8 +349,7 @@ unsafe fn wnd_proc_inner( } } WM_SIZE => { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); + let mut window = crate::Window::new(window_state.create_window()); let width = (lparam & 0xFFFF) as u16 as u32; let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; @@ -592,17 +577,6 @@ impl Window<'_> { window_handle } - pub fn open_as_if_parented(options: WindowOpenOptions, build: B) -> WindowHandle - where - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { - let (window_handle, _) = Self::open(true, null_mut(), options, build); - - window_handle - } - pub fn open_blocking(options: WindowOpenOptions, build: B) where H: WindowHandler + 'static, @@ -691,9 +665,9 @@ impl Window<'_> { #[cfg(feature = "opengl")] let gl_context: Option = options.gl_config.map(|gl_config| { - let mut handle = Win32Handle::empty(); + let mut handle = Win32WindowHandle::empty(); handle.hwnd = hwnd as *mut c_void; - let handle = RawWindowHandleWrapper { handle: RawWindowHandle::Win32(handle) }; + let handle = RawWindowHandle::Win32(handle); GlContext::create(&handle, gl_config).expect("Could not create OpenGL context") }); @@ -723,8 +697,7 @@ impl Window<'_> { }); let handler = { - let mut window = window_state.create_window(); - let mut window = crate::Window::new(&mut window); + let mut window = crate::Window::new(window_state.create_window()); build(&mut window) }; @@ -804,6 +777,10 @@ impl Window<'_> { self.state.deferred_tasks.borrow_mut().push_back(task); } + pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) { + todo!() + } + #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option<&GlContext> { self.state.gl_context.as_ref() @@ -812,13 +789,19 @@ impl Window<'_> { unsafe impl HasRawWindowHandle for Window<'_> { fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = Win32Handle::empty(); + let mut handle = Win32WindowHandle::empty(); handle.hwnd = self.state.hwnd as *mut c_void; RawWindowHandle::Win32(handle) } } -pub fn copy_to_clipboard(data: &str) { +unsafe impl HasRawDisplayHandle for Window<'_> { + fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::Windows(WindowsDisplayHandle::empty()) + } +} + +pub fn copy_to_clipboard(_data: &str) { todo!() } diff --git a/src/window.rs b/src/window.rs index c0ef1ac8..63e3f49a 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,10 +1,12 @@ use std::marker::PhantomData; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::{ + HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, +}; use crate::event::{Event, EventStatus}; use crate::window_open_options::WindowOpenOptions; -use crate::Size; +use crate::{MouseCursor, Size}; #[cfg(target_os = "macos")] use crate::macos as platform; @@ -19,12 +21,6 @@ pub struct WindowHandle { phantom: PhantomData<*mut ()>, } -/// Quick wrapper to satisfy [HasRawWindowHandle], because of course a raw window handle wouldn't -/// have a raw window handle, that would be silly. -pub(crate) struct RawWindowHandleWrapper { - pub handle: RawWindowHandle, -} - impl WindowHandle { fn new(window_handle: platform::WindowHandle) -> Self { Self { window_handle, phantom: PhantomData::default() } @@ -54,10 +50,7 @@ pub trait WindowHandler { } pub struct Window<'a> { - #[cfg(target_os = "windows")] - window: &'a mut platform::Window<'a>, - #[cfg(not(target_os = "windows"))] - window: &'a mut platform::Window, + window: platform::Window<'a>, // so that Window is !Send on all platforms phantom: PhantomData<*mut ()>, @@ -65,12 +58,12 @@ pub struct Window<'a> { impl<'a> Window<'a> { #[cfg(target_os = "windows")] - pub(crate) fn new(window: &'a mut platform::Window<'a>) -> Window<'a> { + pub(crate) fn new(window: platform::Window<'a>) -> Window<'a> { Window { window, phantom: PhantomData } } #[cfg(not(target_os = "windows"))] - pub(crate) fn new(window: &mut platform::Window) -> Window { + pub(crate) fn new(window: platform::Window) -> Window { Window { window, phantom: PhantomData } } @@ -85,16 +78,6 @@ impl<'a> Window<'a> { WindowHandle::new(window_handle) } - pub fn open_as_if_parented(options: WindowOpenOptions, build: B) -> WindowHandle - where - H: WindowHandler + 'static, - B: FnOnce(&mut Window) -> H, - B: Send + 'static, - { - let window_handle = platform::Window::open_as_if_parented::(options, build); - WindowHandle::new(window_handle) - } - pub fn open_blocking(options: WindowOpenOptions, build: B) where H: WindowHandler + 'static, @@ -115,6 +98,10 @@ impl<'a> Window<'a> { self.window.resize(size); } + pub fn set_mouse_cursor(&mut self, cursor: MouseCursor) { + self.window.set_mouse_cursor(cursor); + } + /// 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")] @@ -129,8 +116,8 @@ unsafe impl<'a> HasRawWindowHandle for Window<'a> { } } -unsafe impl HasRawWindowHandle for RawWindowHandleWrapper { - fn raw_window_handle(&self) -> RawWindowHandle { - self.handle +unsafe impl<'a> HasRawDisplayHandle for Window<'a> { + fn raw_display_handle(&self) -> RawDisplayHandle { + self.window.raw_display_handle() } } diff --git a/src/x11/window.rs b/src/x11/window.rs index fcd90837..90ec281f 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -1,12 +1,14 @@ -use std::marker::PhantomData; -use std::os::raw::{c_ulong, c_void}; +use std::ffi::c_void; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc; use std::sync::Arc; use std::thread; use std::time::*; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, XlibHandle}; +use raw_window_handle::{ + HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, XlibDisplayHandle, + XlibWindowHandle, +}; use xcb::ffi::xcb_screen_t; use xcb::StructPtr; @@ -19,18 +21,12 @@ use crate::{ use super::keyboard::{convert_key_press_event, convert_key_release_event, key_mods}; #[cfg(feature = "opengl")] -use crate::{ - gl::{platform, GlContext}, - window::RawWindowHandleWrapper, -}; +use crate::gl::{platform, GlContext}; pub struct WindowHandle { raw_window_handle: Option, close_requested: Arc, is_open: Arc, - - // Ensure handle is !Send - _phantom: PhantomData<*mut ()>, } impl WindowHandle { @@ -57,7 +53,7 @@ unsafe impl HasRawWindowHandle for WindowHandle { } } - RawWindowHandle::Xlib(XlibHandle::empty()) + RawWindowHandle::Xlib(XlibWindowHandle::empty()) } } @@ -75,7 +71,6 @@ impl ParentHandle { raw_window_handle: None, close_requested: Arc::clone(&close_requested), is_open: Arc::clone(&is_open), - _phantom: PhantomData::default(), }; (Self { close_requested, is_open }, handle) @@ -92,11 +87,11 @@ impl Drop for ParentHandle { } } -pub struct Window { +struct WindowInner { xcb_connection: XcbConnection, window_id: u32, window_info: WindowInfo, - // FIXME: There's all this mouse cursor logic but it's never actually used, is this correct? + visual_id: u32, mouse_cursor: MouseCursor, frame_interval: Duration, @@ -110,6 +105,10 @@ pub struct Window { gl_context: Option, } +pub struct Window<'a> { + inner: &'a mut WindowInner, +} + // Hack to allow sending a RawWindowHandle between threads. Do not make public struct SendableRwh(RawWindowHandle); @@ -117,7 +116,7 @@ unsafe impl Send for SendableRwh {} type WindowOpenResult = Result; -impl Window { +impl<'a> Window<'a> { pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle where P: HasRawWindowHandle, @@ -146,26 +145,6 @@ impl Window { window_handle } - pub fn open_as_if_parented(options: WindowOpenOptions, build: B) -> WindowHandle - where - H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static, - { - let (tx, rx) = mpsc::sync_channel::(1); - - let (parent_handle, mut window_handle) = ParentHandle::new(); - - thread::spawn(move || { - Self::window_thread(None, options, build, tx.clone(), Some(parent_handle)); - }); - - let raw_window_handle = rx.recv().unwrap().unwrap(); - window_handle.raw_window_handle = Some(raw_window_handle.0); - - window_handle - } - pub fn open_blocking(options: WindowOpenOptions, build: B) where H: WindowHandler + 'static, @@ -280,10 +259,12 @@ impl Window { | xcb::EVENT_MASK_BUTTON_RELEASE | xcb::EVENT_MASK_KEY_PRESS | xcb::EVENT_MASK_KEY_RELEASE - | xcb::EVENT_MASK_STRUCTURE_NOTIFY, + | xcb::EVENT_MASK_STRUCTURE_NOTIFY + | xcb::EVENT_MASK_ENTER_WINDOW + | xcb::EVENT_MASK_LEAVE_WINDOW, ), - // As mentioend above, these two values are needed to be able to create a window - // with a dpeth of 32-bits when the parent window has a different depth + // As mentioned above, these two values are needed to be able to create a window + // with a depth of 32-bits when the parent window has a different depth (xcb::CW_COLORMAP, colormap), (xcb::CW_BORDER_PIXEL, 0), ], @@ -323,21 +304,22 @@ impl Window { // compared to when raw-gl-context was a separate crate. #[cfg(feature = "opengl")] let gl_context = fb_config.map(|fb_config| { - let mut handle = XlibHandle::empty(); - handle.window = window_id as c_ulong; - handle.display = xcb_connection.conn.get_raw_dpy() as *mut c_void; - let handle = RawWindowHandleWrapper { handle: RawWindowHandle::Xlib(handle) }; + use std::ffi::c_ulong; + + let window = window_id as c_ulong; + let display = xcb_connection.conn.get_raw_dpy(); // Because of the visual negotation we had to take some extra steps to create this context - let context = unsafe { platform::GlContext::create(&handle, fb_config) } + let context = unsafe { platform::GlContext::create(window, display, fb_config) } .expect("Could not create OpenGL context"); GlContext::new(context) }); - let mut window = Self { + let mut inner = WindowInner { xcb_connection, window_id, window_info, + visual_id: visual, mouse_cursor: MouseCursor::default(), frame_interval: Duration::from_millis(15), @@ -351,57 +333,56 @@ impl Window { gl_context, }; - let mut handler = build(&mut crate::Window::new(&mut window)); + let mut window = crate::Window::new(Window { inner: &mut inner }); + + let mut handler = build(&mut window); // Send an initial window resized event so the user is alerted of // the correct dpi scaling. - handler.on_event( - &mut crate::Window::new(&mut window), - Event::Window(WindowEvent::Resized(window_info)), - ); + handler.on_event(&mut window, Event::Window(WindowEvent::Resized(window_info))); let _ = tx.send(Ok(SendableRwh(window.raw_window_handle()))); - window.run_event_loop(&mut handler); + inner.run_event_loop(&mut handler); } pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) { - if self.mouse_cursor == mouse_cursor { + if self.inner.mouse_cursor == mouse_cursor { return; } - let xid = self.xcb_connection.get_cursor_xid(mouse_cursor); + let xid = self.inner.xcb_connection.get_cursor_xid(mouse_cursor); if xid != 0 { xcb::change_window_attributes( - &self.xcb_connection.conn, - self.window_id, + &self.inner.xcb_connection.conn, + self.inner.window_id, &[(xcb::CW_CURSOR, xid)], ); - self.xcb_connection.conn.flush(); + self.inner.xcb_connection.conn.flush(); } - self.mouse_cursor = mouse_cursor; + self.inner.mouse_cursor = mouse_cursor; } pub fn close(&mut self) { - self.close_requested = true; + self.inner.close_requested = true; } pub fn resize(&mut self, size: Size) { - let scaling = self.window_info.scale(); + let scaling = self.inner.window_info.scale(); let new_window_info = WindowInfo::from_logical_size(size, scaling); xcb::configure_window( - &self.xcb_connection.conn, - self.window_id, + &self.inner.xcb_connection.conn, + self.inner.window_id, &[ (xcb::CONFIG_WINDOW_WIDTH as u16, new_window_info.physical_size().width), (xcb::CONFIG_WINDOW_HEIGHT as u16, new_window_info.physical_size().height), ], ); - self.xcb_connection.conn.flush(); + 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 @@ -409,7 +390,7 @@ impl Window { #[cfg(feature = "opengl")] pub fn gl_context(&self) -> Option<&crate::gl::GlContext> { - self.gl_context.as_ref() + self.inner.gl_context.as_ref() } fn find_visual_for_depth(screen: &StructPtr, depth: u8) -> Option { @@ -427,7 +408,9 @@ impl Window { None } +} +impl WindowInner { #[inline] fn drain_xcb_events(&mut self, handler: &mut dyn WindowHandler) { // the X server has a tendency to send spurious/extraneous configure notify events when a @@ -445,7 +428,7 @@ impl Window { let window_info = self.window_info; handler.on_event( - &mut crate::Window::new(self), + &mut crate::Window::new(Window { inner: self }), Event::Window(WindowEvent::Resized(window_info)), ); } @@ -475,7 +458,7 @@ impl Window { // if it's already time to draw a new frame. let next_frame = last_frame + self.frame_interval; if Instant::now() >= next_frame { - handler.on_frame(&mut crate::Window::new(self)); + handler.on_frame(&mut crate::Window::new(Window { inner: self })); last_frame = Instant::max(next_frame, Instant::now() - self.frame_interval); } @@ -521,14 +504,20 @@ impl Window { } fn handle_close_requested(&mut self, handler: &mut dyn WindowHandler) { - handler.on_event(&mut crate::Window::new(self), Event::Window(WindowEvent::WillClose)); + handler.on_event( + &mut crate::Window::new(Window { inner: self }), + Event::Window(WindowEvent::WillClose), + ); // FIXME: handler should decide whether window stays open or not self.event_loop_running = false; } fn handle_must_close(&mut self, handler: &mut dyn WindowHandler) { - handler.on_event(&mut crate::Window::new(self), Event::Window(WindowEvent::WillClose)); + handler.on_event( + &mut crate::Window::new(Window { inner: self }), + Event::Window(WindowEvent::WillClose), + ); self.event_loop_running = false; } @@ -600,7 +589,7 @@ impl Window { let logical_pos = physical_pos.to_logical(&self.window_info); handler.on_event( - &mut crate::Window::new(self), + &mut crate::Window::new(Window { inner: self }), Event::Mouse(MouseEvent::CursorMoved { position: logical_pos, modifiers: key_mods(event.state()), @@ -609,6 +598,32 @@ impl Window { } } + xcb::ENTER_NOTIFY => { + handler.on_event( + &mut crate::Window::new(Window { inner: self }), + Event::Mouse(MouseEvent::CursorEntered), + ); + // 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 event = unsafe { xcb::cast_event::(&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_info); + handler.on_event( + &mut crate::Window::new(Window { inner: self }), + Event::Mouse(MouseEvent::CursorMoved { + position: logical_pos, + modifiers: key_mods(event.state()), + }), + ); + } + + xcb::LEAVE_NOTIFY => { + handler.on_event( + &mut crate::Window::new(Window { inner: self }), + Event::Mouse(MouseEvent::CursorLeft), + ); + } + xcb::BUTTON_PRESS => { let event = unsafe { xcb::cast_event::(&event) }; let detail = event.detail(); @@ -616,7 +631,7 @@ impl Window { match detail { 4..=7 => { handler.on_event( - &mut crate::Window::new(self), + &mut crate::Window::new(Window { inner: self }), Event::Mouse(MouseEvent::WheelScrolled { delta: match detail { 4 => ScrollDelta::Lines { x: 0.0, y: 1.0 }, @@ -632,7 +647,7 @@ impl Window { detail => { let button_id = mouse_id(detail); handler.on_event( - &mut crate::Window::new(self), + &mut crate::Window::new(Window { inner: self }), Event::Mouse(MouseEvent::ButtonPressed { button: button_id, modifiers: key_mods(event.state()), @@ -649,7 +664,7 @@ impl Window { if !(4..=7).contains(&detail) { let button_id = mouse_id(detail); handler.on_event( - &mut crate::Window::new(self), + &mut crate::Window::new(Window { inner: self }), Event::Mouse(MouseEvent::ButtonReleased { button: button_id, modifiers: key_mods(event.state()), @@ -665,7 +680,7 @@ impl Window { let event = unsafe { xcb::cast_event::(&event) }; handler.on_event( - &mut crate::Window::new(self), + &mut crate::Window::new(Window { inner: self }), Event::Keyboard(convert_key_press_event(event)), ); } @@ -674,7 +689,7 @@ impl Window { let event = unsafe { xcb::cast_event::(&event) }; handler.on_event( - &mut crate::Window::new(self), + &mut crate::Window::new(Window { inner: self }), Event::Keyboard(convert_key_release_event(event)), ); } @@ -684,16 +699,29 @@ impl Window { } } -unsafe impl HasRawWindowHandle for Window { +unsafe impl<'a> HasRawWindowHandle for Window<'a> { fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = XlibHandle::empty(); - handle.window = self.window_id as c_ulong; - handle.display = self.xcb_connection.conn.get_raw_dpy() as *mut c_void; + 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.get_raw_dpy(); + let mut handle = XlibDisplayHandle::empty(); + + handle.display = display as *mut c_void; + handle.screen = unsafe { x11::xlib::XDefaultScreen(display) }; + + RawDisplayHandle::Xlib(handle) + } +} + fn mouse_id(id: u8) -> MouseButton { match id { 1 => MouseButton::Left, @@ -705,6 +733,6 @@ fn mouse_id(id: u8) -> MouseButton { } } -pub fn copy_to_clipboard(data: &str) { +pub fn copy_to_clipboard(_data: &str) { todo!() } diff --git a/src/x11/xcb_connection.rs b/src/x11/xcb_connection.rs index 179e4c73..a11f1403 100644 --- a/src/x11/xcb_connection.rs +++ b/src/x11/xcb_connection.rs @@ -19,7 +19,6 @@ pub struct XcbConnection { pub(crate) atoms: Atoms, - // FIXME: Same here, there's a ton of unused cursor machinery in here pub(super) cursor_cache: HashMap, }