This PR adds a simple example that allows to test and showcase the `Window::open_parented` method.
That example first creates a parent window using `Window::open_blocking`, and then creates a smaller child window using `Window::open_parented`.
Both window's handlers log all of their events to the console, in a similar fashion to the `open_window` example.
Both windows actually do rendering (unlike the `open_window` example for now): the parent fills its window with a grey backround, and the child fills its window with a red background.
This example also uses the `softbuffer` crate to perform the rendering, which allows testing it in a more portable manner and in the simplest use case possible, without having to involve OpenGL or any 3D rendering pipeline at all.
Instead of trying to detect when to clean up the window based on the `NSView`'s retain count, require window cleanup to be initiated explicitly via `Window::close`, `WindowHandle::close`, or `[NSWindowDelegate windowShouldClose:]` (in non-parented mode; called when the user clicks the "X" button). This fixes the leaks and use-after-frees that can be caused by the inherent unreliability of the retain count logic.
As discussed in #153, this change essentially means that the `NSView` created by Baseview will not be suitable as the top-level view for an Audio Unit, since the Baseview API now requires that child windows be cleaned up by an explicit call to `WindowHandle::close`, and the only reliable signal for cleaning up an Audio Unit view is a call to `[NSView dealloc]`. However, this does not mean that Baseview cannot be used in the context of an Audio Unit; it just means that plugin frameworks must implement a compatibility layer with a wrapper `NSView` (which is the approach taken by JUCE).
In order to implement this change:
- `WindowState` is stored in an `Rc` rather than a `Box`.
- `WindowHandle` holds an `Rc<WindowState>` so that `WindowHandle::close` can directly invoke window cleanup logic.
- Since the window can be closed during an event handler, `WindowState::from_view` now returns a clone of the `Rc<WindowState>` held by the `NSView` to ensure that it lives until the end of an event handler.
- In the non-parented case, the `NSView` is set as the window delegate, which allows it to receive the `windowShouldClose:` call when the user clicks the "X" button, upon which it will dispatch the `WillClose` event and initiate window cleanup logic.
- `Window::open_parented` and `open_blocking` no longer `release` the `NSView` immediately after attaching it. Instead, the `NSView` is released as part of the cleanup logic in `WindowInner::close`.
- `Window::resize` now checks if the window is open to avoid using the `NSView` after releasing it.
- The overridden `release` method, the `retain_count_after_build` field, the `ParentHandle` struct, and the `close_requested` flag have all been removed.
The public Window struct holds a mutable reference to `platform::Window`, which is a pattern that doesn't make sense for all backends. On Windows, the `platform::Window` struct itself just holds another (immutable) reference, and on macOS the `platform::Window` struct has to be wrapped in a `RefCell` so that a mutable reference to it can be formed.
Change the public `Window` struct to hold `platform::Window` directly, and change `platform::Window` in the macOS and X11 backends so that it simply holds a reference to another `WindowInner` struct similarly to the Windows backend. This allows us to remove the platform conditional in the declaration of the top-level `Window` struct. It also allows us to remove the `RefCell` wrapping `platform::Window` in the macOS backend and replace it with `Cell`s wrapping its individual fields.
The X11 backend has a full implementation of a `Window::set_mouse_cursor` method, but it isn't exposed via any public interface, which results in a lot of warnings for unused code. Add a public `set_mouse_cursor` method, along with stubbed-out versions in the Windows and macOS backends.
Forming mutable references to the WindowState is unsound given the possibility
of reentrant calls to NSView methods. Instead, form only immutable references
to the WindowState and wrap mutable fields in Cell and RefCell.
Follow-up work should use try_borrow_mut instead of borrow_mut to avoid
panicking in the case of reentrant calls.
RawWindowHandleWrapper is only necessary since GlContext::create takes an `impl
HasRawWindowHandle` argument, but GlContext::create is an internal unsafe API
and it is can just take a RawWindowHandle directly.
The open_as_if_parented method was intended to support the use case of Audio
Unit plugin views. However, the Audio Unit API differs in important ways from
other plugin APIs (such as VST 3 and CLAP) with regard to the lifetime
management of the plugin's NSView, and trying to support both lifetime
management patterns simultaneously has been the source of complexity and bugs.
Thus, rather than implementing Audio Unit NSView management directly in
Baseview, it will be the responsibility of plugin frameworks to implement a
compatibility layer between the Audio Unit API and the Baseview API.
We were leaking the DropTarget, so WindowState holds a reference to DropTarget again, so it can be dropped when the window is destroyed. Also, DropTarget now holds a Weak reference to WindowState and that's taken into account in callbacks.
- WindowState no longer holds a reference to DropTarget
- DropTarget is passed to RegisterDragDrop() with Rc::into_raw() instead of Rc::as_ptr() so it keeps the reference
- WindowState is created with Rc instead of Box so DropTarget can hold a Rc to it
Instead of working around the winapi bug by manually parsing the pointer we're (incorrectly) given, use the correct function signature and transmute the function pointer