add ability to close window from user code, add HostWindowHandle (#103)
* add ability to close window from user code, add HostWindowHandle * fix manual close method for Mac, rename HostWindowHandle to ChildWindowHandle * fix rustfmt.toml and run cargo format * fix merge conflict mistake * fix more merge conflict mistakes * implement requested changes (with a non-broken commit this time) * implement requested changes * slight reordering of impls
This commit is contained in:
parent
2a894c6bc9
commit
f6e99e9aa6
|
@ -33,4 +33,4 @@ objc = "0.2.7"
|
|||
uuid = { version = "0.8", features = ["v4"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rtrb = "0.1.1"
|
||||
rtrb = "0.2"
|
||||
|
|
|
@ -38,7 +38,7 @@ fn main() {
|
|||
scale: WindowScalePolicy::SystemScaleFactor,
|
||||
};
|
||||
|
||||
let (mut tx, rx) = RingBuffer::new(128).split();
|
||||
let (mut tx, rx) = RingBuffer::new(128);
|
||||
|
||||
::std::thread::spawn(move || loop {
|
||||
::std::thread::sleep(Duration::from_secs(5));
|
||||
|
|
|
@ -178,7 +178,7 @@ extern "C" fn release(this: &mut Object, _sel: Sel) {
|
|||
let retain_count_after_build = WindowState::from_field(this).retain_count_after_build;
|
||||
|
||||
if retain_count <= retain_count_after_build {
|
||||
WindowState::from_field(this).remove_timer();
|
||||
WindowState::from_field(this).stop();
|
||||
|
||||
this.set_ivar(BASEVIEW_STATE_IVAR, ::std::ptr::null() as *const c_void);
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use std::ffi::c_void;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use cocoa::appkit::{
|
||||
NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSWindow,
|
||||
|
@ -15,21 +18,94 @@ use objc::{msg_send, runtime::Object, sel, sel_impl};
|
|||
|
||||
use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle};
|
||||
|
||||
use crate::{Event, EventStatus, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy};
|
||||
use crate::{
|
||||
Event, EventStatus, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
|
||||
WindowScalePolicy,
|
||||
};
|
||||
|
||||
use super::keyboard::KeyboardState;
|
||||
use super::view::{create_view, BASEVIEW_STATE_IVAR};
|
||||
|
||||
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 {
|
||||
pub fn close(&mut self) {
|
||||
if let Some(_) = self.raw_window_handle.take() {
|
||||
self.close_requested.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.is_open.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
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::MacOS(MacOSHandle { ..MacOSHandle::empty() })
|
||||
}
|
||||
}
|
||||
|
||||
struct ParentHandle {
|
||||
_close_requested: Arc<AtomicBool>,
|
||||
is_open: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl ParentHandle {
|
||||
pub fn new(raw_window_handle: RawWindowHandle) -> (Self, WindowHandle) {
|
||||
let close_requested = Arc::new(AtomicBool::new(false));
|
||||
let is_open = Arc::new(AtomicBool::new(true));
|
||||
|
||||
let handle = WindowHandle {
|
||||
raw_window_handle: Some(raw_window_handle),
|
||||
close_requested: Arc::clone(&close_requested),
|
||||
is_open: Arc::clone(&is_open),
|
||||
_phantom: PhantomData::default(),
|
||||
};
|
||||
|
||||
(Self { _close_requested: close_requested, is_open }, handle)
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn parent_did_drop(&self) -> bool {
|
||||
self.close_requested.load(Ordering::Relaxed)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl Drop for ParentHandle {
|
||||
fn drop(&mut self) {
|
||||
self.is_open.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
/// Only set if we created the parent window, i.e. we are running in
|
||||
/// parentless mode
|
||||
ns_app: Option<id>,
|
||||
/// Only set if we created the parent window, i.e. we are running in
|
||||
/// parentless mode
|
||||
ns_window: Option<id>,
|
||||
/// Our subclassed NSView
|
||||
ns_view: id,
|
||||
close_requested: bool,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B)
|
||||
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
|
||||
where
|
||||
P: HasRawWindowHandle,
|
||||
H: WindowHandler + 'static,
|
||||
|
@ -46,9 +122,9 @@ impl Window {
|
|||
|
||||
let ns_view = unsafe { create_view(&options) };
|
||||
|
||||
let window = Window { ns_window: None, ns_view };
|
||||
let window = Window { ns_app: None, ns_window: None, ns_view, close_requested: false };
|
||||
|
||||
Self::init(window, build);
|
||||
let window_handle = Self::init(true, window, build);
|
||||
|
||||
unsafe {
|
||||
let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view];
|
||||
|
@ -56,9 +132,11 @@ impl Window {
|
|||
|
||||
let () = msg_send![pool, drain];
|
||||
}
|
||||
|
||||
window_handle
|
||||
}
|
||||
|
||||
pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> RawWindowHandle
|
||||
pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> WindowHandle
|
||||
where
|
||||
H: WindowHandler + 'static,
|
||||
B: FnOnce(&mut crate::Window) -> H,
|
||||
|
@ -68,17 +146,15 @@ impl Window {
|
|||
|
||||
let ns_view = unsafe { create_view(&options) };
|
||||
|
||||
let window = Window { ns_window: None, ns_view };
|
||||
let window = Window { ns_app: None, ns_window: None, ns_view, close_requested: false };
|
||||
|
||||
let raw_window_handle = window.raw_window_handle();
|
||||
|
||||
Self::init(window, build);
|
||||
let window_handle = Self::init(true, window, build);
|
||||
|
||||
unsafe {
|
||||
let () = msg_send![pool, drain];
|
||||
}
|
||||
|
||||
raw_window_handle
|
||||
window_handle
|
||||
}
|
||||
|
||||
pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
|
||||
|
@ -118,7 +194,9 @@ impl Window {
|
|||
let ns_window = unsafe {
|
||||
let ns_window = NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_(
|
||||
rect,
|
||||
NSWindowStyleMask::NSTitledWindowMask,
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
| NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask,
|
||||
NSBackingStoreBuffered,
|
||||
NO,
|
||||
);
|
||||
|
@ -134,21 +212,26 @@ impl Window {
|
|||
|
||||
let ns_view = unsafe { create_view(&options) };
|
||||
|
||||
let window = Window { ns_window: Some(ns_window), ns_view };
|
||||
let window = Window {
|
||||
ns_app: Some(app),
|
||||
ns_window: Some(ns_window),
|
||||
ns_view,
|
||||
close_requested: false,
|
||||
};
|
||||
|
||||
Self::init(window, build);
|
||||
let _ = Self::init(false, window, build);
|
||||
|
||||
unsafe {
|
||||
ns_window.setContentView_(ns_view);
|
||||
let () = msg_send![ns_view as id, release];
|
||||
|
||||
let () = msg_send![ns_view as id, release];
|
||||
let () = msg_send![pool, drain];
|
||||
|
||||
app.run();
|
||||
}
|
||||
}
|
||||
|
||||
fn init<H, B>(mut window: Window, build: B)
|
||||
fn init<H, B>(parented: bool, mut window: Window, build: B) -> WindowHandle
|
||||
where
|
||||
H: WindowHandler + 'static,
|
||||
B: FnOnce(&mut crate::Window) -> H,
|
||||
|
@ -156,6 +239,9 @@ impl Window {
|
|||
{
|
||||
let window_handler = Box::new(build(&mut crate::Window::new(&mut window)));
|
||||
|
||||
let (parent_handle, window_handle) = ParentHandle::new(window.raw_window_handle());
|
||||
let parent_handle = if parented { Some(parent_handle) } else { None };
|
||||
|
||||
let retain_count_after_build: usize = unsafe { msg_send![window.ns_view, retainCount] };
|
||||
|
||||
let window_state_ptr = Box::into_raw(Box::new(WindowState {
|
||||
|
@ -164,6 +250,7 @@ impl Window {
|
|||
keyboard_state: KeyboardState::new(),
|
||||
frame_timer: None,
|
||||
retain_count_after_build,
|
||||
_parent_handle: parent_handle,
|
||||
}));
|
||||
|
||||
unsafe {
|
||||
|
@ -172,6 +259,12 @@ impl Window {
|
|||
|
||||
WindowState::setup_timer(window_state_ptr);
|
||||
}
|
||||
|
||||
window_handle
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.close_requested = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,6 +273,7 @@ pub(super) struct WindowState {
|
|||
window_handler: Box<dyn WindowHandler>,
|
||||
keyboard_state: KeyboardState,
|
||||
frame_timer: Option<CFRunLoopTimer>,
|
||||
_parent_handle: Option<ParentHandle>,
|
||||
pub retain_count_after_build: usize,
|
||||
}
|
||||
|
||||
|
@ -201,6 +295,36 @@ impl WindowState {
|
|||
|
||||
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 process_native_key_event(&mut self, event: *mut Object) -> Option<KeyboardEvent> {
|
||||
|
@ -235,10 +359,17 @@ impl WindowState {
|
|||
}
|
||||
|
||||
/// Call when freeing view
|
||||
pub(super) unsafe fn remove_timer(&mut self) {
|
||||
pub(super) unsafe fn stop(&mut self) {
|
||||
if let Some(frame_timer) = self.frame_timer.take() {
|
||||
CFRunLoop::get_current().remove_timer(&frame_timer, kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
||||
self.trigger_event(Event::Window(WindowEvent::WillClose));
|
||||
|
||||
// If in non-parented mode, we want to also quit the app altogether
|
||||
if let Some(app) = self.window.ns_app.take() {
|
||||
app.stop_(app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,25 +4,29 @@ use winapi::shared::windef::{HWND, RECT};
|
|||
use winapi::um::combaseapi::CoCreateGuid;
|
||||
use winapi::um::winuser::{
|
||||
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW,
|
||||
GetCapture, GetDpiForWindow, GetMessageW, GetWindowLongPtrW, IsWindow, LoadCursorW,
|
||||
PostMessageW, RegisterClassW, ReleaseCapture, SetCapture, SetProcessDpiAwarenessContext,
|
||||
SetTimer, SetWindowLongPtrW, SetWindowPos, TranslateMessage, UnregisterClassW, CS_OWNDC,
|
||||
GET_XBUTTON_WPARAM, GWLP_USERDATA, IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, WHEEL_DELTA,
|
||||
WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP,
|
||||
WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL,
|
||||
WM_NCDESTROY, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN,
|
||||
WM_SYSKEYUP, WM_TIMER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD,
|
||||
WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
|
||||
XBUTTON1, XBUTTON2,
|
||||
GetDpiForWindow, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, RegisterClassW,
|
||||
ReleaseCapture, SetCapture, SetProcessDpiAwarenessContext, SetTimer, SetWindowLongPtrW,
|
||||
SetWindowPos, TranslateMessage, UnregisterClassW, CS_OWNDC, GET_XBUTTON_WPARAM, GWLP_USERDATA,
|
||||
IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE,
|
||||
WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP,
|
||||
WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY, WM_RBUTTONDOWN,
|
||||
WM_RBUTTONUP, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER,
|
||||
WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS,
|
||||
WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, XBUTTON1, XBUTTON2,
|
||||
};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::{c_void, OsStr};
|
||||
use std::ffi::OsStr;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use raw_window_handle::{windows::WindowsHandle, HasRawWindowHandle, RawWindowHandle};
|
||||
|
||||
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
|
||||
|
||||
use crate::{
|
||||
Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, WindowEvent, WindowHandler,
|
||||
WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
||||
|
@ -51,6 +55,65 @@ unsafe fn generate_guid() -> String {
|
|||
|
||||
const WIN_FRAME_TIMER: usize = 4242;
|
||||
|
||||
pub struct WindowHandle {
|
||||
hwnd: Option<HWND>,
|
||||
is_open: Arc<AtomicBool>,
|
||||
|
||||
// Ensure handle is !Send
|
||||
_phantom: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl WindowHandle {
|
||||
pub fn close(&mut self) {
|
||||
if let Some(hwnd) = self.hwnd.take() {
|
||||
unsafe {
|
||||
PostMessageW(hwnd, BV_WINDOW_MUST_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.is_open.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HasRawWindowHandle for WindowHandle {
|
||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
if let Some(hwnd) = self.hwnd {
|
||||
RawWindowHandle::Windows(WindowsHandle {
|
||||
hwnd: hwnd as *mut std::ffi::c_void,
|
||||
..WindowsHandle::empty()
|
||||
})
|
||||
} else {
|
||||
RawWindowHandle::Windows(WindowsHandle { ..WindowsHandle::empty() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ParentHandle {
|
||||
is_open: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl ParentHandle {
|
||||
pub fn new(hwnd: HWND) -> (Self, WindowHandle) {
|
||||
let is_open = Arc::new(AtomicBool::new(true));
|
||||
|
||||
let handle = WindowHandle {
|
||||
hwnd: Some(hwnd),
|
||||
is_open: Arc::clone(&is_open),
|
||||
_phantom: PhantomData::default(),
|
||||
};
|
||||
|
||||
(Self { is_open }, handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ParentHandle {
|
||||
fn drop(&mut self) {
|
||||
self.is_open.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn wnd_proc(
|
||||
hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM,
|
||||
) -> LRESULT {
|
||||
|
@ -247,7 +310,12 @@ unsafe extern "system" fn wnd_proc(
|
|||
unregister_wnd_class(window_state.borrow().window_class);
|
||||
SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
if msg == BV_WINDOW_MUST_CLOSE {
|
||||
DestroyWindow(hwnd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,6 +351,7 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) {
|
|||
struct WindowState {
|
||||
window_class: ATOM,
|
||||
window_info: WindowInfo,
|
||||
_parent_handle: Option<ParentHandle>,
|
||||
keyboard_state: KeyboardState,
|
||||
mouse_button_counter: usize,
|
||||
handler: Box<dyn WindowHandler>,
|
||||
|
@ -295,7 +364,7 @@ pub struct Window {
|
|||
}
|
||||
|
||||
impl Window {
|
||||
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B)
|
||||
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
|
||||
where
|
||||
P: HasRawWindowHandle,
|
||||
H: WindowHandler + 'static,
|
||||
|
@ -307,21 +376,20 @@ impl Window {
|
|||
h => panic!("unsupported parent handle {:?}", h),
|
||||
};
|
||||
|
||||
Self::open(true, parent, options, build);
|
||||
let (window_handle, _) = Self::open(true, parent, options, build);
|
||||
|
||||
window_handle
|
||||
}
|
||||
|
||||
pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> RawWindowHandle
|
||||
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 hwnd = Self::open(true, null_mut(), options, build);
|
||||
let (window_handle, _) = Self::open(true, null_mut(), options, build);
|
||||
|
||||
RawWindowHandle::Windows(WindowsHandle {
|
||||
hwnd: hwnd as *mut std::ffi::c_void,
|
||||
..WindowsHandle::empty()
|
||||
})
|
||||
window_handle
|
||||
}
|
||||
|
||||
pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
|
||||
|
@ -330,7 +398,7 @@ impl Window {
|
|||
B: FnOnce(&mut crate::Window) -> H,
|
||||
B: Send + 'static,
|
||||
{
|
||||
let hwnd = Self::open(false, null_mut(), options, build);
|
||||
let (_, hwnd) = Self::open(false, null_mut(), options, build);
|
||||
|
||||
unsafe {
|
||||
let mut msg: MSG = std::mem::zeroed();
|
||||
|
@ -348,7 +416,9 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
fn open<H, B>(parented: bool, parent: HWND, options: WindowOpenOptions, build: B) -> HWND
|
||||
fn open<H, B>(
|
||||
parented: bool, parent: HWND, options: WindowOpenOptions, build: B,
|
||||
) -> (WindowHandle, HWND)
|
||||
where
|
||||
H: WindowHandler + 'static,
|
||||
B: FnOnce(&mut crate::Window) -> H,
|
||||
|
@ -410,9 +480,13 @@ impl Window {
|
|||
|
||||
let handler = Box::new(build(&mut crate::Window::new(&mut Window { hwnd })));
|
||||
|
||||
let (parent_handle, window_handle) = ParentHandle::new(hwnd);
|
||||
let parent_handle = if parented { Some(parent_handle) } else { None };
|
||||
|
||||
let mut window_state = Box::new(RefCell::new(WindowState {
|
||||
window_class,
|
||||
window_info,
|
||||
_parent_handle: parent_handle,
|
||||
keyboard_state: KeyboardState::new(),
|
||||
mouse_button_counter: 0,
|
||||
handler,
|
||||
|
@ -473,7 +547,13 @@ impl Window {
|
|||
);
|
||||
}
|
||||
|
||||
hwnd
|
||||
(window_handle, hwnd)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
unsafe {
|
||||
PostMessageW(self.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,35 @@ use crate::win as platform;
|
|||
#[cfg(target_os = "linux")]
|
||||
use crate::x11 as platform;
|
||||
|
||||
pub struct WindowHandle {
|
||||
window_handle: platform::WindowHandle,
|
||||
// so that WindowHandle is !Send on all platforms
|
||||
phantom: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl WindowHandle {
|
||||
fn new(window_handle: platform::WindowHandle) -> Self {
|
||||
Self { window_handle, phantom: PhantomData::default() }
|
||||
}
|
||||
|
||||
/// Close the window
|
||||
pub fn close(&mut self) {
|
||||
self.window_handle.close();
|
||||
}
|
||||
|
||||
/// Returns `true` if the window is still open, and returns `false`
|
||||
/// if the window was closed/dropped.
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.window_handle.is_open()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HasRawWindowHandle for WindowHandle {
|
||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
self.window_handle.raw_window_handle()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WindowHandler {
|
||||
fn on_frame(&mut self, window: &mut Window);
|
||||
fn on_event(&mut self, window: &mut Window, event: Event) -> EventStatus;
|
||||
|
@ -28,23 +57,25 @@ impl<'a> Window<'a> {
|
|||
Window { window, phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B)
|
||||
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
|
||||
where
|
||||
P: HasRawWindowHandle,
|
||||
H: WindowHandler + 'static,
|
||||
B: FnOnce(&mut Window) -> H,
|
||||
B: Send + 'static,
|
||||
{
|
||||
platform::Window::open_parented::<P, H, B>(parent, options, build)
|
||||
let window_handle = platform::Window::open_parented::<P, H, B>(parent, options, build);
|
||||
WindowHandle::new(window_handle)
|
||||
}
|
||||
|
||||
pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> RawWindowHandle
|
||||
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,
|
||||
{
|
||||
platform::Window::open_as_if_parented::<H, B>(options, build)
|
||||
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)
|
||||
|
@ -55,6 +86,11 @@ impl<'a> Window<'a> {
|
|||
{
|
||||
platform::Window::open_blocking::<H, B>(options, build)
|
||||
}
|
||||
|
||||
/// Close the window
|
||||
pub fn close(&mut self) {
|
||||
self.window.close();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> HasRawWindowHandle for Window<'a> {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::os::raw::{c_ulong, c_void};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::*;
|
||||
|
||||
|
@ -13,6 +16,74 @@ use crate::{
|
|||
|
||||
use super::keyboard::{convert_key_press_event, convert_key_release_event};
|
||||
|
||||
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 {
|
||||
pub fn close(&mut self) {
|
||||
if let Some(_) = self.raw_window_handle.take() {
|
||||
// FIXME: This will need to be changed from just setting an atomic to somehow
|
||||
// synchronizing with the window being closed (using a synchronous channel, or
|
||||
// by joining on the event loop thread).
|
||||
|
||||
self.close_requested.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.is_open.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl HasRawWindowHandle for WindowHandle {
|
||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||
if let Some(raw_window_handle) = self.raw_window_handle {
|
||||
if self.is_open.load(Ordering::Relaxed) {
|
||||
return raw_window_handle;
|
||||
}
|
||||
}
|
||||
|
||||
RawWindowHandle::Xlib(XlibHandle { ..raw_window_handle::unix::XlibHandle::empty() })
|
||||
}
|
||||
}
|
||||
|
||||
struct ParentHandle {
|
||||
close_requested: Arc<AtomicBool>,
|
||||
is_open: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl ParentHandle {
|
||||
pub fn new() -> (Self, WindowHandle) {
|
||||
let close_requested = Arc::new(AtomicBool::new(false));
|
||||
let is_open = Arc::new(AtomicBool::new(true));
|
||||
|
||||
let handle = WindowHandle {
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
xcb_connection: XcbConnection,
|
||||
window_id: u32,
|
||||
|
@ -21,8 +92,10 @@ pub struct Window {
|
|||
|
||||
frame_interval: Duration,
|
||||
event_loop_running: bool,
|
||||
close_requested: bool,
|
||||
|
||||
new_physical_size: Option<PhySize>,
|
||||
parent_handle: Option<ParentHandle>,
|
||||
}
|
||||
|
||||
// Hack to allow sending a RawWindowHandle between threads. Do not make public
|
||||
|
@ -33,7 +106,7 @@ unsafe impl Send for SendableRwh {}
|
|||
type WindowOpenResult = Result<SendableRwh, ()>;
|
||||
|
||||
impl Window {
|
||||
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B)
|
||||
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
|
||||
where
|
||||
P: HasRawWindowHandle,
|
||||
H: WindowHandler + 'static,
|
||||
|
@ -49,14 +122,19 @@ impl Window {
|
|||
|
||||
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
|
||||
|
||||
let (parent_handle, mut window_handle) = ParentHandle::new();
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
Self::window_thread(Some(parent_id), options, build, tx.clone());
|
||||
Self::window_thread(Some(parent_id), options, build, tx.clone(), Some(parent_handle));
|
||||
});
|
||||
|
||||
let _ = rx.recv().unwrap().unwrap();
|
||||
let raw_window_handle = rx.recv().unwrap().unwrap();
|
||||
window_handle.raw_window_handle = Some(raw_window_handle.0);
|
||||
|
||||
window_handle
|
||||
}
|
||||
|
||||
pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> RawWindowHandle
|
||||
pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> WindowHandle
|
||||
where
|
||||
H: WindowHandler + 'static,
|
||||
B: FnOnce(&mut crate::Window) -> H,
|
||||
|
@ -64,11 +142,16 @@ impl Window {
|
|||
{
|
||||
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
|
||||
|
||||
let (parent_handle, mut window_handle) = ParentHandle::new();
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
Self::window_thread(None, options, build, tx.clone());
|
||||
Self::window_thread(None, options, build, tx.clone(), Some(parent_handle));
|
||||
});
|
||||
|
||||
rx.recv().unwrap().unwrap().0
|
||||
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)
|
||||
|
@ -80,7 +163,7 @@ impl Window {
|
|||
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
Self::window_thread(None, options, build, tx.clone());
|
||||
Self::window_thread(None, options, build, tx.clone(), None);
|
||||
});
|
||||
|
||||
let _ = rx.recv().unwrap().unwrap();
|
||||
|
@ -90,7 +173,7 @@ impl Window {
|
|||
|
||||
fn window_thread<H, B>(
|
||||
parent: Option<u32>, options: WindowOpenOptions, build: B,
|
||||
tx: mpsc::SyncSender<WindowOpenResult>,
|
||||
tx: mpsc::SyncSender<WindowOpenResult>, parent_handle: Option<ParentHandle>,
|
||||
) where
|
||||
H: WindowHandler + 'static,
|
||||
B: FnOnce(&mut crate::Window) -> H,
|
||||
|
@ -182,8 +265,10 @@ impl Window {
|
|||
|
||||
frame_interval: Duration::from_millis(15),
|
||||
event_loop_running: false,
|
||||
close_requested: false,
|
||||
|
||||
new_physical_size: None,
|
||||
parent_handle,
|
||||
};
|
||||
|
||||
let mut handler = build(&mut crate::Window::new(&mut window));
|
||||
|
@ -200,10 +285,6 @@ impl Window {
|
|||
window.run_event_loop(&mut handler);
|
||||
}
|
||||
|
||||
pub fn window_info(&self) -> &WindowInfo {
|
||||
&self.window_info
|
||||
}
|
||||
|
||||
pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
|
||||
if self.mouse_cursor == mouse_cursor {
|
||||
return;
|
||||
|
@ -224,6 +305,10 @@ impl Window {
|
|||
self.mouse_cursor = mouse_cursor;
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.close_requested = true;
|
||||
}
|
||||
|
||||
#[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
|
||||
|
@ -291,9 +376,41 @@ impl Window {
|
|||
self.drain_xcb_events(handler);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the parents's handle was dropped (such as when the host
|
||||
// requested the window to close)
|
||||
//
|
||||
// FIXME: This will need to be changed from just setting an atomic to somehow
|
||||
// synchronizing with the window being closed (using a synchronous channel, or
|
||||
// by joining on the event loop thread).
|
||||
if let Some(parent_handle) = &self.parent_handle {
|
||||
if parent_handle.parent_did_drop() {
|
||||
self.handle_must_close(handler);
|
||||
self.close_requested = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the user has requested the window to close
|
||||
if self.close_requested {
|
||||
self.handle_must_close(handler);
|
||||
self.close_requested = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_close_requested(&mut self, handler: &mut dyn WindowHandler) {
|
||||
handler.on_event(&mut crate::Window::new(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));
|
||||
|
||||
self.event_loop_running = false;
|
||||
}
|
||||
|
||||
fn handle_xcb_event(&mut self, handler: &mut dyn WindowHandler, event: xcb::GenericEvent) {
|
||||
let event_type = event.response_type() & !0x80;
|
||||
|
||||
|
@ -332,13 +449,7 @@ impl Window {
|
|||
self.xcb_connection.atoms.wm_delete_window.unwrap_or(xcb::NONE);
|
||||
|
||||
if wm_delete_window == data32[0] {
|
||||
handler.on_event(
|
||||
&mut crate::Window::new(self),
|
||||
Event::Window(WindowEvent::WillClose),
|
||||
);
|
||||
|
||||
// FIXME: handler should decide whether window stays open or not
|
||||
self.event_loop_running = false;
|
||||
self.handle_close_requested(handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue