Merge branch 'master' into feature/windows-cursor-enter-leave
This commit is contained in:
commit
d8cedc8a77
|
@ -1,3 +1,3 @@
|
|||
fn_args_layout = "Compressed"
|
||||
fn_params_layout = "Compressed"
|
||||
use_small_heuristics = "Max"
|
||||
use_field_init_shorthand = true
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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!")
|
||||
}
|
||||
_ => (),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 })
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
let size = NSSize::new(size.width.round(), size.height.round());
|
||||
if self.inner.open.get() {
|
||||
// NOTE: macOS gives you a personal rave if you pass in fractional pixels here. Even
|
||||
// though the size is in fractional pixels.
|
||||
let size = NSSize::new(size.width.round(), size.height.round());
|
||||
|
||||
unsafe { NSView::setFrameSize(self.ns_view, size) };
|
||||
unsafe {
|
||||
let _: () = msg_send![self.ns_view, setNeedsDisplay: YES];
|
||||
}
|
||||
unsafe { NSView::setFrameSize(self.inner.ns_view, size) };
|
||||
unsafe {
|
||||
let _: () = msg_send![self.inner.ns_view, setNeedsDisplay: YES];
|
||||
}
|
||||
|
||||
// When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because macOS.
|
||||
#[cfg(feature = "opengl")]
|
||||
if let Some(gl_context) = &self.gl_context {
|
||||
gl_context.resize(size);
|
||||
}
|
||||
// When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because
|
||||
// macOS.
|
||||
#[cfg(feature = "opengl")]
|
||||
if let Some(gl_context) = &self.inner.gl_context {
|
||||
gl_context.resize(size);
|
||||
}
|
||||
|
||||
// If this is a standalone window then we'll also need to resize the window itself
|
||||
if let Some(ns_window) = self.ns_window {
|
||||
unsafe { NSWindow::setContentSize_(ns_window, size) };
|
||||
// If this is a standalone window then we'll also need to resize the window itself
|
||||
if let Some(ns_window) = self.inner.ns_window.get() {
|
||||
unsafe { NSWindow::setContentSize_(ns_window, size) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[cfg(feature = "opengl")]
|
||||
pub fn gl_context(&self) -> Option<&GlContext> {
|
||||
self.gl_context.as_ref()
|
||||
self.inner.gl_context.as_ref()
|
||||
}
|
||||
|
||||
#[cfg(feature = "opengl")]
|
||||
fn create_gl_context(ns_window: Option<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;
|
||||
}
|
||||
|
||||
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 trigger_frame(&self) {
|
||||
let mut window = crate::Window::new(Window { inner: &self.window_inner });
|
||||
self.window_handler.borrow_mut().on_frame(&mut window);
|
||||
}
|
||||
|
||||
pub(super) fn process_native_key_event(&mut self, event: *mut Object) -> Option<KeyboardEvent> {
|
||||
pub(super) fn keyboard_state(&self) -> &KeyboardState {
|
||||
&self.keyboard_state
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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!()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!()
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue