Add EventLoopBuilder
This commit adds an `EventLoopBuilder` struct to simplify event loop customization and providing options to it upon creation. It also deprecates the use of `EventLoop::with_user_event` in favor of the same method on new builder, and replaces old platforms specific extension traits with the new ones on the `EventLoopBuilder`.
This commit is contained in:
parent
0e52672f4a
commit
f3f6f1008a
19 changed files with 324 additions and 271 deletions
|
|
@ -71,6 +71,9 @@ pub struct EventLoop<T: 'static> {
|
|||
running: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
macro_rules! call_event_handler {
|
||||
( $event_handler:expr, $window_target:expr, $cf:expr, $event:expr ) => {{
|
||||
if let ControlFlow::ExitWithCode(code) = $cf {
|
||||
|
|
@ -82,7 +85,7 @@ macro_rules! call_event_handler {
|
|||
}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn new() -> Self {
|
||||
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Self {
|
||||
Self {
|
||||
window_target: event_loop::EventLoopWindowTarget {
|
||||
p: EventLoopWindowTarget {
|
||||
|
|
|
|||
|
|
@ -69,8 +69,11 @@ pub struct EventLoop<T: 'static> {
|
|||
window_target: RootEventLoopWindowTarget<T>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn new() -> EventLoop<T> {
|
||||
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> EventLoop<T> {
|
||||
static mut SINGLETON_INIT: bool = false;
|
||||
unsafe {
|
||||
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
|
||||
|
|
|
|||
|
|
@ -78,8 +78,10 @@ mod window;
|
|||
|
||||
use std::fmt;
|
||||
|
||||
pub use self::{
|
||||
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
|
||||
pub(crate) use self::{
|
||||
event_loop::{
|
||||
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
|
||||
},
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -49,6 +49,29 @@ pub mod x11;
|
|||
/// If this variable is set with any other value, winit will panic.
|
||||
const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND";
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
||||
pub(crate) enum Backend {
|
||||
#[cfg(feature = "x11")]
|
||||
X,
|
||||
#[cfg(feature = "wayland")]
|
||||
Wayland,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {
|
||||
pub(crate) forced_backend: Option<Backend>,
|
||||
pub(crate) any_thread: bool,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificEventLoopAttributes {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
forced_backend: None,
|
||||
any_thread: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
#[cfg(feature = "x11")]
|
||||
|
|
@ -568,13 +591,28 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
|
|||
}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn new() -> EventLoop<T> {
|
||||
assert_is_main_thread("new_any_thread");
|
||||
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self {
|
||||
if !attributes.any_thread && !is_main_thread() {
|
||||
panic!(
|
||||
"Initializing the event loop outside of the main thread is a significant \
|
||||
cross-platform compatibility hazard. If you absolutely need to create an \
|
||||
EventLoop on a different thread, you can use the \
|
||||
`EventLoopBuilderExtUnix::any_thread` function."
|
||||
);
|
||||
}
|
||||
|
||||
EventLoop::new_any_thread()
|
||||
}
|
||||
#[cfg(feature = "x11")]
|
||||
if attributes.forced_backend == Some(Backend::X) {
|
||||
// TODO: Propagate
|
||||
return EventLoop::new_x11_any_thread().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
if attributes.forced_backend == Some(Backend::Wayland) {
|
||||
// TODO: Propagate
|
||||
return EventLoop::new_wayland_any_thread().expect("failed to open Wayland connection");
|
||||
}
|
||||
|
||||
pub fn new_any_thread() -> EventLoop<T> {
|
||||
if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) {
|
||||
match env_var.as_str() {
|
||||
"x11" => {
|
||||
|
|
@ -623,26 +661,12 @@ impl<T: 'static> EventLoop<T> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
pub fn new_wayland() -> Result<EventLoop<T>, Box<dyn Error>> {
|
||||
assert_is_main_thread("new_wayland_any_thread");
|
||||
|
||||
EventLoop::new_wayland_any_thread()
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
pub fn new_wayland_any_thread() -> Result<EventLoop<T>, Box<dyn Error>> {
|
||||
fn new_wayland_any_thread() -> Result<EventLoop<T>, Box<dyn Error>> {
|
||||
wayland::EventLoop::new().map(EventLoop::Wayland)
|
||||
}
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
pub fn new_x11() -> Result<EventLoop<T>, XNotSupported> {
|
||||
assert_is_main_thread("new_x11_any_thread");
|
||||
|
||||
EventLoop::new_x11_any_thread()
|
||||
}
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
pub fn new_x11_any_thread() -> Result<EventLoop<T>, XNotSupported> {
|
||||
fn new_x11_any_thread() -> Result<EventLoop<T>, XNotSupported> {
|
||||
let xconn = match X11_BACKEND.lock().as_ref() {
|
||||
Ok(xconn) => xconn.clone(),
|
||||
Err(err) => return Err(err.clone()),
|
||||
|
|
@ -750,17 +774,6 @@ fn sticky_exit_callback<T, F>(
|
|||
}
|
||||
}
|
||||
|
||||
fn assert_is_main_thread(suggested_method: &str) {
|
||||
if !is_main_thread() {
|
||||
panic!(
|
||||
"Initializing the event loop outside of the main thread is a significant \
|
||||
cross-platform compatibility hazard. If you really, absolutely need to create an \
|
||||
EventLoop on a different thread, please use the `EventLoopExtUnix::{}` function.",
|
||||
suggested_method
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_main_thread() -> bool {
|
||||
use libc::{c_long, getpid, syscall, SYS_gettid};
|
||||
|
|
|
|||
|
|
@ -13,12 +13,8 @@ use std::{
|
|||
static AUX_DELEGATE_STATE_NAME: &str = "auxState";
|
||||
|
||||
pub struct AuxDelegateState {
|
||||
/// We store this value in order to be able to defer setting the activation policy until
|
||||
/// after the app has finished launching. If the activation policy is set earlier, the
|
||||
/// menubar is initially unresponsive on macOS 10.15 for example.
|
||||
pub activation_policy: ActivationPolicy,
|
||||
|
||||
pub create_default_menu: bool,
|
||||
pub default_menu: bool,
|
||||
}
|
||||
|
||||
pub struct AppDelegateClass(pub *const Class);
|
||||
|
|
@ -54,11 +50,12 @@ extern "C" fn new(class: &Class, _: Sel) -> id {
|
|||
unsafe {
|
||||
let this: id = msg_send![class, alloc];
|
||||
let this: id = msg_send![this, init];
|
||||
// TODO: Remove the need for this initialization here
|
||||
(*this).set_ivar(
|
||||
AUX_DELEGATE_STATE_NAME,
|
||||
Box::into_raw(Box::new(RefCell::new(AuxDelegateState {
|
||||
activation_policy: ActivationPolicy::Regular,
|
||||
create_default_menu: true,
|
||||
default_menu: true,
|
||||
}))) as *mut c_void,
|
||||
);
|
||||
this
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ impl AppState {
|
|||
};
|
||||
HANDLER.set_ready();
|
||||
HANDLER.waker().start();
|
||||
let create_default_menu = unsafe { get_aux_state_mut(app_delegate).create_default_menu };
|
||||
let create_default_menu = unsafe { get_aux_state_mut(app_delegate).default_menu };
|
||||
if create_default_menu {
|
||||
// The menubar initialization should be before the `NewEvents` event, to allow
|
||||
// overriding of the default menu even if it's created
|
||||
|
|
@ -486,7 +486,7 @@ fn apply_activation_policy(app_delegate: &Object) {
|
|||
let ns_app = NSApp();
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar won't be interactable.
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
let act_pol = get_aux_state_mut(app_delegate).activation_policy;
|
||||
ns_app.setActivationPolicy_(match act_pol {
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
|
||||
|
|
|
|||
|
|
@ -22,13 +22,17 @@ use crate::{
|
|||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
|
||||
monitor::MonitorHandle as RootMonitorHandle,
|
||||
platform_impl::platform::{
|
||||
app::APP_CLASS,
|
||||
app_delegate::APP_DELEGATE_CLASS,
|
||||
app_state::{AppState, Callback},
|
||||
monitor::{self, MonitorHandle},
|
||||
observer::*,
|
||||
util::IdRef,
|
||||
platform::macos::ActivationPolicy,
|
||||
platform_impl::{
|
||||
get_aux_state_mut,
|
||||
platform::{
|
||||
app::APP_CLASS,
|
||||
app_delegate::APP_DELEGATE_CLASS,
|
||||
app_state::{AppState, Callback},
|
||||
monitor::{self, MonitorHandle},
|
||||
observer::*,
|
||||
util::IdRef,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -100,7 +104,9 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
pub(crate) delegate: IdRef,
|
||||
/// The delegate is only weakly referenced by NSApplication, so we keep
|
||||
/// it around here as well.
|
||||
_delegate: IdRef,
|
||||
|
||||
window_target: Rc<RootWindowTarget<T>>,
|
||||
panic_info: Rc<PanicInfo>,
|
||||
|
|
@ -114,8 +120,23 @@ pub struct EventLoop<T: 'static> {
|
|||
_callback: Option<Rc<Callback<T>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {
|
||||
pub(crate) activation_policy: ActivationPolicy,
|
||||
pub(crate) default_menu: bool,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificEventLoopAttributes {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
activation_policy: Default::default(), // Regular
|
||||
default_menu: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
pub fn new() -> Self {
|
||||
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self {
|
||||
let delegate = unsafe {
|
||||
let is_main_thread: BOOL = msg_send!(class!(NSThread), isMainThread);
|
||||
if is_main_thread == NO {
|
||||
|
|
@ -129,15 +150,21 @@ impl<T> EventLoop<T> {
|
|||
let app: id = msg_send![APP_CLASS.0, sharedApplication];
|
||||
|
||||
let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
|
||||
|
||||
let mut aux_state = get_aux_state_mut(&**delegate);
|
||||
aux_state.activation_policy = attributes.activation_policy;
|
||||
aux_state.default_menu = attributes.default_menu;
|
||||
|
||||
autoreleasepool(|| {
|
||||
let _: () = msg_send![app, setDelegate:*delegate];
|
||||
});
|
||||
|
||||
delegate
|
||||
};
|
||||
let panic_info: Rc<PanicInfo> = Default::default();
|
||||
setup_control_flow_observers(Rc::downgrade(&panic_info));
|
||||
EventLoop {
|
||||
delegate,
|
||||
_delegate: delegate,
|
||||
window_target: Rc::new(RootWindowTarget {
|
||||
p: Default::default(),
|
||||
_marker: PhantomData,
|
||||
|
|
|
|||
|
|
@ -18,9 +18,12 @@ mod window_delegate;
|
|||
|
||||
use std::{fmt, ops::Deref, sync::Arc};
|
||||
|
||||
pub use self::{
|
||||
app_delegate::{get_aux_state_mut, AuxDelegateState},
|
||||
event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy},
|
||||
pub(crate) use self::{
|
||||
app_delegate::get_aux_state_mut,
|
||||
event_loop::{
|
||||
EventLoop, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
|
||||
Proxy as EventLoopProxy,
|
||||
},
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, UnownedWindow},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,8 +16,11 @@ pub struct EventLoop<T: 'static> {
|
|||
elw: root::EventLoopWindowTarget<T>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
pub fn new() -> Self {
|
||||
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Self {
|
||||
EventLoop {
|
||||
elw: root::EventLoopWindowTarget {
|
||||
p: WindowTarget::new(),
|
||||
|
|
|
|||
|
|
@ -28,8 +28,9 @@ mod backend;
|
|||
|
||||
pub use self::device::Id as DeviceId;
|
||||
pub use self::error::OsError;
|
||||
pub use self::event_loop::{
|
||||
EventLoop, Proxy as EventLoopProxy, WindowTarget as EventLoopWindowTarget,
|
||||
pub(crate) use self::event_loop::{
|
||||
EventLoop, PlatformSpecificEventLoopAttributes, Proxy as EventLoopProxy,
|
||||
WindowTarget as EventLoopWindowTarget,
|
||||
};
|
||||
pub use self::monitor::{Handle as MonitorHandle, Mode as VideoMode};
|
||||
pub use self::window::{
|
||||
|
|
|
|||
|
|
@ -113,48 +113,44 @@ pub struct EventLoop<T: 'static> {
|
|||
window_target: RootELW<T>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {
|
||||
pub(crate) any_thread: bool,
|
||||
pub(crate) dpi_aware: bool,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificEventLoopAttributes {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
any_thread: false,
|
||||
dpi_aware: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopWindowTarget<T: 'static> {
|
||||
thread_id: DWORD,
|
||||
thread_msg_target: HWND,
|
||||
pub(crate) runner_shared: EventLoopRunnerShared<T>,
|
||||
}
|
||||
|
||||
macro_rules! main_thread_check {
|
||||
($fn_name:literal) => {{
|
||||
let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
||||
if thread_id != main_thread_id() {
|
||||
panic!(concat!(
|
||||
"Initializing the event loop outside of the main thread is a significant \
|
||||
cross-platform compatibility hazard. If you really, absolutely need to create an \
|
||||
EventLoop on a different thread, please use the `EventLoopExtWindows::",
|
||||
$fn_name,
|
||||
"` function."
|
||||
));
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn new() -> EventLoop<T> {
|
||||
main_thread_check!("new_any_thread");
|
||||
|
||||
Self::new_any_thread()
|
||||
}
|
||||
|
||||
pub fn new_any_thread() -> EventLoop<T> {
|
||||
become_dpi_aware();
|
||||
Self::new_dpi_unaware_any_thread()
|
||||
}
|
||||
|
||||
pub fn new_dpi_unaware() -> EventLoop<T> {
|
||||
main_thread_check!("new_dpi_unaware_any_thread");
|
||||
|
||||
Self::new_dpi_unaware_any_thread()
|
||||
}
|
||||
|
||||
pub fn new_dpi_unaware_any_thread() -> EventLoop<T> {
|
||||
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self {
|
||||
let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
||||
|
||||
if !attributes.any_thread && thread_id != main_thread_id() {
|
||||
panic!(
|
||||
"Initializing the event loop outside of the main thread is a significant \
|
||||
cross-platform compatibility hazard. If you absolutely need to create an \
|
||||
EventLoop on a different thread, you can use the \
|
||||
`EventLoopBuilderExtWindows::any_thread` function."
|
||||
);
|
||||
}
|
||||
|
||||
if attributes.dpi_aware {
|
||||
become_dpi_aware();
|
||||
}
|
||||
|
||||
let thread_msg_target = create_event_target_window::<T>();
|
||||
|
||||
let send_thread_msg_target = thread_msg_target as usize;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
use winapi::{self, shared::windef::HMENU, shared::windef::HWND};
|
||||
|
||||
pub use self::{
|
||||
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
|
||||
pub(crate) use self::{
|
||||
event_loop::{
|
||||
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
|
||||
},
|
||||
icon::WinIcon,
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
window::Window,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue