1
0
Fork 0

Compare commits

..

1 commit

Author SHA1 Message Date
Alex Janka 1949bda39b don't disable mouse coalescing on macos 2023-10-09 09:38:06 +11:00
15 changed files with 511 additions and 496 deletions

View file

@ -1,3 +1,3 @@
fn_params_layout = "Compressed" fn_args_layout = "Compressed"
use_small_heuristics = "Max" use_small_heuristics = "Max"
use_field_init_shorthand = true use_field_init_shorthand = true

View file

@ -29,7 +29,7 @@ impl WindowHandler for OpenWindowExample {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
match e { match e {
MouseEvent::ButtonPressed { .. } => { MouseEvent::ButtonPressed { button, modifiers } => {
copy_to_clipboard(&"This is a test!") copy_to_clipboard(&"This is a test!")
} }
_ => (), _ => (),

View file

@ -157,6 +157,6 @@ pub enum EventStatus {
/// plugin window is in focus. /// plugin window is in focus.
Ignored, Ignored,
/// We are prepared to handle the data in the drag and dropping will /// We are prepared to handle the data in the drag and dropping will
/// result in [DropEffect] /// result in [DropEffect]
AcceptDrop(DropEffect), AcceptDrop(DropEffect),
} }

View file

@ -1,7 +1,7 @@
use std::ffi::c_void; use std::ffi::c_void;
use std::str::FromStr; use std::str::FromStr;
use raw_window_handle::RawWindowHandle; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use cocoa::appkit::{ use cocoa::appkit::{
NSOpenGLContext, NSOpenGLContextParameter, NSOpenGLPFAAccelerated, NSOpenGLPFAAlphaSize, NSOpenGLContext, NSOpenGLContextParameter, NSOpenGLPFAAccelerated, NSOpenGLPFAAlphaSize,
@ -28,8 +28,10 @@ pub struct GlContext {
} }
impl GlContext { impl GlContext {
pub unsafe fn create(parent: &RawWindowHandle, config: GlConfig) -> Result<GlContext, GlError> { pub unsafe fn create(
let handle = if let RawWindowHandle::AppKit(handle) = parent { parent: &impl HasRawWindowHandle, config: GlConfig,
) -> Result<GlContext, GlError> {
let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() {
handle handle
} else { } else {
return Err(GlError::InvalidWindowHandle); return Err(GlError::InvalidWindowHandle);

View file

@ -3,7 +3,7 @@ use std::marker::PhantomData;
// On X11 creating the context is a two step process // On X11 creating the context is a two step process
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
use raw_window_handle::RawWindowHandle; use raw_window_handle::HasRawWindowHandle;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod win; mod win;
@ -77,7 +77,7 @@ pub struct GlContext {
impl GlContext { impl GlContext {
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
pub(crate) unsafe fn create( pub(crate) unsafe fn create(
parent: &RawWindowHandle, config: GlConfig, parent: &impl HasRawWindowHandle, config: GlConfig,
) -> Result<GlContext, GlError> { ) -> Result<GlContext, GlError> {
platform::GlContext::create(parent, config) platform::GlContext::create(parent, config)
.map(|context| GlContext { context, phantom: PhantomData }) .map(|context| GlContext { context, phantom: PhantomData })

View file

@ -1,7 +1,7 @@
use std::ffi::{c_void, CString, OsStr}; use std::ffi::{c_void, CString, OsStr};
use std::os::windows::ffi::OsStrExt; use std::os::windows::ffi::OsStrExt;
use raw_window_handle::RawWindowHandle; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use winapi::shared::minwindef::{HINSTANCE, HMODULE}; use winapi::shared::minwindef::{HINSTANCE, HMODULE};
use winapi::shared::ntdef::WCHAR; use winapi::shared::ntdef::WCHAR;
@ -77,8 +77,10 @@ extern "C" {
} }
impl GlContext { impl GlContext {
pub unsafe fn create(parent: &RawWindowHandle, config: GlConfig) -> Result<GlContext, GlError> { pub unsafe fn create(
let handle = if let RawWindowHandle::Win32(handle) = parent { parent: &impl HasRawWindowHandle, config: GlConfig,
) -> Result<GlContext, GlError> {
let handle = if let RawWindowHandle::Win32(handle) = parent.raw_window_handle() {
handle handle
} else { } else {
return Err(GlError::InvalidWindowHandle); return Err(GlError::InvalidWindowHandle);

View file

@ -18,8 +18,6 @@
//! Conversion of platform keyboard event into cross-platform event. //! Conversion of platform keyboard event into cross-platform event.
use std::cell::Cell;
use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventType}; use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventType};
use cocoa::base::id; use cocoa::base::id;
use cocoa::foundation::NSString; use cocoa::foundation::NSString;
@ -46,7 +44,7 @@ pub(crate) fn from_nsstring(s: id) -> String {
/// Most of the logic in this module is adapted from Mozilla, and in particular /// Most of the logic in this module is adapted from Mozilla, and in particular
/// TextInputHandler.mm. /// TextInputHandler.mm.
pub(crate) struct KeyboardState { pub(crate) struct KeyboardState {
last_mods: Cell<NSEventModifierFlags>, last_mods: NSEventModifierFlags,
} }
/// Convert a macOS platform key code (keyCode field of NSEvent). /// Convert a macOS platform key code (keyCode field of NSEvent).
@ -271,15 +269,11 @@ fn is_modifier_code(code: Code) -> bool {
impl KeyboardState { impl KeyboardState {
pub(crate) fn new() -> KeyboardState { pub(crate) fn new() -> KeyboardState {
let last_mods = Cell::new(NSEventModifierFlags::empty()); let last_mods = NSEventModifierFlags::empty();
KeyboardState { last_mods } KeyboardState { last_mods }
} }
pub(crate) fn last_mods(&self) -> NSEventModifierFlags { pub(crate) fn process_native_event(&mut self, event: id) -> Option<KeyboardEvent> {
self.last_mods.get()
}
pub(crate) fn process_native_event(&self, event: id) -> Option<KeyboardEvent> {
unsafe { unsafe {
let event_type = event.eventType(); let event_type = event.eventType();
let key_code = event.keyCode(); let key_code = event.keyCode();
@ -294,8 +288,8 @@ impl KeyboardState {
// We use `bits` here because we want to distinguish the // We use `bits` here because we want to distinguish the
// device dependent bits (when both left and right keys // device dependent bits (when both left and right keys
// may be pressed, for example). // may be pressed, for example).
let any_down = raw_mods.bits() & !self.last_mods.get().bits(); let any_down = raw_mods.bits() & !self.last_mods.bits();
self.last_mods.set(raw_mods); self.last_mods = raw_mods;
if is_modifier_code(code) { if is_modifier_code(code) {
if any_down == 0 { if any_down == 0 {
KeyState::Up KeyState::Up

View file

@ -3,15 +3,3 @@ mod view;
mod window; mod window;
pub use 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::*;

View file

@ -1,8 +1,8 @@
use std::ffi::c_void; use std::ffi::c_void;
use cocoa::appkit::{NSEvent, NSFilenamesPboardType, NSView, NSWindow}; use cocoa::appkit::{NSEvent, NSView, NSWindow};
use cocoa::base::{id, nil, BOOL, NO, YES}; use cocoa::base::{id, nil, BOOL, NO, YES};
use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize, NSUInteger}; use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize};
use objc::{ use objc::{
class, class,
@ -15,16 +15,12 @@ use uuid::Uuid;
use crate::MouseEvent::{ButtonPressed, ButtonReleased}; use crate::MouseEvent::{ButtonPressed, ButtonReleased};
use crate::{ use crate::{
DropData, DropEffect, Event, EventStatus, MouseButton, MouseEvent, Point, ScrollDelta, Size, Event, EventStatus, MouseButton, MouseEvent, Point, ScrollDelta, Size, WindowEvent, WindowInfo,
WindowEvent, WindowInfo, WindowOpenOptions, WindowOpenOptions,
}; };
use super::keyboard::{from_nsstring, make_modifiers}; use super::keyboard::make_modifiers;
use super::window::WindowState; use super::window::WindowState;
use super::{
NSDragOperationCopy, NSDragOperationGeneric, NSDragOperationLink, NSDragOperationMove,
NSDragOperationNone,
};
/// Name of the field used to store the `WindowState` pointer. /// Name of the field used to store the `WindowState` pointer.
pub(super) const BASEVIEW_STATE_IVAR: &str = "baseview_state"; pub(super) const BASEVIEW_STATE_IVAR: &str = "baseview_state";
@ -33,7 +29,9 @@ macro_rules! add_simple_mouse_class_method {
($class:ident, $sel:ident, $event:expr) => { ($class:ident, $sel:ident, $event:expr) => {
#[allow(non_snake_case)] #[allow(non_snake_case)]
extern "C" fn $sel(this: &Object, _: Sel, _: id){ extern "C" fn $sel(this: &Object, _: Sel, _: id){
let state = unsafe { WindowState::from_view(this) }; let state: &mut WindowState = unsafe {
WindowState::from_field(this)
};
state.trigger_event(Event::Mouse($event)); state.trigger_event(Event::Mouse($event));
} }
@ -51,7 +49,9 @@ macro_rules! add_mouse_button_class_method {
($class:ident, $sel:ident, $event_ty:ident, $button:expr) => { ($class:ident, $sel:ident, $event_ty:ident, $button:expr) => {
#[allow(non_snake_case)] #[allow(non_snake_case)]
extern "C" fn $sel(this: &Object, _: Sel, event: id){ extern "C" fn $sel(this: &Object, _: Sel, event: id){
let state = unsafe { WindowState::from_view(this) }; let state: &mut WindowState = unsafe {
WindowState::from_field(this)
};
let modifiers = unsafe { NSEvent::modifierFlags(event) }; let modifiers = unsafe { NSEvent::modifierFlags(event) };
@ -72,7 +72,9 @@ macro_rules! add_simple_keyboard_class_method {
($class:ident, $sel:ident) => { ($class:ident, $sel:ident) => {
#[allow(non_snake_case)] #[allow(non_snake_case)]
extern "C" fn $sel(this: &Object, _: Sel, event: id){ extern "C" fn $sel(this: &Object, _: Sel, event: id){
let state = unsafe { WindowState::from_view(this) }; let state: &mut WindowState = unsafe {
WindowState::from_field(this)
};
if let Some(key_event) = state.process_native_key_event(event){ if let Some(key_event) = state.process_native_key_event(event){
let status = state.trigger_event(Event::Keyboard(key_event)); let status = state.trigger_event(Event::Keyboard(key_event));
@ -103,11 +105,6 @@ pub(super) unsafe fn create_view(window_options: &WindowOpenOptions) -> id {
view.initWithFrame_(NSRect::new(NSPoint::new(0., 0.), NSSize::new(size.width, size.height))); 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 view
} }
@ -134,10 +131,7 @@ unsafe fn create_view_class() -> &'static Class {
accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL, accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
); );
class.add_method( class.add_method(sel!(release), release as extern "C" fn(&mut Object, Sel));
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!(dealloc), dealloc as extern "C" fn(&mut Object, Sel));
class.add_method( class.add_method(
sel!(viewWillMoveToWindow:), sel!(viewWillMoveToWindow:),
@ -160,24 +154,6 @@ unsafe fn create_view_class() -> &'static Class {
view_did_change_backing_properties as extern "C" fn(&Object, Sel, id), 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, mouseDown, ButtonPressed, MouseButton::Left);
add_mouse_button_class_method!(class, mouseUp, ButtonReleased, MouseButton::Left); add_mouse_button_class_method!(class, mouseUp, ButtonReleased, MouseButton::Left);
add_mouse_button_class_method!(class, rightMouseDown, ButtonPressed, MouseButton::Right); add_mouse_button_class_method!(class, rightMouseDown, ButtonPressed, MouseButton::Right);
@ -208,14 +184,37 @@ extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL
YES YES
} }
extern "C" fn window_should_close(this: &Object, _: Sel, _sender: id) -> BOOL { extern "C" fn release(this: &mut Object, _sel: Sel) {
let state = unsafe { WindowState::from_view(this) }; // 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.
state.trigger_event(Event::Window(WindowEvent::WillClose)); unsafe {
let retain_count: usize = msg_send![this, retainCount];
state.window_inner.close(); let state_ptr: *mut c_void = *this.get_ivar(BASEVIEW_STATE_IVAR);
NO if !state_ptr.is_null() {
let retain_count_after_build = WindowState::from_field(this).retain_count_after_build;
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];
}
} }
extern "C" fn dealloc(this: &mut Object, _sel: Sel) { extern "C" fn dealloc(this: &mut Object, _sel: Sel) {
@ -237,7 +236,7 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel, _: id) {
let scale_factor: f64 = let scale_factor: f64 =
if ns_window.is_null() { 1.0 } else { NSWindow::backingScaleFactor(ns_window) }; if ns_window.is_null() { 1.0 } else { NSWindow::backingScaleFactor(ns_window) };
let state = WindowState::from_view(this); let state: &mut WindowState = WindowState::from_field(this);
let bounds: NSRect = msg_send![this, bounds]; let bounds: NSRect = msg_send![this, bounds];
@ -246,12 +245,10 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel, _: id) {
scale_factor, 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 // Only send the event when the window's size has actually changed to be in line with the
// other platform implementations // other platform implementations
if new_window_info.physical_size() != window_info.physical_size() { if new_window_info.physical_size() != state.window_info.physical_size() {
state.window_info.set(new_window_info); state.window_info = new_window_info;
state.trigger_event(Event::Window(WindowEvent::Resized(new_window_info))); state.trigger_event(Event::Window(WindowEvent::Resized(new_window_info)));
} }
} }
@ -335,7 +332,7 @@ extern "C" fn update_tracking_areas(this: &Object, _self: Sel, _: id) {
} }
extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) { extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
let state = unsafe { WindowState::from_view(this) }; let state: &mut WindowState = unsafe { WindowState::from_field(this) };
let point: NSPoint = unsafe { let point: NSPoint = unsafe {
let point = NSEvent::locationInWindow(event); let point = NSEvent::locationInWindow(event);
@ -353,7 +350,7 @@ extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
} }
extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) { extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) {
let state = unsafe { WindowState::from_view(this) }; let state: &mut WindowState = unsafe { WindowState::from_field(this) };
let delta = unsafe { let delta = unsafe {
let x = NSEvent::scrollingDeltaX(event) as f32; let x = NSEvent::scrollingDeltaX(event) as f32;
@ -373,101 +370,3 @@ extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) {
modifiers: make_modifiers(modifiers), 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);
}

View file

@ -1,7 +1,8 @@
use std::cell::{Cell, RefCell};
use std::ffi::c_void; use std::ffi::c_void;
use std::marker::PhantomData;
use std::ptr; use std::ptr;
use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use cocoa::appkit::{ use cocoa::appkit::{
NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered,
@ -22,7 +23,7 @@ use raw_window_handle::{
}; };
use crate::{ use crate::{
Event, EventStatus, MouseCursor, Size, WindowHandler, WindowInfo, WindowOpenOptions, Event, EventStatus, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
WindowScalePolicy, WindowScalePolicy,
}; };
@ -30,99 +31,93 @@ use super::keyboard::KeyboardState;
use super::view::{create_view, BASEVIEW_STATE_IVAR}; use super::view::{create_view, BASEVIEW_STATE_IVAR};
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
use crate::gl::{GlConfig, GlContext}; use crate::{
gl::{GlConfig, GlContext},
window::RawWindowHandleWrapper,
};
pub struct WindowHandle { pub struct WindowHandle {
state: Rc<WindowState>, raw_window_handle: Option<RawWindowHandle>,
close_requested: Arc<AtomicBool>,
is_open: Arc<AtomicBool>,
// Ensure handle is !Send
_phantom: PhantomData<*mut ()>,
} }
impl WindowHandle { impl WindowHandle {
pub fn close(&mut self) { pub fn close(&mut self) {
self.state.window_inner.close(); if self.raw_window_handle.take().is_some() {
self.close_requested.store(true, Ordering::Relaxed);
}
} }
pub fn is_open(&self) -> bool { pub fn is_open(&self) -> bool {
self.state.window_inner.open.get() self.is_open.load(Ordering::Relaxed)
} }
} }
unsafe impl HasRawWindowHandle for WindowHandle { unsafe impl HasRawWindowHandle for WindowHandle {
fn raw_window_handle(&self) -> RawWindowHandle { fn raw_window_handle(&self) -> RawWindowHandle {
self.state.window_inner.raw_window_handle() if let Some(raw_window_handle) = self.raw_window_handle {
} if self.is_open.load(Ordering::Relaxed) {
} return raw_window_handle;
pub(super) struct WindowInner {
open: Cell<bool>,
/// Only set if we created the parent window, i.e. we are running in
/// parentless mode
ns_app: Cell<Option<id>>,
/// Only set if we created the parent window, i.e. we are running in
/// parentless mode
ns_window: Cell<Option<id>>,
/// Our subclassed NSView
ns_view: id,
#[cfg(feature = "opengl")]
gl_context: Option<GlContext>,
}
impl WindowInner {
pub(super) fn close(&self) {
if self.open.get() {
self.open.set(false);
unsafe {
// Take back ownership of the NSView's Rc<WindowState>
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);
// Cancel the frame timer
if let Some(frame_timer) = window_state.frame_timer.take() {
CFRunLoop::get_current().remove_timer(&frame_timer, kCFRunLoopDefaultMode);
}
drop(window_state);
// Close the window if in non-parented mode
if let Some(ns_window) = self.ns_window.take() {
ns_window.close();
}
// Ensure that the NSView is detached from the parent window
self.ns_view.removeFromSuperview();
let () = msg_send![self.ns_view as id, release];
// 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);
}
} }
} }
}
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;
let mut handle = AppKitWindowHandle::empty();
handle.ns_window = ns_window;
handle.ns_view = self.ns_view as *mut c_void;
return RawWindowHandle::AppKit(handle);
}
RawWindowHandle::AppKit(AppKitWindowHandle::empty()) RawWindowHandle::AppKit(AppKitWindowHandle::empty())
} }
} }
pub struct Window<'a> { struct ParentHandle {
inner: &'a WindowInner, _close_requested: Arc<AtomicBool>,
is_open: Arc<AtomicBool>,
} }
impl<'a> Window<'a> { 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)
}
*/
}
impl Drop for ParentHandle {
fn drop(&mut self) {
self.is_open.store(false, Ordering::Relaxed);
}
}
pub struct Window {
/// Only set if we created the parent window, i.e. we are running in
/// parentless mode
ns_app: Option<id>,
/// Only set if we created the parent window, i.e. we are running in
/// parentless mode
ns_window: Option<id>,
/// Our subclassed NSView
ns_view: id,
close_requested: bool,
#[cfg(feature = "opengl")]
gl_context: Option<GlContext>,
}
impl Window {
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
where where
P: HasRawWindowHandle, P: HasRawWindowHandle,
@ -147,11 +142,11 @@ impl<'a> Window<'a> {
let ns_view = unsafe { create_view(&options) }; let ns_view = unsafe { create_view(&options) };
let window_inner = WindowInner { let window = Window {
open: Cell::new(true), ns_app: None,
ns_app: Cell::new(None), ns_window: None,
ns_window: Cell::new(None),
ns_view, ns_view,
close_requested: false,
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
gl_context: options gl_context: options
@ -159,10 +154,11 @@ impl<'a> Window<'a> {
.map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)), .map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)),
}; };
let window_handle = Self::init(window_inner, window_info, build); let window_handle = Self::init(true, window, window_info, build);
unsafe { unsafe {
let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view]; let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view];
let () = msg_send![ns_view as id, release];
let () = msg_send![pool, drain]; let () = msg_send![pool, drain];
} }
@ -170,6 +166,44 @@ impl<'a> Window<'a> {
window_handle window_handle
} }
pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> WindowHandle
where
H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static,
{
let pool = unsafe { NSAutoreleasePool::new(nil) };
let scaling = match options.scale {
WindowScalePolicy::ScaleFactor(scale) => scale,
WindowScalePolicy::SystemScaleFactor => 1.0,
};
let window_info = WindowInfo::from_logical_size(options.size, scaling);
let ns_view = unsafe { create_view(&options) };
let window = Window {
ns_app: None,
ns_window: None,
ns_view,
close_requested: false,
#[cfg(feature = "opengl")]
gl_context: options
.gl_config
.map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)),
};
let window_handle = Self::init(true, window, window_info, build);
unsafe {
let () = msg_send![pool, drain];
}
window_handle
}
pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B) pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
where where
H: WindowHandler + 'static, H: WindowHandler + 'static,
@ -222,11 +256,11 @@ impl<'a> Window<'a> {
let ns_view = unsafe { create_view(&options) }; let ns_view = unsafe { create_view(&options) };
let window_inner = WindowInner { let window = Window {
open: Cell::new(true), ns_app: Some(app),
ns_app: Cell::new(Some(app)), ns_window: Some(ns_window),
ns_window: Cell::new(Some(ns_window)),
ns_view, ns_view,
close_requested: false,
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
gl_context: options gl_context: options
@ -234,84 +268,82 @@ impl<'a> Window<'a> {
.map(|gl_config| Self::create_gl_context(Some(ns_window), ns_view, gl_config)), .map(|gl_config| Self::create_gl_context(Some(ns_window), ns_view, gl_config)),
}; };
let _ = Self::init(window_inner, window_info, build); let _ = Self::init(false, window, window_info, build);
unsafe { unsafe {
ns_window.setContentView_(ns_view); ns_window.setContentView_(ns_view);
ns_window.setDelegate_(ns_view);
let () = msg_send![ns_view as id, release];
let () = msg_send![pool, drain]; let () = msg_send![pool, drain];
app.run(); app.run();
} }
} }
fn init<H, B>(window_inner: WindowInner, window_info: WindowInfo, build: B) -> WindowHandle fn init<H, B>(
parented: bool, mut window: Window, window_info: WindowInfo, build: B,
) -> WindowHandle
where where
H: WindowHandler + 'static, H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H, B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static, B: Send + 'static,
{ {
let mut window = crate::Window::new(Window { inner: &window_inner }); let window_handler = Box::new(build(&mut crate::Window::new(&mut window)));
let window_handler = Box::new(build(&mut window));
let ns_view = window_inner.ns_view; let (parent_handle, window_handle) = ParentHandle::new(window.raw_window_handle());
let parent_handle = if parented { Some(parent_handle) } else { None };
let window_state = Rc::new(WindowState { let retain_count_after_build: usize = unsafe { msg_send![window.ns_view, retainCount] };
window_inner,
window_handler: RefCell::new(window_handler), let window_state_ptr = Box::into_raw(Box::new(WindowState {
window,
window_handler,
keyboard_state: KeyboardState::new(), keyboard_state: KeyboardState::new(),
frame_timer: Cell::new(None), frame_timer: None,
window_info: Cell::new(window_info), retain_count_after_build,
}); window_info,
_parent_handle: parent_handle,
let window_state_ptr = Rc::into_raw(Rc::clone(&window_state)); }));
unsafe { unsafe {
(*ns_view).set_ivar(BASEVIEW_STATE_IVAR, window_state_ptr as *const c_void); (*(*window_state_ptr).window.ns_view)
.set_ivar(BASEVIEW_STATE_IVAR, window_state_ptr as *mut c_void);
WindowState::setup_timer(window_state_ptr); WindowState::setup_timer(window_state_ptr);
} }
WindowHandle { state: window_state } window_handle
} }
pub fn close(&mut self) { pub fn close(&mut self) {
self.inner.close(); self.close_requested = true;
} }
pub fn resize(&mut self, size: Size) { pub fn resize(&mut self, size: Size) {
if self.inner.open.get() { // NOTE: macOS gives you a personal rave if you pass in fractional pixels here. Even though
// NOTE: macOS gives you a personal rave if you pass in fractional pixels here. Even // the size is in fractional pixels.
// though the size is in fractional pixels. let size = NSSize::new(size.width.round(), size.height.round());
let size = NSSize::new(size.width.round(), size.height.round());
unsafe { NSView::setFrameSize(self.inner.ns_view, size) }; unsafe { NSView::setFrameSize(self.ns_view, size) };
unsafe { unsafe {
let _: () = msg_send![self.inner.ns_view, setNeedsDisplay: YES]; let _: () = msg_send![self.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.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.inner.ns_window.get() {
unsafe { NSWindow::setContentSize_(ns_window, size) };
}
} }
}
pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) { // When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because macOS.
todo!() #[cfg(feature = "opengl")]
if let Some(gl_context) = &self.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) };
}
} }
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
pub fn gl_context(&self) -> Option<&GlContext> { pub fn gl_context(&self) -> Option<&GlContext> {
self.inner.gl_context.as_ref() self.gl_context.as_ref()
} }
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
@ -319,59 +351,82 @@ impl<'a> Window<'a> {
let mut handle = AppKitWindowHandle::empty(); let mut handle = AppKitWindowHandle::empty();
handle.ns_window = ns_window.unwrap_or(ptr::null_mut()) as *mut c_void; handle.ns_window = ns_window.unwrap_or(ptr::null_mut()) as *mut c_void;
handle.ns_view = ns_view as *mut c_void; handle.ns_view = ns_view as *mut c_void;
let handle = RawWindowHandle::AppKit(handle); let handle = RawWindowHandleWrapper { handle: RawWindowHandle::AppKit(handle) };
unsafe { GlContext::create(&handle, config).expect("Could not create OpenGL context") } unsafe { GlContext::create(&handle, config).expect("Could not create OpenGL context") }
} }
} }
pub(super) struct WindowState { pub(super) struct WindowState {
pub(super) window_inner: WindowInner, window: Window,
window_handler: RefCell<Box<dyn WindowHandler>>, window_handler: Box<dyn WindowHandler>,
keyboard_state: KeyboardState, keyboard_state: KeyboardState,
frame_timer: Cell<Option<CFRunLoopTimer>>, frame_timer: Option<CFRunLoopTimer>,
_parent_handle: Option<ParentHandle>,
pub retain_count_after_build: usize,
/// The last known window info for this window. /// The last known window info for this window.
pub window_info: Cell<WindowInfo>, pub window_info: WindowInfo,
} }
impl WindowState { impl WindowState {
/// Gets the `WindowState` held by a given `NSView`. /// Returns a mutable reference to a WindowState from an Objective-C field
/// ///
/// This method returns a cloned `Rc<WindowState>` rather than just a `&WindowState`, since the /// Don't use this to create two simulataneous references to a single
/// original `Rc<WindowState>` owned by the `NSView` can be dropped at any time /// WindowState. Apparently, macOS blocks for the duration of an event,
/// (including during an event handler). /// callback, meaning that this shouldn't be a problem in practice.
pub(super) unsafe fn from_view(view: &Object) -> Rc<WindowState> { pub(super) unsafe fn from_field(obj: &Object) -> &mut Self {
let state_ptr: *const c_void = *view.get_ivar(BASEVIEW_STATE_IVAR); let state_ptr: *mut c_void = *obj.get_ivar(BASEVIEW_STATE_IVAR);
let state_rc = Rc::from_raw(state_ptr as *const WindowState); &mut *(state_ptr as *mut Self)
let state = Rc::clone(&state_rc);
let _ = Rc::into_raw(state_rc);
state
} }
pub(super) fn trigger_event(&self, event: Event) -> EventStatus { pub(super) fn trigger_event(&mut self, event: Event) -> EventStatus {
let mut window = crate::Window::new(Window { inner: &self.window_inner }); self.window_handler.on_event(&mut crate::Window::new(&mut self.window), event)
self.window_handler.borrow_mut().on_event(&mut window, event)
} }
pub(super) fn trigger_frame(&self) { pub(super) fn trigger_frame(&mut self) {
let mut window = crate::Window::new(Window { inner: &self.window_inner }); self.window_handler.on_frame(&mut crate::Window::new(&mut self.window));
self.window_handler.borrow_mut().on_frame(&mut 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;
}
}
*/
// Check if the user requested the window to close
if self.window.close_requested {
do_close = true;
self.window.close_requested = false;
}
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 { pub(super) fn process_native_key_event(&mut self, event: *mut Object) -> Option<KeyboardEvent> {
&self.keyboard_state
}
pub(super) fn process_native_key_event(&self, event: *mut Object) -> Option<KeyboardEvent> {
self.keyboard_state.process_native_event(event) self.keyboard_state.process_native_event(event)
} }
unsafe fn setup_timer(window_state_ptr: *const WindowState) { /// Don't call until WindowState pointer is stored in view
unsafe fn setup_timer(window_state_ptr: *mut WindowState) {
extern "C" fn timer_callback(_: *mut __CFRunLoopTimer, window_state_ptr: *mut c_void) { extern "C" fn timer_callback(_: *mut __CFRunLoopTimer, window_state_ptr: *mut c_void) {
unsafe { unsafe {
let window_state = &*(window_state_ptr as *const WindowState); let window_state = &mut *(window_state_ptr as *mut WindowState);
window_state.trigger_frame(); window_state.trigger_frame();
} }
@ -389,17 +444,50 @@ impl WindowState {
CFRunLoop::get_current().add_timer(&timer, kCFRunLoopDefaultMode); CFRunLoop::get_current().add_timer(&timer, kCFRunLoopDefaultMode);
(*window_state_ptr).frame_timer.set(Some(timer)); 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<WindowState> 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);
}
} }
} }
unsafe impl<'a> HasRawWindowHandle for Window<'a> { unsafe impl HasRawWindowHandle for Window {
fn raw_window_handle(&self) -> RawWindowHandle { fn raw_window_handle(&self) -> RawWindowHandle {
self.inner.raw_window_handle() let ns_window = self.ns_window.unwrap_or(ptr::null_mut()) as *mut c_void;
let mut handle = AppKitWindowHandle::empty();
handle.ns_window = ns_window;
handle.ns_view = self.ns_view as *mut c_void;
RawWindowHandle::AppKit(handle)
} }
} }
unsafe impl<'a> HasRawDisplayHandle for Window<'a> { unsafe impl HasRawDisplayHandle for Window {
fn raw_display_handle(&self) -> RawDisplayHandle { fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()) RawDisplayHandle::AppKit(AppKitDisplayHandle::empty())
} }

View file

@ -2,51 +2,31 @@ use std::ffi::OsString;
use std::mem::transmute; use std::mem::transmute;
use std::os::windows::prelude::OsStringExt; use std::os::windows::prelude::OsStringExt;
use std::ptr::null_mut; use std::ptr::null_mut;
use std::rc::{Rc, Weak}; use std::rc::{Weak, Rc};
use winapi::shared::guiddef::{IsEqualIID, REFIID}; use winapi::Interface;
use winapi::shared::guiddef::{REFIID, IsEqualIID};
use winapi::shared::minwindef::{DWORD, WPARAM}; use winapi::shared::minwindef::{DWORD, WPARAM};
use winapi::shared::ntdef::{HRESULT, ULONG}; use winapi::shared::ntdef::{HRESULT, ULONG};
use winapi::shared::windef::POINTL; use winapi::shared::windef::POINTL;
use winapi::shared::winerror::{E_NOINTERFACE, E_UNEXPECTED, S_OK}; use winapi::shared::winerror::{S_OK, E_NOINTERFACE, E_UNEXPECTED};
use winapi::shared::wtypes::DVASPECT_CONTENT; use winapi::shared::wtypes::DVASPECT_CONTENT;
use winapi::um::objidl::{IDataObject, FORMATETC, STGMEDIUM, TYMED_HGLOBAL}; use winapi::um::objidl::{IDataObject, FORMATETC, TYMED_HGLOBAL, STGMEDIUM};
use winapi::um::oleidl::{ use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl, DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK, DROPEFFECT_SCROLL, DROPEFFECT_NONE};
IDropTarget, IDropTargetVtbl, DROPEFFECT_COPY, DROPEFFECT_LINK, DROPEFFECT_MOVE,
DROPEFFECT_NONE, DROPEFFECT_SCROLL,
};
use winapi::um::shellapi::DragQueryFileW; use winapi::um::shellapi::DragQueryFileW;
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; use winapi::um::unknwnbase::{IUnknownVtbl, IUnknown};
use winapi::um::winuser::CF_HDROP; use winapi::um::winuser::CF_HDROP;
use winapi::Interface;
use crate::{DropData, DropEffect, Event, EventStatus, MouseEvent, PhyPoint, Point}; use crate::{Point, DropData, MouseEvent, Event, EventStatus, DropEffect, PhyPoint};
use super::WindowState; use super::WindowState;
// These function pointers have to be stored in a (const) variable before they can be transmuted // 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 // Transmuting is needed because winapi has a bug where the pt parameter has an incorrect
// type `*const POINTL` // type `*const POINTL`
const DRAG_ENTER_PTR: unsafe extern "system" fn( 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;
this: *mut IDropTarget, const DRAG_OVER_PTR: unsafe extern "system" fn(this: *mut IDropTarget, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drag_over;
pDataObj: *const IDataObject, const DROP_PTR: unsafe extern "system" fn(this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD) -> HRESULT = DropTarget::drop;
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 { const DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl {
parent: IUnknownVtbl { parent: IUnknownVtbl {
QueryInterface: DropTarget::query_interface, QueryInterface: DropTarget::query_interface,
@ -83,18 +63,17 @@ impl DropTarget {
} }
} }
#[allow(non_snake_case)]
fn on_event(&self, pdwEffect: Option<*mut DWORD>, event: MouseEvent) { fn on_event(&self, pdwEffect: Option<*mut DWORD>, event: MouseEvent) {
let Some(window_state) = self.window_state.upgrade() else { let Some(window_state) = self.window_state.upgrade() else {
return; return;
}; };
unsafe { unsafe {
let mut window = crate::Window::new(window_state.create_window()); let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let event = Event::Mouse(event); let event = Event::Mouse(event);
let event_status = let event_status = window_state.handler_mut().as_mut().unwrap().on_event(&mut window, event);
window_state.handler_mut().as_mut().unwrap().on_event(&mut window, event);
if let Some(pdwEffect) = pdwEffect { if let Some(pdwEffect) = pdwEffect {
match event_status { match event_status {
@ -103,8 +82,8 @@ impl DropTarget {
EventStatus::AcceptDrop(DropEffect::Link) => *pdwEffect = DROPEFFECT_LINK, EventStatus::AcceptDrop(DropEffect::Link) => *pdwEffect = DROPEFFECT_LINK,
EventStatus::AcceptDrop(DropEffect::Scroll) => *pdwEffect = DROPEFFECT_SCROLL, EventStatus::AcceptDrop(DropEffect::Scroll) => *pdwEffect = DROPEFFECT_SCROLL,
_ => *pdwEffect = DROPEFFECT_NONE, _ => *pdwEffect = DROPEFFECT_NONE,
} }
} }
} }
} }
@ -126,7 +105,11 @@ impl DropTarget {
tymed: TYMED_HGLOBAL, 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 { unsafe {
let hresult = data_object.GetData(&format, &mut medium); let hresult = data_object.GetData(&format, &mut medium);
@ -136,13 +119,13 @@ impl DropTarget {
} }
let hdrop = transmute((*medium.u).hGlobal()); let hdrop = transmute((*medium.u).hGlobal());
let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, null_mut(), 0); let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, null_mut(), 0);
if item_count == 0 { if item_count == 0 {
self.drop_data = DropData::None; self.drop_data = DropData::None;
return; return;
} }
let mut paths = Vec::with_capacity(item_count as usize); let mut paths = Vec::with_capacity(item_count as usize);
for i in 0..item_count { for i in 0..item_count {
@ -150,12 +133,7 @@ impl DropTarget {
let buffer_size = characters as usize + 1; let buffer_size = characters as usize + 1;
let mut buffer = Vec::<u16>::with_capacity(buffer_size); let mut buffer = Vec::<u16>::with_capacity(buffer_size);
DragQueryFileW( DragQueryFileW(hdrop, i, transmute(buffer.spare_capacity_mut().as_mut_ptr()), buffer_size as u32);
hdrop,
i,
transmute(buffer.spare_capacity_mut().as_mut_ptr()),
buffer_size as u32,
);
buffer.set_len(buffer_size); buffer.set_len(buffer_size);
paths.push(OsString::from_wide(&buffer[..characters as usize]).into()) paths.push(OsString::from_wide(&buffer[..characters as usize]).into())
@ -165,19 +143,21 @@ impl DropTarget {
} }
} }
#[allow(non_snake_case)]
unsafe extern "system" fn query_interface( unsafe extern "system" fn query_interface(
this: *mut IUnknown, riid: REFIID, ppvObject: *mut *mut winapi::ctypes::c_void, this: *mut IUnknown,
) -> HRESULT { riid: REFIID,
if IsEqualIID(&*riid, &IUnknown::uuidof()) || IsEqualIID(&*riid, &IDropTarget::uuidof()) { ppvObject: *mut *mut winapi::ctypes::c_void,
) -> HRESULT
{
if IsEqualIID(&*riid, &IUnknown::uuidof()) || IsEqualIID(&*riid, &IDropTarget::uuidof()){
Self::add_ref(this); Self::add_ref(this);
*ppvObject = this as *mut winapi::ctypes::c_void; *ppvObject = this as *mut winapi::ctypes::c_void;
return S_OK; return S_OK;
} }
return E_NOINTERFACE; return E_NOINTERFACE;
} }
unsafe extern "system" fn add_ref(this: *mut IUnknown) -> ULONG { unsafe extern "system" fn add_ref(this: *mut IUnknown) -> ULONG {
let arc = Rc::from_raw(this); let arc = Rc::from_raw(this);
let result = Rc::strong_count(&arc) + 1; let result = Rc::strong_count(&arc) + 1;
@ -187,7 +167,7 @@ impl DropTarget {
result as ULONG result as ULONG
} }
unsafe extern "system" fn release(this: *mut IUnknown) -> ULONG { unsafe extern "system" fn release(this: *mut IUnknown) -> ULONG {
let arc = Rc::from_raw(this); let arc = Rc::from_raw(this);
let result = Rc::strong_count(&arc) - 1; let result = Rc::strong_count(&arc) - 1;
@ -197,19 +177,21 @@ impl DropTarget {
result as ULONG result as ULONG
} }
#[allow(non_snake_case)]
unsafe extern "system" fn drag_enter( 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, pdwEffect: *mut DWORD,
) -> HRESULT { ) -> HRESULT
{
let drop_target = &mut *(this as *mut DropTarget); let drop_target = &mut *(this as *mut DropTarget);
let Some(window_state) = drop_target.window_state.upgrade() else { let Some(window_state) = drop_target.window_state.upgrade() else {
return E_UNEXPECTED; return E_UNEXPECTED;
}; };
let modifiers = let modifiers = window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
drop_target.parse_coordinates(pt); drop_target.parse_coordinates(pt);
drop_target.parse_drop_data(&*pDataObj); drop_target.parse_drop_data(&*pDataObj);
@ -223,18 +205,20 @@ impl DropTarget {
drop_target.on_event(Some(pdwEffect), event); drop_target.on_event(Some(pdwEffect), event);
S_OK S_OK
} }
#[allow(non_snake_case)]
unsafe extern "system" fn drag_over( unsafe extern "system" fn drag_over(
this: *mut IDropTarget, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD, this: *mut IDropTarget,
) -> HRESULT { grfKeyState: DWORD,
pt: POINTL,
pdwEffect: *mut DWORD,
) -> HRESULT
{
let drop_target = &mut *(this as *mut DropTarget); let drop_target = &mut *(this as *mut DropTarget);
let Some(window_state) = drop_target.window_state.upgrade() else { let Some(window_state) = drop_target.window_state.upgrade() else {
return E_UNEXPECTED; return E_UNEXPECTED;
}; };
let modifiers = let modifiers = window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
drop_target.parse_coordinates(pt); drop_target.parse_coordinates(pt);
@ -247,25 +231,27 @@ impl DropTarget {
drop_target.on_event(Some(pdwEffect), event); drop_target.on_event(Some(pdwEffect), event);
S_OK S_OK
} }
unsafe extern "system" fn drag_leave(this: *mut IDropTarget) -> HRESULT { unsafe extern "system" fn drag_leave(this: *mut IDropTarget) -> HRESULT {
let drop_target = &mut *(this as *mut DropTarget); let drop_target = &mut *(this as *mut DropTarget);
drop_target.on_event(None, MouseEvent::DragLeft); drop_target.on_event(None, MouseEvent::DragLeft);
S_OK S_OK
} }
#[allow(non_snake_case)]
unsafe extern "system" fn drop( 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, pdwEffect: *mut DWORD,
) -> HRESULT { ) -> HRESULT
{
let drop_target = &mut *(this as *mut DropTarget); let drop_target = &mut *(this as *mut DropTarget);
let Some(window_state) = drop_target.window_state.upgrade() else { let Some(window_state) = drop_target.window_state.upgrade() else {
return E_UNEXPECTED; return E_UNEXPECTED;
}; };
let modifiers = let modifiers = window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM);
drop_target.parse_coordinates(pt); drop_target.parse_coordinates(pt);
drop_target.parse_drop_data(&*pDataObj); drop_target.parse_drop_data(&*pDataObj);

View file

@ -2,7 +2,7 @@ use winapi::shared::guiddef::GUID;
use winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM}; use winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM};
use winapi::shared::windef::{HWND, RECT}; use winapi::shared::windef::{HWND, RECT};
use winapi::um::combaseapi::CoCreateGuid; use winapi::um::combaseapi::CoCreateGuid;
use winapi::um::ole2::{OleInitialize, RegisterDragDrop, RevokeDragDrop}; use winapi::um::ole2::{RegisterDragDrop, OleInitialize, RevokeDragDrop};
use winapi::um::oleidl::LPDROPTARGET; use winapi::um::oleidl::LPDROPTARGET;
use winapi::um::winuser::{ use winapi::um::winuser::{
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW,
@ -18,9 +18,10 @@ use winapi::um::winuser::{
XBUTTON1, XBUTTON2, XBUTTON1, XBUTTON2,
}; };
use std::cell::{Cell, Ref, RefCell, RefMut}; use std::cell::{Cell, RefCell, Ref, RefMut};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ffi::{c_void, OsStr}; use std::ffi::{c_void, OsStr};
use std::marker::PhantomData;
use std::os::windows::ffi::OsStrExt; use std::os::windows::ffi::OsStrExt;
use std::ptr::null_mut; use std::ptr::null_mut;
use std::rc::Rc; use std::rc::Rc;
@ -33,7 +34,7 @@ use raw_window_handle::{
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1; const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
use crate::{ use crate::{
Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent, Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent,
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
}; };
@ -41,7 +42,7 @@ use super::drop_target::DropTarget;
use super::keyboard::KeyboardState; use super::keyboard::KeyboardState;
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
use crate::gl::GlContext; use crate::{gl::GlContext, window::RawWindowHandleWrapper};
unsafe fn generate_guid() -> String { unsafe fn generate_guid() -> String {
let mut guid: GUID = std::mem::zeroed(); let mut guid: GUID = std::mem::zeroed();
@ -67,6 +68,9 @@ const WIN_FRAME_TIMER: usize = 4242;
pub struct WindowHandle { pub struct WindowHandle {
hwnd: Option<HWND>, hwnd: Option<HWND>,
is_open: Rc<Cell<bool>>, is_open: Rc<Cell<bool>>,
// Ensure handle is !Send
_phantom: PhantomData<*mut ()>,
} }
impl WindowHandle { impl WindowHandle {
@ -104,7 +108,11 @@ impl ParentHandle {
pub fn new(hwnd: HWND) -> (Self, WindowHandle) { pub fn new(hwnd: HWND) -> (Self, WindowHandle) {
let is_open = Rc::new(Cell::new(true)); let is_open = Rc::new(Cell::new(true));
let handle = WindowHandle { hwnd: Some(hwnd), is_open: Rc::clone(&is_open) }; let handle = WindowHandle {
hwnd: Some(hwnd),
is_open: Rc::clone(&is_open),
_phantom: PhantomData::default(),
};
(Self { is_open }, handle) (Self { is_open }, handle)
} }
@ -168,7 +176,8 @@ unsafe fn wnd_proc_inner(
) -> Option<LRESULT> { ) -> Option<LRESULT> {
match msg { match msg {
WM_MOUSEMOVE => { WM_MOUSEMOVE => {
let mut window = crate::Window::new(window_state.create_window()); let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let x = (lparam & 0xFFFF) as i16 as i32; let x = (lparam & 0xFFFF) as i16 as i32;
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32; let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
@ -188,7 +197,8 @@ unsafe fn wnd_proc_inner(
Some(0) Some(0)
} }
WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { WM_MOUSEWHEEL | WM_MOUSEHWHEEL => {
let mut window = crate::Window::new(window_state.create_window()); let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let value = (wparam >> 16) as i16; let value = (wparam >> 16) as i16;
let value = value as i32; let value = value as i32;
@ -212,7 +222,8 @@ unsafe fn wnd_proc_inner(
} }
WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN
| WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => {
let mut window = crate::Window::new(window_state.create_window()); let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let mut mouse_button_counter = window_state.mouse_button_counter.get(); let mut mouse_button_counter = window_state.mouse_button_counter.get();
@ -275,7 +286,8 @@ unsafe fn wnd_proc_inner(
None None
} }
WM_TIMER => { WM_TIMER => {
let mut window = crate::Window::new(window_state.create_window()); let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
if wparam == WIN_FRAME_TIMER { if wparam == WIN_FRAME_TIMER {
window_state.handler.borrow_mut().as_mut().unwrap().on_frame(&mut window); window_state.handler.borrow_mut().as_mut().unwrap().on_frame(&mut window);
@ -286,7 +298,8 @@ unsafe fn wnd_proc_inner(
WM_CLOSE => { WM_CLOSE => {
// Make sure to release the borrow before the DefWindowProc call // Make sure to release the borrow before the DefWindowProc call
{ {
let mut window = crate::Window::new(window_state.create_window()); let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
window_state window_state
.handler .handler
@ -302,7 +315,8 @@ unsafe fn wnd_proc_inner(
} }
WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP
| WM_INPUTLANGCHANGE => { | WM_INPUTLANGCHANGE => {
let mut window = crate::Window::new(window_state.create_window()); let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let opt_event = let opt_event =
window_state.keyboard_state.borrow_mut().process_message(hwnd, msg, wparam, lparam); window_state.keyboard_state.borrow_mut().process_message(hwnd, msg, wparam, lparam);
@ -323,7 +337,8 @@ unsafe fn wnd_proc_inner(
} }
} }
WM_SIZE => { WM_SIZE => {
let mut window = crate::Window::new(window_state.create_window()); let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
let width = (lparam & 0xFFFF) as u16 as u32; let width = (lparam & 0xFFFF) as u16 as u32;
let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; let height = ((lparam >> 16) & 0xFFFF) as u16 as u32;
@ -550,6 +565,17 @@ impl Window<'_> {
window_handle window_handle
} }
pub fn open_as_if_parented<H, B>(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<H, B>(options: WindowOpenOptions, build: B) pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
where where
H: WindowHandler + 'static, H: WindowHandler + 'static,
@ -640,7 +666,7 @@ impl Window<'_> {
let gl_context: Option<GlContext> = options.gl_config.map(|gl_config| { let gl_context: Option<GlContext> = options.gl_config.map(|gl_config| {
let mut handle = Win32WindowHandle::empty(); let mut handle = Win32WindowHandle::empty();
handle.hwnd = hwnd as *mut c_void; handle.hwnd = hwnd as *mut c_void;
let handle = RawWindowHandle::Win32(handle); let handle = RawWindowHandleWrapper { handle: RawWindowHandle::Win32(handle) };
GlContext::create(&handle, gl_config).expect("Could not create OpenGL context") GlContext::create(&handle, gl_config).expect("Could not create OpenGL context")
}); });
@ -669,7 +695,8 @@ impl Window<'_> {
}); });
let handler = { let handler = {
let mut window = crate::Window::new(window_state.create_window()); let mut window = window_state.create_window();
let mut window = crate::Window::new(&mut window);
build(&mut window) build(&mut window)
}; };
@ -749,10 +776,6 @@ impl Window<'_> {
self.state.deferred_tasks.borrow_mut().push_back(task); self.state.deferred_tasks.borrow_mut().push_back(task);
} }
pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) {
todo!()
}
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
pub fn gl_context(&self) -> Option<&GlContext> { pub fn gl_context(&self) -> Option<&GlContext> {
self.state.gl_context.as_ref() self.state.gl_context.as_ref()
@ -774,6 +797,6 @@ unsafe impl HasRawDisplayHandle for Window<'_> {
} }
} }
pub fn copy_to_clipboard(_data: &str) { pub fn copy_to_clipboard(data: &str) {
todo!() todo!()
} }

View file

@ -6,7 +6,7 @@ use raw_window_handle::{
use crate::event::{Event, EventStatus}; use crate::event::{Event, EventStatus};
use crate::window_open_options::WindowOpenOptions; use crate::window_open_options::WindowOpenOptions;
use crate::{MouseCursor, Size}; use crate::Size;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use crate::macos as platform; use crate::macos as platform;
@ -21,6 +21,12 @@ pub struct WindowHandle {
phantom: PhantomData<*mut ()>, 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 { impl WindowHandle {
fn new(window_handle: platform::WindowHandle) -> Self { fn new(window_handle: platform::WindowHandle) -> Self {
Self { window_handle, phantom: PhantomData::default() } Self { window_handle, phantom: PhantomData::default() }
@ -50,7 +56,10 @@ pub trait WindowHandler {
} }
pub struct Window<'a> { pub struct Window<'a> {
window: platform::Window<'a>, #[cfg(target_os = "windows")]
window: &'a mut platform::Window<'a>,
#[cfg(not(target_os = "windows"))]
window: &'a mut platform::Window,
// so that Window is !Send on all platforms // so that Window is !Send on all platforms
phantom: PhantomData<*mut ()>, phantom: PhantomData<*mut ()>,
@ -58,12 +67,12 @@ pub struct Window<'a> {
impl<'a> Window<'a> { impl<'a> Window<'a> {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub(crate) fn new(window: platform::Window<'a>) -> Window<'a> { pub(crate) fn new(window: &'a mut platform::Window<'a>) -> Window<'a> {
Window { window, phantom: PhantomData } Window { window, phantom: PhantomData }
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
pub(crate) fn new(window: platform::Window) -> Window { pub(crate) fn new(window: &mut platform::Window) -> Window {
Window { window, phantom: PhantomData } Window { window, phantom: PhantomData }
} }
@ -78,6 +87,16 @@ impl<'a> Window<'a> {
WindowHandle::new(window_handle) WindowHandle::new(window_handle)
} }
pub fn open_as_if_parented<H, B>(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::<H, B>(options, build);
WindowHandle::new(window_handle)
}
pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B) pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
where where
H: WindowHandler + 'static, H: WindowHandler + 'static,
@ -98,10 +117,6 @@ impl<'a> Window<'a> {
self.window.resize(size); 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 /// 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::Window::gl_context].
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
@ -121,3 +136,9 @@ unsafe impl<'a> HasRawDisplayHandle for Window<'a> {
self.window.raw_display_handle() self.window.raw_display_handle()
} }
} }
unsafe impl HasRawWindowHandle for RawWindowHandleWrapper {
fn raw_window_handle(&self) -> RawWindowHandle {
self.handle
}
}

View file

@ -1,4 +1,5 @@
use std::ffi::c_void; use std::marker::PhantomData;
use std::os::raw::{c_ulong, c_void};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::Arc; use std::sync::Arc;
@ -27,6 +28,9 @@ pub struct WindowHandle {
raw_window_handle: Option<RawWindowHandle>, raw_window_handle: Option<RawWindowHandle>,
close_requested: Arc<AtomicBool>, close_requested: Arc<AtomicBool>,
is_open: Arc<AtomicBool>, is_open: Arc<AtomicBool>,
// Ensure handle is !Send
_phantom: PhantomData<*mut ()>,
} }
impl WindowHandle { impl WindowHandle {
@ -71,6 +75,7 @@ impl ParentHandle {
raw_window_handle: None, raw_window_handle: None,
close_requested: Arc::clone(&close_requested), close_requested: Arc::clone(&close_requested),
is_open: Arc::clone(&is_open), is_open: Arc::clone(&is_open),
_phantom: PhantomData::default(),
}; };
(Self { close_requested, is_open }, handle) (Self { close_requested, is_open }, handle)
@ -87,11 +92,12 @@ impl Drop for ParentHandle {
} }
} }
struct WindowInner { pub struct Window {
xcb_connection: XcbConnection, xcb_connection: XcbConnection,
window_id: u32, window_id: u32,
window_info: WindowInfo, window_info: WindowInfo,
visual_id: u32, visual_id: u32,
// FIXME: There's all this mouse cursor logic but it's never actually used, is this correct?
mouse_cursor: MouseCursor, mouse_cursor: MouseCursor,
frame_interval: Duration, frame_interval: Duration,
@ -105,10 +111,6 @@ struct WindowInner {
gl_context: Option<GlContext>, gl_context: Option<GlContext>,
} }
pub struct Window<'a> {
inner: &'a mut WindowInner,
}
// Hack to allow sending a RawWindowHandle between threads. Do not make public // Hack to allow sending a RawWindowHandle between threads. Do not make public
struct SendableRwh(RawWindowHandle); struct SendableRwh(RawWindowHandle);
@ -116,7 +118,7 @@ unsafe impl Send for SendableRwh {}
type WindowOpenResult = Result<SendableRwh, ()>; type WindowOpenResult = Result<SendableRwh, ()>;
impl<'a> Window<'a> { impl Window {
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
where where
P: HasRawWindowHandle, P: HasRawWindowHandle,
@ -145,6 +147,26 @@ impl<'a> Window<'a> {
window_handle window_handle
} }
pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> WindowHandle
where
H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static,
{
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(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<H, B>(options: WindowOpenOptions, build: B) pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
where where
H: WindowHandler + 'static, H: WindowHandler + 'static,
@ -304,8 +326,6 @@ impl<'a> Window<'a> {
// compared to when raw-gl-context was a separate crate. // compared to when raw-gl-context was a separate crate.
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
let gl_context = fb_config.map(|fb_config| { let gl_context = fb_config.map(|fb_config| {
use std::ffi::c_ulong;
let window = window_id as c_ulong; let window = window_id as c_ulong;
let display = xcb_connection.conn.get_raw_dpy(); let display = xcb_connection.conn.get_raw_dpy();
@ -315,7 +335,7 @@ impl<'a> Window<'a> {
GlContext::new(context) GlContext::new(context)
}); });
let mut inner = WindowInner { let mut window = Self {
xcb_connection, xcb_connection,
window_id, window_id,
window_info, window_info,
@ -333,56 +353,57 @@ impl<'a> Window<'a> {
gl_context, gl_context,
}; };
let mut window = crate::Window::new(Window { inner: &mut inner }); let mut handler = build(&mut crate::Window::new(&mut window));
let mut handler = build(&mut window);
// Send an initial window resized event so the user is alerted of // Send an initial window resized event so the user is alerted of
// the correct dpi scaling. // the correct dpi scaling.
handler.on_event(&mut window, Event::Window(WindowEvent::Resized(window_info))); handler.on_event(
&mut crate::Window::new(&mut window),
Event::Window(WindowEvent::Resized(window_info)),
);
let _ = tx.send(Ok(SendableRwh(window.raw_window_handle()))); let _ = tx.send(Ok(SendableRwh(window.raw_window_handle())));
inner.run_event_loop(&mut handler); window.run_event_loop(&mut handler);
} }
pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) { pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
if self.inner.mouse_cursor == mouse_cursor { if self.mouse_cursor == mouse_cursor {
return; return;
} }
let xid = self.inner.xcb_connection.get_cursor_xid(mouse_cursor); let xid = self.xcb_connection.get_cursor_xid(mouse_cursor);
if xid != 0 { if xid != 0 {
xcb::change_window_attributes( xcb::change_window_attributes(
&self.inner.xcb_connection.conn, &self.xcb_connection.conn,
self.inner.window_id, self.window_id,
&[(xcb::CW_CURSOR, xid)], &[(xcb::CW_CURSOR, xid)],
); );
self.inner.xcb_connection.conn.flush(); self.xcb_connection.conn.flush();
} }
self.inner.mouse_cursor = mouse_cursor; self.mouse_cursor = mouse_cursor;
} }
pub fn close(&mut self) { pub fn close(&mut self) {
self.inner.close_requested = true; self.close_requested = true;
} }
pub fn resize(&mut self, size: Size) { pub fn resize(&mut self, size: Size) {
let scaling = self.inner.window_info.scale(); let scaling = self.window_info.scale();
let new_window_info = WindowInfo::from_logical_size(size, scaling); let new_window_info = WindowInfo::from_logical_size(size, scaling);
xcb::configure_window( xcb::configure_window(
&self.inner.xcb_connection.conn, &self.xcb_connection.conn,
self.inner.window_id, self.window_id,
&[ &[
(xcb::CONFIG_WINDOW_WIDTH as u16, new_window_info.physical_size().width), (xcb::CONFIG_WINDOW_WIDTH as u16, new_window_info.physical_size().width),
(xcb::CONFIG_WINDOW_HEIGHT as u16, new_window_info.physical_size().height), (xcb::CONFIG_WINDOW_HEIGHT as u16, new_window_info.physical_size().height),
], ],
); );
self.inner.xcb_connection.conn.flush(); self.xcb_connection.conn.flush();
// This will trigger a `ConfigureNotify` event which will in turn change `self.window_info` // This will trigger a `ConfigureNotify` event which will in turn change `self.window_info`
// and notify the window handler about it // and notify the window handler about it
@ -390,7 +411,7 @@ impl<'a> Window<'a> {
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
pub fn gl_context(&self) -> Option<&crate::gl::GlContext> { pub fn gl_context(&self) -> Option<&crate::gl::GlContext> {
self.inner.gl_context.as_ref() self.gl_context.as_ref()
} }
fn find_visual_for_depth(screen: &StructPtr<xcb_screen_t>, depth: u8) -> Option<u32> { fn find_visual_for_depth(screen: &StructPtr<xcb_screen_t>, depth: u8) -> Option<u32> {
@ -408,9 +429,7 @@ impl<'a> Window<'a> {
None None
} }
}
impl WindowInner {
#[inline] #[inline]
fn drain_xcb_events(&mut self, handler: &mut dyn WindowHandler) { 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 // the X server has a tendency to send spurious/extraneous configure notify events when a
@ -428,7 +447,7 @@ impl WindowInner {
let window_info = self.window_info; let window_info = self.window_info;
handler.on_event( handler.on_event(
&mut crate::Window::new(Window { inner: self }), &mut crate::Window::new(self),
Event::Window(WindowEvent::Resized(window_info)), Event::Window(WindowEvent::Resized(window_info)),
); );
} }
@ -458,7 +477,7 @@ impl WindowInner {
// if it's already time to draw a new frame. // if it's already time to draw a new frame.
let next_frame = last_frame + self.frame_interval; let next_frame = last_frame + self.frame_interval;
if Instant::now() >= next_frame { if Instant::now() >= next_frame {
handler.on_frame(&mut crate::Window::new(Window { inner: self })); handler.on_frame(&mut crate::Window::new(self));
last_frame = Instant::max(next_frame, Instant::now() - self.frame_interval); last_frame = Instant::max(next_frame, Instant::now() - self.frame_interval);
} }
@ -504,20 +523,14 @@ impl WindowInner {
} }
fn handle_close_requested(&mut self, handler: &mut dyn WindowHandler) { fn handle_close_requested(&mut self, handler: &mut dyn WindowHandler) {
handler.on_event( handler.on_event(&mut crate::Window::new(self), Event::Window(WindowEvent::WillClose));
&mut crate::Window::new(Window { inner: self }),
Event::Window(WindowEvent::WillClose),
);
// FIXME: handler should decide whether window stays open or not // FIXME: handler should decide whether window stays open or not
self.event_loop_running = false; self.event_loop_running = false;
} }
fn handle_must_close(&mut self, handler: &mut dyn WindowHandler) { fn handle_must_close(&mut self, handler: &mut dyn WindowHandler) {
handler.on_event( handler.on_event(&mut crate::Window::new(self), Event::Window(WindowEvent::WillClose));
&mut crate::Window::new(Window { inner: self }),
Event::Window(WindowEvent::WillClose),
);
self.event_loop_running = false; self.event_loop_running = false;
} }
@ -589,7 +602,7 @@ impl WindowInner {
let logical_pos = physical_pos.to_logical(&self.window_info); let logical_pos = physical_pos.to_logical(&self.window_info);
handler.on_event( handler.on_event(
&mut crate::Window::new(Window { inner: self }), &mut crate::Window::new(self),
Event::Mouse(MouseEvent::CursorMoved { Event::Mouse(MouseEvent::CursorMoved {
position: logical_pos, position: logical_pos,
modifiers: key_mods(event.state()), modifiers: key_mods(event.state()),
@ -600,7 +613,7 @@ impl WindowInner {
xcb::ENTER_NOTIFY => { xcb::ENTER_NOTIFY => {
handler.on_event( handler.on_event(
&mut crate::Window::new(Window { inner: self }), &mut crate::Window::new(self),
Event::Mouse(MouseEvent::CursorEntered), Event::Mouse(MouseEvent::CursorEntered),
); );
// since no `MOTION_NOTIFY` event is generated when `ENTER_NOTIFY` is generated, // since no `MOTION_NOTIFY` event is generated when `ENTER_NOTIFY` is generated,
@ -609,7 +622,7 @@ impl WindowInner {
let physical_pos = PhyPoint::new(event.event_x() as i32, event.event_y() as i32); 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); let logical_pos = physical_pos.to_logical(&self.window_info);
handler.on_event( handler.on_event(
&mut crate::Window::new(Window { inner: self }), &mut crate::Window::new(self),
Event::Mouse(MouseEvent::CursorMoved { Event::Mouse(MouseEvent::CursorMoved {
position: logical_pos, position: logical_pos,
modifiers: key_mods(event.state()), modifiers: key_mods(event.state()),
@ -618,10 +631,8 @@ impl WindowInner {
} }
xcb::LEAVE_NOTIFY => { xcb::LEAVE_NOTIFY => {
handler.on_event( handler
&mut crate::Window::new(Window { inner: self }), .on_event(&mut crate::Window::new(self), Event::Mouse(MouseEvent::CursorLeft));
Event::Mouse(MouseEvent::CursorLeft),
);
} }
xcb::BUTTON_PRESS => { xcb::BUTTON_PRESS => {
@ -631,7 +642,7 @@ impl WindowInner {
match detail { match detail {
4..=7 => { 4..=7 => {
handler.on_event( handler.on_event(
&mut crate::Window::new(Window { inner: self }), &mut crate::Window::new(self),
Event::Mouse(MouseEvent::WheelScrolled { Event::Mouse(MouseEvent::WheelScrolled {
delta: match detail { delta: match detail {
4 => ScrollDelta::Lines { x: 0.0, y: 1.0 }, 4 => ScrollDelta::Lines { x: 0.0, y: 1.0 },
@ -647,7 +658,7 @@ impl WindowInner {
detail => { detail => {
let button_id = mouse_id(detail); let button_id = mouse_id(detail);
handler.on_event( handler.on_event(
&mut crate::Window::new(Window { inner: self }), &mut crate::Window::new(self),
Event::Mouse(MouseEvent::ButtonPressed { Event::Mouse(MouseEvent::ButtonPressed {
button: button_id, button: button_id,
modifiers: key_mods(event.state()), modifiers: key_mods(event.state()),
@ -664,7 +675,7 @@ impl WindowInner {
if !(4..=7).contains(&detail) { if !(4..=7).contains(&detail) {
let button_id = mouse_id(detail); let button_id = mouse_id(detail);
handler.on_event( handler.on_event(
&mut crate::Window::new(Window { inner: self }), &mut crate::Window::new(self),
Event::Mouse(MouseEvent::ButtonReleased { Event::Mouse(MouseEvent::ButtonReleased {
button: button_id, button: button_id,
modifiers: key_mods(event.state()), modifiers: key_mods(event.state()),
@ -680,7 +691,7 @@ impl WindowInner {
let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) }; let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) };
handler.on_event( handler.on_event(
&mut crate::Window::new(Window { inner: self }), &mut crate::Window::new(self),
Event::Keyboard(convert_key_press_event(event)), Event::Keyboard(convert_key_press_event(event)),
); );
} }
@ -689,7 +700,7 @@ impl WindowInner {
let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) }; let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) };
handler.on_event( handler.on_event(
&mut crate::Window::new(Window { inner: self }), &mut crate::Window::new(self),
Event::Keyboard(convert_key_release_event(event)), Event::Keyboard(convert_key_release_event(event)),
); );
} }
@ -699,20 +710,20 @@ impl WindowInner {
} }
} }
unsafe impl<'a> HasRawWindowHandle for Window<'a> { unsafe impl HasRawWindowHandle for Window {
fn raw_window_handle(&self) -> RawWindowHandle { fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = XlibWindowHandle::empty(); let mut handle = XlibWindowHandle::empty();
handle.window = self.inner.window_id.into(); handle.window = self.window_id.into();
handle.visual_id = self.inner.visual_id.into(); handle.visual_id = self.visual_id.into();
RawWindowHandle::Xlib(handle) RawWindowHandle::Xlib(handle)
} }
} }
unsafe impl<'a> HasRawDisplayHandle for Window<'a> { unsafe impl HasRawDisplayHandle for Window {
fn raw_display_handle(&self) -> RawDisplayHandle { fn raw_display_handle(&self) -> RawDisplayHandle {
let display = self.inner.xcb_connection.conn.get_raw_dpy(); let display = self.xcb_connection.conn.get_raw_dpy();
let mut handle = XlibDisplayHandle::empty(); let mut handle = XlibDisplayHandle::empty();
handle.display = display as *mut c_void; handle.display = display as *mut c_void;
@ -733,6 +744,6 @@ fn mouse_id(id: u8) -> MouseButton {
} }
} }
pub fn copy_to_clipboard(_data: &str) { pub fn copy_to_clipboard(data: &str) {
todo!() todo!()
} }

View file

@ -19,6 +19,7 @@ pub struct XcbConnection {
pub(crate) atoms: Atoms, pub(crate) atoms: Atoms,
// FIXME: Same here, there's a ton of unused cursor machinery in here
pub(super) cursor_cache: HashMap<MouseCursor, u32>, pub(super) cursor_cache: HashMap<MouseCursor, u32>,
} }