winit backend: remove the GLOBAL_PROXY

We have a proxy in the backend, use it
This commit is contained in:
Olivier Goffart 2024-06-24 13:31:24 +02:00 committed by Simon Hausmann
parent 42e65da8e2
commit d2fb69c182
3 changed files with 28 additions and 137 deletions

View file

@ -10,7 +10,6 @@
use crate::drag_resize_window::{handle_cursor_move_for_resize, handle_resize};
use crate::WinitWindowEventResult;
use crate::{SharedBackendData, SlintUserEvent};
use corelib::api::EventLoopError;
use corelib::graphics::euclid;
use corelib::input::{KeyEvent, KeyEventType, MouseEvent};
use corelib::items::{ColorScheme, PointerEventButton};
@ -32,7 +31,6 @@ pub(crate) struct NotRunningEventLoop {
#[cfg(not(target_arch = "wasm32"))]
pub(crate) clipboard: Rc<std::cell::RefCell<crate::clipboard::ClipboardPair>>,
pub(crate) instance: winit::event_loop::EventLoop<SlintUserEvent>,
event_loop_proxy: winit::event_loop::EventLoopProxy<SlintUserEvent>,
}
impl NotRunningEventLoop {
@ -72,7 +70,6 @@ impl NotRunningEventLoop {
let instance =
builder.build().map_err(|e| format!("Error initializing winit event loop: {e}"))?;
let event_loop_proxy = instance.create_proxy();
#[cfg(not(target_arch = "wasm32"))]
let clipboard = crate::clipboard::create_clipboard(
@ -83,7 +80,6 @@ impl NotRunningEventLoop {
Ok(Self {
instance,
event_loop_proxy,
#[cfg(not(target_family = "wasm"))]
clipboard: Rc::new(clipboard.into()),
})
@ -154,53 +150,6 @@ thread_local! {
scoped_tls_hkt::scoped_thread_local!(static CURRENT_WINDOW_TARGET : for<'a> &'a RunningEventLoop<'a>);
pub(crate) enum GlobalEventLoopProxyOrEventQueue {
Proxy(winit::event_loop::EventLoopProxy<SlintUserEvent>),
Queue(Vec<SlintUserEvent>),
}
impl GlobalEventLoopProxyOrEventQueue {
pub(crate) fn send_event(&mut self, event: SlintUserEvent) -> Result<(), EventLoopError> {
match self {
GlobalEventLoopProxyOrEventQueue::Proxy(proxy) => {
proxy.send_event(event).map_err(|_| EventLoopError::EventLoopTerminated)
}
GlobalEventLoopProxyOrEventQueue::Queue(queue) => {
queue.push(event);
Ok(())
}
}
}
fn set_proxy(&mut self, proxy: winit::event_loop::EventLoopProxy<SlintUserEvent>) {
match self {
GlobalEventLoopProxyOrEventQueue::Proxy(_) => {}
GlobalEventLoopProxyOrEventQueue::Queue(queue) => {
std::mem::take(queue)
.into_iter()
.for_each(|event| proxy.send_event(event).ok().unwrap());
*self = GlobalEventLoopProxyOrEventQueue::Proxy(proxy);
}
}
}
}
impl Default for GlobalEventLoopProxyOrEventQueue {
fn default() -> Self {
Self::Queue(Vec::new())
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) static GLOBAL_PROXY: std::sync::OnceLock<
std::sync::Mutex<GlobalEventLoopProxyOrEventQueue>,
> = std::sync::OnceLock::new();
#[cfg(target_arch = "wasm32")]
thread_local! {
pub(crate) static GLOBAL_PROXY: RefCell<Option<GlobalEventLoopProxyOrEventQueue>> = RefCell::new(None)
}
pub(crate) fn with_window_target<T>(
callback: impl FnOnce(
&dyn EventLoopInterface,
@ -715,21 +664,6 @@ impl EventLoopState {
})
.map_err(|e| format!("Error initializing winit event loop: {e}"))?;
let event_loop_proxy = not_running_loop_instance.event_loop_proxy;
#[cfg(not(target_arch = "wasm32"))]
GLOBAL_PROXY
.get_or_init(Default::default)
.lock()
.unwrap()
.set_proxy(event_loop_proxy.clone());
#[cfg(target_arch = "wasm32")]
GLOBAL_PROXY.with(|global_proxy| {
global_proxy
.borrow_mut()
.get_or_insert_with(Default::default)
.set_proxy(event_loop_proxy.clone())
});
let mut winit_loop = not_running_loop_instance.instance;
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "ios")))]
@ -739,13 +673,10 @@ impl EventLoopState {
.run_app_on_demand(&mut ActiveEventLoopSetterDuringEventProcessing(&mut self))
.map_err(|e| format!("Error running winit event loop: {e}"))?;
*GLOBAL_PROXY.get_or_init(Default::default).lock().unwrap() = Default::default();
// Keep the EventLoop instance alive and re-use it in future invocations of run_event_loop().
// Winit does not support creating multiple instances of the event loop.
let nre = NotRunningEventLoop {
instance: winit_loop,
event_loop_proxy,
clipboard: not_running_loop_instance.clipboard,
};
MAYBE_LOOP_INSTANCE.with(|loop_instance| *loop_instance.borrow_mut() = Some(nre));
@ -783,13 +714,6 @@ impl EventLoopState {
})
.map_err(|e| format!("Error initializing winit event loop: {e}"))?;
let event_loop_proxy = not_running_loop_instance.event_loop_proxy;
GLOBAL_PROXY
.get_or_init(Default::default)
.lock()
.unwrap()
.set_proxy(event_loop_proxy.clone());
let mut winit_loop = not_running_loop_instance.instance;
self.pumping_events_instantly = timeout.is_some_and(|duration| duration.is_zero());
@ -799,13 +723,10 @@ impl EventLoopState {
self.pumping_events_instantly = false;
*GLOBAL_PROXY.get_or_init(Default::default).lock().unwrap() = Default::default();
// Keep the EventLoop instance alive and re-use it in future invocations of run_event_loop().
// Winit does not support creating multiple instances of the event loop.
let nre = NotRunningEventLoop {
instance: winit_loop,
event_loop_proxy,
clipboard: not_running_loop_instance.clipboard,
};
MAYBE_LOOP_INSTANCE.with(|loop_instance| *loop_instance.borrow_mut() = Some(nre));
@ -826,14 +747,6 @@ impl EventLoopState {
})
.map_err(|e| format!("Error initializing winit event loop: {e}"))?;
let event_loop_proxy = not_running_loop_instance.event_loop_proxy;
GLOBAL_PROXY.with(|global_proxy| {
global_proxy
.borrow_mut()
.get_or_insert_with(Default::default)
.set_proxy(event_loop_proxy.clone())
});
not_running_loop_instance
.instance
.spawn_app(ActiveEventLoopSetterDuringEventProcessing(self));

View file

@ -8,8 +8,9 @@
extern crate alloc;
use event_loop::{CustomEvent, EventLoopState};
use i_slint_core::api::EventLoopError;
use i_slint_core::graphics::{RequestedGraphicsAPI, RequestedOpenGLVersion};
use i_slint_core::platform::EventLoopProxy;
use i_slint_core::platform::{EventLoopProxy, PlatformError};
use i_slint_core::window::WindowAdapter;
use renderer::WinitCompatibleRenderer;
use std::cell::RefCell;
@ -21,8 +22,6 @@ use std::rc::Weak;
mod clipboard;
mod drag_resize_window;
mod winitwindowadapter;
use i_slint_core::platform::PlatformError;
use winitwindowadapter::*;
pub(crate) mod event_loop;
@ -454,37 +453,6 @@ impl Backend {
}
}
fn send_event_via_global_event_loop_proxy(
event: SlintUserEvent,
) -> Result<(), i_slint_core::api::EventLoopError> {
#[cfg(not(target_arch = "wasm32"))]
crate::event_loop::GLOBAL_PROXY
.get_or_init(Default::default)
.lock()
.unwrap()
.send_event(event)?;
#[cfg(target_arch = "wasm32")]
{
crate::event_loop::GLOBAL_PROXY.with(|global_proxy| {
let mut maybe_proxy = global_proxy.borrow_mut();
let proxy = maybe_proxy.get_or_insert_with(Default::default);
// Calling send_event is usually done by winit at the bottom of the stack,
// in event handlers, and thus winit might decide to process the event
// immediately within that stack.
// To prevent re-entrancy issues that might happen by getting the application
// event processed on top of the current stack, set winit in Poll mode so that
// events are queued and process on top of a clean stack during a requested animation
// frame a few moments later.
// This also allows batching multiple post_event calls and redraw their state changes
// all at once.
proxy.send_event(SlintUserEvent(CustomEvent::WakeEventLoopWorkaround))?;
proxy.send_event(event)?;
Ok(())
})?
}
Ok(())
}
impl i_slint_core::platform::Platform for Backend {
fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, PlatformError> {
let mut attrs = WinitWindowAdapter::window_attributes()?;
@ -561,21 +529,38 @@ impl i_slint_core::platform::Platform for Backend {
}
fn new_event_loop_proxy(&self) -> Option<Box<dyn EventLoopProxy>> {
struct Proxy;
struct Proxy(winit::event_loop::EventLoopProxy<SlintUserEvent>);
impl EventLoopProxy for Proxy {
fn quit_event_loop(&self) -> Result<(), i_slint_core::api::EventLoopError> {
send_event_via_global_event_loop_proxy(SlintUserEvent(CustomEvent::Exit))
fn quit_event_loop(&self) -> Result<(), EventLoopError> {
self.0
.send_event(SlintUserEvent(CustomEvent::Exit))
.map_err(|_| EventLoopError::EventLoopTerminated)
}
fn invoke_from_event_loop(
&self,
event: Box<dyn FnOnce() + Send>,
) -> Result<(), i_slint_core::api::EventLoopError> {
let e = SlintUserEvent(CustomEvent::UserEvent(event));
send_event_via_global_event_loop_proxy(e)
) -> Result<(), EventLoopError> {
// Calling send_event is usually done by winit at the bottom of the stack,
// in event handlers, and thus winit might decide to process the event
// immediately within that stack.
// To prevent re-entrancy issues that might happen by getting the application
// event processed on top of the current stack, set winit in Poll mode so that
// events are queued and process on top of a clean stack during a requested animation
// frame a few moments later.
// This also allows batching multiple post_event calls and redraw their state changes
// all at once.
#[cfg(target_arch = "wasm32")]
self.0
.send_event(SlintUserEvent(CustomEvent::WakeEventLoopWorkaround))
.map_err(|_| EventLoopError::EventLoopTerminated)?;
self.0
.send_event(SlintUserEvent(CustomEvent::UserEvent(event)))
.map_err(|_| EventLoopError::EventLoopTerminated)
}
}
Some(Box::new(Proxy))
Some(Box::new(Proxy(self.proxy.clone())))
}
#[cfg(target_arch = "wasm32")]

View file

@ -278,15 +278,8 @@ impl WasmInputHelper {
) {
let closure = move |arg: Arg| {
closure(arg);
crate::event_loop::GLOBAL_PROXY.with(|global_proxy| {
if let Ok(mut x) = global_proxy.try_borrow_mut() {
if let Some(proxy) = &mut *x {
let _ = proxy.send_event(crate::SlintUserEvent(
crate::event_loop::CustomEvent::WakeEventLoopWorkaround,
));
}
}
});
// wake up event loop
i_slint_core::api::invoke_from_event_loop(|| {}).ok();
};
let closure = Closure::wrap(Box::new(closure) as Box<dyn Fn(_)>);
self.input