Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

winit backend: remove the GLOBAL_PROXY #5471

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 3 additions & 87 deletions internal/backends/winit/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use crate::drag_resize_window::{handle_cursor_move_for_resize, handle_resize};
use crate::winitwindowadapter::WinitWindowAdapter;
use crate::SlintUserEvent;
use corelib::api::EventLoopError;
use corelib::graphics::euclid;
use corelib::input::{KeyEvent, KeyEventType, MouseEvent};
use corelib::items::{ColorScheme, PointerEventButton};
Expand All @@ -30,7 +29,6 @@ use winit::event_loop::ControlFlow;
use winit::window::ResizeDirection;
struct NotRunningEventLoop {
instance: winit::event_loop::EventLoop<SlintUserEvent>,
event_loop_proxy: winit::event_loop::EventLoopProxy<SlintUserEvent>,
}

impl NotRunningEventLoop {
Expand Down Expand Up @@ -58,8 +56,7 @@ impl NotRunningEventLoop {

let instance =
builder.build().map_err(|e| format!("Error initializing winit event loop: {e}"))?;
let event_loop_proxy = instance.create_proxy();
Ok(Self { instance, event_loop_proxy })
Ok(Self { instance })
}
}

Expand Down Expand Up @@ -130,53 +127,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: once_cell::sync::OnceCell<
std::sync::Mutex<GlobalEventLoopProxyOrEventQueue>,
> = once_cell::sync::OnceCell::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,
Expand Down Expand Up @@ -609,21 +559,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(not(target_arch = "wasm32"))]
Expand All @@ -633,11 +568,9 @@ 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 };
let nre = NotRunningEventLoop { instance: winit_loop };
MAYBE_LOOP_INSTANCE.with(|loop_instance| *loop_instance.borrow_mut() = Some(nre));

if let Some(error) = self.loop_error {
Expand Down Expand Up @@ -673,23 +606,14 @@ 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;

let result = winit_loop
.pump_app_events(timeout, &mut ActiveEventLoopSetterDuringEventProcessing(&mut self));

*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 };
let nre = NotRunningEventLoop { instance: winit_loop };
MAYBE_LOOP_INSTANCE.with(|loop_instance| *loop_instance.borrow_mut() = Some(nre));

if let Some(error) = self.loop_error {
Expand All @@ -709,14 +633,6 @@ pub fn spawn() -> Result<(), corelib::platform::PlatformError> {
})
.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())
});

let loop_state = EventLoopState::default();

not_running_loop_instance
Expand Down
67 changes: 26 additions & 41 deletions internal/backends/winit/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
extern crate alloc;

use event_loop::CustomEvent;
use i_slint_core::platform::EventLoopProxy;
use i_slint_core::api::EventLoopError;
use i_slint_core::platform::{EventLoopProxy, PlatformError};
use i_slint_core::window::WindowAdapter;
use renderer::WinitCompatibleRenderer;
use std::rc::Rc;
Expand All @@ -16,8 +17,6 @@ use std::rc::Rc;
mod clipboard;
mod drag_resize_window;
mod winitwindowadapter;

use i_slint_core::platform::PlatformError;
use winitwindowadapter::*;
pub(crate) mod event_loop;

Expand Down Expand Up @@ -226,37 +225,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 builder = WinitWindowAdapter::window_attributes(
Expand Down Expand Up @@ -315,21 +283,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")]
Expand Down
Loading