1
0
Fork 0

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:
Billy Messenger 2021-11-16 00:00:22 -06:00 committed by GitHub
parent 2a894c6bc9
commit f6e99e9aa6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 423 additions and 65 deletions

View file

@ -33,4 +33,4 @@ objc = "0.2.7"
uuid = { version = "0.8", features = ["v4"] } uuid = { version = "0.8", features = ["v4"] }
[dev-dependencies] [dev-dependencies]
rtrb = "0.1.1" rtrb = "0.2"

View file

@ -38,7 +38,7 @@ fn main() {
scale: WindowScalePolicy::SystemScaleFactor, 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::spawn(move || loop {
::std::thread::sleep(Duration::from_secs(5)); ::std::thread::sleep(Duration::from_secs(5));

View file

@ -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; let retain_count_after_build = WindowState::from_field(this).retain_count_after_build;
if retain_count <= 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); this.set_ivar(BASEVIEW_STATE_IVAR, ::std::ptr::null() as *const c_void);

View file

@ -1,4 +1,7 @@
use std::ffi::c_void; use std::ffi::c_void;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use cocoa::appkit::{ use cocoa::appkit::{
NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSWindow, 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 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::keyboard::KeyboardState;
use super::view::{create_view, BASEVIEW_STATE_IVAR}; 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 { 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 /// Only set if we created the parent window, i.e. we are running in
/// parentless mode /// parentless mode
ns_window: Option<id>, ns_window: Option<id>,
/// Our subclassed NSView /// Our subclassed NSView
ns_view: id, ns_view: id,
close_requested: bool,
} }
impl 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 where
P: HasRawWindowHandle, P: HasRawWindowHandle,
H: WindowHandler + 'static, H: WindowHandler + 'static,
@ -46,9 +122,9 @@ impl Window {
let ns_view = unsafe { create_view(&options) }; 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 { unsafe {
let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view]; let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view];
@ -56,9 +132,11 @@ impl Window {
let () = msg_send![pool, drain]; 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 where
H: WindowHandler + 'static, H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H, B: FnOnce(&mut crate::Window) -> H,
@ -68,17 +146,15 @@ impl Window {
let ns_view = unsafe { create_view(&options) }; 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(); let window_handle = Self::init(true, window, build);
Self::init(window, build);
unsafe { unsafe {
let () = msg_send![pool, drain]; let () = msg_send![pool, drain];
} }
raw_window_handle window_handle
} }
pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B) pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
@ -118,7 +194,9 @@ impl Window {
let ns_window = unsafe { let ns_window = unsafe {
let ns_window = NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( let ns_window = NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_(
rect, rect,
NSWindowStyleMask::NSTitledWindowMask, NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSClosableWindowMask
| NSWindowStyleMask::NSMiniaturizableWindowMask,
NSBackingStoreBuffered, NSBackingStoreBuffered,
NO, NO,
); );
@ -134,21 +212,26 @@ impl Window {
let ns_view = unsafe { create_view(&options) }; 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 { unsafe {
ns_window.setContentView_(ns_view); 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]; let () = msg_send![pool, drain];
app.run(); app.run();
} }
} }
fn init<H, B>(mut window: Window, build: B) fn init<H, B>(parented: bool, mut window: Window, build: B) -> WindowHandle
where where
H: WindowHandler + 'static, H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H, 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 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 retain_count_after_build: usize = unsafe { msg_send![window.ns_view, retainCount] };
let window_state_ptr = Box::into_raw(Box::new(WindowState { let window_state_ptr = Box::into_raw(Box::new(WindowState {
@ -164,6 +250,7 @@ impl Window {
keyboard_state: KeyboardState::new(), keyboard_state: KeyboardState::new(),
frame_timer: None, frame_timer: None,
retain_count_after_build, retain_count_after_build,
_parent_handle: parent_handle,
})); }));
unsafe { unsafe {
@ -172,6 +259,12 @@ impl Window {
WindowState::setup_timer(window_state_ptr); 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>, window_handler: Box<dyn WindowHandler>,
keyboard_state: KeyboardState, keyboard_state: KeyboardState,
frame_timer: Option<CFRunLoopTimer>, frame_timer: Option<CFRunLoopTimer>,
_parent_handle: Option<ParentHandle>,
pub retain_count_after_build: usize, pub retain_count_after_build: usize,
} }
@ -201,6 +295,36 @@ impl WindowState {
pub(super) fn trigger_frame(&mut self) { pub(super) fn trigger_frame(&mut self) {
self.window_handler.on_frame(&mut crate::Window::new(&mut self.window)); 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> { pub(super) fn process_native_key_event(&mut self, event: *mut Object) -> Option<KeyboardEvent> {
@ -235,10 +359,17 @@ impl WindowState {
} }
/// Call when freeing view /// 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() { if let Some(frame_timer) = self.frame_timer.take() {
CFRunLoop::get_current().remove_timer(&frame_timer, kCFRunLoopDefaultMode); 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);
}
} }
} }

View file

@ -4,25 +4,29 @@ use winapi::shared::windef::{HWND, RECT};
use winapi::um::combaseapi::CoCreateGuid; use winapi::um::combaseapi::CoCreateGuid;
use winapi::um::winuser::{ use winapi::um::winuser::{
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW,
GetCapture, GetDpiForWindow, GetMessageW, GetWindowLongPtrW, IsWindow, LoadCursorW, GetDpiForWindow, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, RegisterClassW,
PostMessageW, RegisterClassW, ReleaseCapture, SetCapture, SetProcessDpiAwarenessContext, ReleaseCapture, SetCapture, SetProcessDpiAwarenessContext, SetTimer, SetWindowLongPtrW,
SetTimer, SetWindowLongPtrW, SetWindowPos, TranslateMessage, UnregisterClassW, CS_OWNDC, SetWindowPos, TranslateMessage, UnregisterClassW, CS_OWNDC, GET_XBUTTON_WPARAM, GWLP_USERDATA,
GET_XBUTTON_WPARAM, GWLP_USERDATA, IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, WHEEL_DELTA, IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE,
WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP,
WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY, WM_RBUTTONDOWN,
WM_NCDESTROY, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_RBUTTONUP, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER,
WM_SYSKEYUP, WM_TIMER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS,
WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, XBUTTON1, XBUTTON2,
XBUTTON1, XBUTTON2,
}; };
use std::cell::RefCell; 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::os::windows::ffi::OsStrExt;
use std::ptr::null_mut; use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use raw_window_handle::{windows::WindowsHandle, HasRawWindowHandle, RawWindowHandle}; use raw_window_handle::{windows::WindowsHandle, HasRawWindowHandle, RawWindowHandle};
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
use crate::{ use crate::{
Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, WindowEvent, WindowHandler, Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, WindowEvent, WindowHandler,
WindowInfo, WindowOpenOptions, WindowScalePolicy, WindowInfo, WindowOpenOptions, WindowScalePolicy,
@ -51,6 +55,65 @@ unsafe fn generate_guid() -> String {
const WIN_FRAME_TIMER: usize = 4242; 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( unsafe extern "system" fn wnd_proc(
hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM, hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM,
) -> LRESULT { ) -> LRESULT {
@ -247,7 +310,12 @@ unsafe extern "system" fn wnd_proc(
unregister_wnd_class(window_state.borrow().window_class); unregister_wnd_class(window_state.borrow().window_class);
SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); 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 { struct WindowState {
window_class: ATOM, window_class: ATOM,
window_info: WindowInfo, window_info: WindowInfo,
_parent_handle: Option<ParentHandle>,
keyboard_state: KeyboardState, keyboard_state: KeyboardState,
mouse_button_counter: usize, mouse_button_counter: usize,
handler: Box<dyn WindowHandler>, handler: Box<dyn WindowHandler>,
@ -295,7 +364,7 @@ pub struct Window {
} }
impl 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 where
P: HasRawWindowHandle, P: HasRawWindowHandle,
H: WindowHandler + 'static, H: WindowHandler + 'static,
@ -307,21 +376,20 @@ impl Window {
h => panic!("unsupported parent handle {:?}", h), 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 where
H: WindowHandler + 'static, H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H, B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static, B: Send + 'static,
{ {
let hwnd = Self::open(true, null_mut(), options, build); let (window_handle, _) = Self::open(true, null_mut(), options, build);
RawWindowHandle::Windows(WindowsHandle { window_handle
hwnd: hwnd as *mut std::ffi::c_void,
..WindowsHandle::empty()
})
} }
pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B) pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
@ -330,7 +398,7 @@ impl Window {
B: FnOnce(&mut crate::Window) -> H, B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static, B: Send + 'static,
{ {
let hwnd = Self::open(false, null_mut(), options, build); let (_, hwnd) = Self::open(false, null_mut(), options, build);
unsafe { unsafe {
let mut msg: MSG = std::mem::zeroed(); 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 where
H: WindowHandler + 'static, H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H, 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 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 { let mut window_state = Box::new(RefCell::new(WindowState {
window_class, window_class,
window_info, window_info,
_parent_handle: parent_handle,
keyboard_state: KeyboardState::new(), keyboard_state: KeyboardState::new(),
mouse_button_counter: 0, mouse_button_counter: 0,
handler, 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);
} }
} }
} }

View file

@ -12,6 +12,35 @@ use crate::win as platform;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use crate::x11 as platform; 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 { pub trait WindowHandler {
fn on_frame(&mut self, window: &mut Window); fn on_frame(&mut self, window: &mut Window);
fn on_event(&mut self, window: &mut Window, event: Event) -> EventStatus; fn on_event(&mut self, window: &mut Window, event: Event) -> EventStatus;
@ -28,23 +57,25 @@ impl<'a> Window<'a> {
Window { window, phantom: PhantomData } 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 where
P: HasRawWindowHandle, P: HasRawWindowHandle,
H: WindowHandler + 'static, H: WindowHandler + 'static,
B: FnOnce(&mut Window) -> H, B: FnOnce(&mut Window) -> H,
B: Send + 'static, 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 where
H: WindowHandler + 'static, H: WindowHandler + 'static,
B: FnOnce(&mut Window) -> H, B: FnOnce(&mut Window) -> H,
B: Send + 'static, 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) 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) 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> { unsafe impl<'a> HasRawWindowHandle for Window<'a> {

View file

@ -1,5 +1,8 @@
use std::marker::PhantomData;
use std::os::raw::{c_ulong, c_void}; use std::os::raw::{c_ulong, c_void};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::Arc;
use std::thread; use std::thread;
use std::time::*; use std::time::*;
@ -13,6 +16,74 @@ use crate::{
use super::keyboard::{convert_key_press_event, convert_key_release_event}; 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 { pub struct Window {
xcb_connection: XcbConnection, xcb_connection: XcbConnection,
window_id: u32, window_id: u32,
@ -21,8 +92,10 @@ pub struct Window {
frame_interval: Duration, frame_interval: Duration,
event_loop_running: bool, event_loop_running: bool,
close_requested: bool,
new_physical_size: Option<PhySize>, new_physical_size: Option<PhySize>,
parent_handle: Option<ParentHandle>,
} }
// Hack to allow sending a RawWindowHandle between threads. Do not make public // 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, ()>; type WindowOpenResult = Result<SendableRwh, ()>;
impl 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 where
P: HasRawWindowHandle, P: HasRawWindowHandle,
H: WindowHandler + 'static, H: WindowHandler + 'static,
@ -49,14 +122,19 @@ impl Window {
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1); let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
let (parent_handle, mut window_handle) = ParentHandle::new();
let thread = thread::spawn(move || { 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 where
H: WindowHandler + 'static, H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H, B: FnOnce(&mut crate::Window) -> H,
@ -64,11 +142,16 @@ impl Window {
{ {
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1); let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
let (parent_handle, mut window_handle) = ParentHandle::new();
let thread = thread::spawn(move || { 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) 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 (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
let thread = thread::spawn(move || { 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(); let _ = rx.recv().unwrap().unwrap();
@ -90,7 +173,7 @@ impl Window {
fn window_thread<H, B>( fn window_thread<H, B>(
parent: Option<u32>, options: WindowOpenOptions, build: B, parent: Option<u32>, options: WindowOpenOptions, build: B,
tx: mpsc::SyncSender<WindowOpenResult>, tx: mpsc::SyncSender<WindowOpenResult>, parent_handle: Option<ParentHandle>,
) where ) where
H: WindowHandler + 'static, H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H, B: FnOnce(&mut crate::Window) -> H,
@ -182,8 +265,10 @@ impl Window {
frame_interval: Duration::from_millis(15), frame_interval: Duration::from_millis(15),
event_loop_running: false, event_loop_running: false,
close_requested: false,
new_physical_size: None, new_physical_size: None,
parent_handle,
}; };
let mut handler = build(&mut crate::Window::new(&mut window)); let mut handler = build(&mut crate::Window::new(&mut window));
@ -200,10 +285,6 @@ impl Window {
window.run_event_loop(&mut handler); 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) { pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
if self.mouse_cursor == mouse_cursor { if self.mouse_cursor == mouse_cursor {
return; return;
@ -224,6 +305,10 @@ impl Window {
self.mouse_cursor = mouse_cursor; self.mouse_cursor = mouse_cursor;
} }
pub fn close(&mut self) {
self.close_requested = true;
}
#[inline] #[inline]
fn drain_xcb_events(&mut self, handler: &mut dyn WindowHandler) { fn drain_xcb_events(&mut self, handler: &mut dyn WindowHandler) {
// the X server has a tendency to send spurious/extraneous configure notify events when a // the X server has a tendency to send spurious/extraneous configure notify events when a
@ -291,9 +376,41 @@ impl Window {
self.drain_xcb_events(handler); 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) { fn handle_xcb_event(&mut self, handler: &mut dyn WindowHandler, event: xcb::GenericEvent) {
let event_type = event.response_type() & !0x80; let event_type = event.response_type() & !0x80;
@ -332,13 +449,7 @@ impl Window {
self.xcb_connection.atoms.wm_delete_window.unwrap_or(xcb::NONE); self.xcb_connection.atoms.wm_delete_window.unwrap_or(xcb::NONE);
if wm_delete_window == data32[0] { if wm_delete_window == data32[0] {
handler.on_event( self.handle_close_requested(handler);
&mut crate::Window::new(self),
Event::Window(WindowEvent::WillClose),
);
// FIXME: handler should decide whether window stays open or not
self.event_loop_running = false;
} }
} }