1
0
Fork 0

Merge branch 'master' into feature/windows-cursor-enter-leave

This commit is contained in:
Fredemus 2024-01-09 01:38:14 +01:00
commit d8cedc8a77
17 changed files with 573 additions and 536 deletions

View file

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

View file

@ -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"] }

View file

@ -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!")
}
_ => (),

View file

@ -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<GlContext, GlError> {
let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() {
pub unsafe fn create(parent: &RawWindowHandle, config: GlConfig) -> Result<GlContext, GlError> {
let handle = if let RawWindowHandle::AppKit(handle) = parent {
handle
} else {
return Err(GlError::InvalidWindowHandle);

View file

@ -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<GlContext, GlError> {
platform::GlContext::create(parent, config)
.map(|context| GlContext { context, phantom: PhantomData })

View file

@ -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<GlContext, GlError> {
let handle = if let RawWindowHandle::Win32(handle) = parent.raw_window_handle() {
pub unsafe fn create(parent: &RawWindowHandle, config: GlConfig) -> Result<GlContext, GlError> {
let handle = if let RawWindowHandle::Win32(handle) = parent {
handle
} else {
return Err(GlError::InvalidWindowHandle);

View file

@ -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<GlContext, GlError> {
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 })
})
}

View file

@ -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<NSEventModifierFlags>,
}
/// 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<KeyboardEvent> {
pub(crate) fn last_mods(&self) -> NSEventModifierFlags {
self.last_mods.get()
}
pub(crate) fn process_native_event(&self, event: id) -> Option<KeyboardEvent> {
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

View file

@ -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::*;

View file

@ -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.
extern "C" fn window_should_close(this: &Object, _: Sel, _sender: id) -> BOOL {
let state = unsafe { WindowState::from_view(this) };
unsafe {
let retain_count: usize = msg_send![this, retainCount];
state.trigger_event(Event::Window(WindowEvent::WillClose));
let state_ptr: *mut c_void = *this.get_ivar(BASEVIEW_STATE_IVAR);
state.window_inner.close();
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];
}
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);
}

View file

@ -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,93 +30,99 @@ 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<RawWindowHandle>,
close_requested: Arc<AtomicBool>,
is_open: Arc<AtomicBool>,
// Ensure handle is !Send
_phantom: PhantomData<*mut ()>,
state: Rc<WindowState>,
}
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())
self.state.window_inner.raw_window_handle()
}
}
struct ParentHandle {
_close_requested: Arc<AtomicBool>,
is_open: Arc<AtomicBool>,
}
pub(super) struct WindowInner {
open: Cell<bool>,
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>,
ns_app: Cell<Option<id>>,
/// Only set if we created the parent window, i.e. we are running in
/// parentless mode
ns_window: Option<id>,
ns_window: Cell<Option<id>>,
/// Our subclassed NSView
ns_view: id,
close_requested: bool,
#[cfg(feature = "opengl")]
gl_context: Option<GlContext>,
}
impl Window {
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())
}
}
pub struct Window<'a> {
inner: &'a WindowInner,
}
impl<'a> Window<'a> {
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
where
P: HasRawWindowHandle,
@ -139,11 +147,11 @@ impl 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
@ -151,11 +159,10 @@ 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![ns_view as id, release];
let () = msg_send![pool, drain];
}
@ -163,44 +170,6 @@ impl Window {
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)
where
H: WindowHandler + 'static,
@ -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<H, B>(
parented: bool, mut window: Window, window_info: WindowInfo, build: B,
) -> WindowHandle
fn init<H, B>(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.
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 { NSView::setFrameSize(self.inner.ns_view, size) };
unsafe {
let _: () = msg_send![self.ns_view, setNeedsDisplay: YES];
let _: () = msg_send![self.inner.ns_view, setNeedsDisplay: YES];
}
// When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because macOS.
// When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because
// macOS.
#[cfg(feature = "opengl")]
if let Some(gl_context) = &self.gl_context {
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 {
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<id>, 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<dyn WindowHandler>,
pub(super) window_inner: WindowInner,
window_handler: RefCell<Box<dyn WindowHandler>>,
keyboard_state: KeyboardState,
frame_timer: Option<CFRunLoopTimer>,
_parent_handle: Option<ParentHandle>,
pub retain_count_after_build: usize,
frame_timer: Cell<Option<CFRunLoopTimer>>,
/// The last known window info for this window.
pub window_info: WindowInfo,
pub window_info: Cell<WindowInfo>,
}
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<WindowState>` rather than just a `&WindowState`, since the
/// original `Rc<WindowState>` owned by the `NSView` can be dropped at any time
/// (including during an event handler).
pub(super) unsafe fn from_view(view: &Object) -> Rc<WindowState> {
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);
state
}
pub(super) fn trigger_event(&mut self, event: Event) -> EventStatus {
self.window_handler.on_event(&mut crate::Window::new(&mut self.window), event)
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)
}
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;
}
}
*/
// 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<KeyboardEvent> {
pub(super) fn process_native_key_event(&self, event: *mut Object) -> Option<KeyboardEvent> {
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<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);
}
(*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;
self.inner.raw_window_handle()
}
}
let mut handle = AppKitHandle::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> {
fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty())
}
}

View file

@ -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 {
@ -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);
@ -133,7 +150,12 @@ impl DropTarget {
let buffer_size = characters as usize + 1;
let mut buffer = Vec::<u16>::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,13 +165,11 @@ 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;
@ -178,20 +198,18 @@ 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);
@ -206,19 +224,17 @@ impl DropTarget {
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);
@ -238,20 +254,18 @@ impl DropTarget {
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);

View file

@ -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<HWND>,
is_open: Rc<Cell<bool>>,
// 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<LRESULT> {
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<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)
where
H: WindowHandler + 'static,
@ -691,9 +665,9 @@ impl Window<'_> {
#[cfg(feature = "opengl")]
let gl_context: Option<GlContext> = 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!()
}

View file

@ -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<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)
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()
}
}

View file

@ -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<RawWindowHandle>,
close_requested: Arc<AtomicBool>,
is_open: Arc<AtomicBool>,
// 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<GlContext>,
}
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<SendableRwh, ()>;
impl Window {
impl<'a> Window<'a> {
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
where
P: HasRawWindowHandle,
@ -146,26 +145,6 @@ impl Window {
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)
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<xcb_screen_t>, depth: u8) -> Option<u32> {
@ -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::<xcb::EnterNotifyEvent>(&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::<xcb::ButtonPressEvent>(&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::<xcb::KeyPressEvent>(&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::<xcb::KeyReleaseEvent>(&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!()
}

View file

@ -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<MouseCursor, u32>,
}