Refactor event loop <-> instance communication in multi_window

This commit is contained in:
Héctor Ramón Jiménez 2023-11-29 23:55:17 +01:00
parent 3b39ba7029
commit d34bc4e4a2
No known key found for this signature in database
GPG key ID: 7CC46565708259A7

View file

@ -8,7 +8,7 @@ use crate::conversion;
use crate::core::widget::operation; use crate::core::widget::operation;
use crate::core::{self, mouse, renderer, window, Size}; use crate::core::{self, mouse, renderer, window, Size};
use crate::futures::futures::channel::mpsc; use crate::futures::futures::channel::mpsc;
use crate::futures::futures::{task, Future, FutureExt, StreamExt}; use crate::futures::futures::{task, Future, StreamExt};
use crate::futures::{Executor, Runtime, Subscription}; use crate::futures::{Executor, Runtime, Subscription};
use crate::graphics::{compositor, Compositor}; use crate::graphics::{compositor, Compositor};
use crate::multi_window::windows::Windows; use crate::multi_window::windows::Windows;
@ -21,23 +21,24 @@ use crate::{Clipboard, Error, Proxy, Settings};
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::time::Instant; use std::time::Instant;
use winit::monitor::MonitorHandle;
#[derive(Debug)] enum Event<Message: 'static> {
enum Event<Message> {
Application(Message),
NewWindow {
id: window::Id,
settings: window::Settings,
title: String,
monitor: Option<MonitorHandle>,
},
CloseWindow(window::Id),
WindowCreated { WindowCreated {
id: window::Id, id: window::Id,
window: winit::window::Window, window: winit::window::Window,
exit_on_close_request: bool, exit_on_close_request: bool,
}, },
EventLoopAwakened(winit::event::Event<'static, Message>),
}
enum Control {
ChangeFlow(winit::event_loop::ControlFlow),
CreateWindow {
id: window::Id,
settings: window::Settings,
title: String,
monitor: Option<winit::monitor::MonitorHandle>,
},
} }
/// An interactive, native, cross-platform, multi-windowed application. /// An interactive, native, cross-platform, multi-windowed application.
@ -243,13 +244,31 @@ where
event: winit::event::WindowEvent::Resized(*new_inner_size), event: winit::event::WindowEvent::Resized(*new_inner_size),
window_id, window_id,
}), }),
winit::event::Event::UserEvent(Event::NewWindow { _ => event.to_static(),
};
if let Some(event) = event {
event_sender
.start_send(Event::EventLoopAwakened(event))
.expect("Send event");
loop {
let poll = instance.as_mut().poll(&mut context);
match poll {
task::Poll::Pending => match control_receiver.try_next() {
Ok(Some(control)) => match control {
Control::ChangeFlow(flow) => {
*control_flow = flow;
}
Control::CreateWindow {
id, id,
settings, settings,
title, title,
monitor, monitor,
}) => { } => {
let exit_on_close_request = settings.exit_on_close_request; let exit_on_close_request =
settings.exit_on_close_request;
let window = conversion::window_settings( let window = conversion::window_settings(
settings, &title, monitor, None, settings, &title, monitor, None,
@ -257,44 +276,37 @@ where
.build(window_target) .build(window_target)
.expect("Failed to build window"); .expect("Failed to build window");
Some(winit::event::Event::UserEvent(Event::WindowCreated { event_sender
.start_send(Event::WindowCreated {
id, id,
window, window,
exit_on_close_request, exit_on_close_request,
})) })
} .expect("Send event");
_ => event.to_static(),
};
if let Some(event) = event {
event_sender.start_send(event).expect("Send event");
let poll = instance.as_mut().poll(&mut context);
match poll {
task::Poll::Pending => {
if let Ok(Some(flow)) = control_receiver.try_next() {
*control_flow = flow;
} }
},
_ => {
break;
} }
},
task::Poll::Ready(_) => { task::Poll::Ready(_) => {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
break;
} }
}; };
} }
}
}) })
} }
async fn run_instance<A, E, C>( async fn run_instance<A, E, C>(
mut application: A, mut application: A,
mut compositor: C, mut compositor: C,
mut runtime: Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
mut proxy: winit::event_loop::EventLoopProxy<Event<A::Message>>, mut proxy: winit::event_loop::EventLoopProxy<A::Message>,
mut debug: Debug, mut debug: Debug,
mut event_receiver: mpsc::UnboundedReceiver< mut event_receiver: mpsc::UnboundedReceiver<Event<A::Message>>,
winit::event::Event<'_, Event<A::Message>>, mut control_sender: mpsc::UnboundedSender<Control>,
>,
mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,
init_command: Command<A::Message>, init_command: Command<A::Message>,
mut windows: Windows<A, C>, mut windows: Windows<A, C>,
should_main_window_be_visible: bool, should_main_window_be_visible: bool,
@ -327,18 +339,14 @@ async fn run_instance<A, E, C>(
init_command, init_command,
&mut runtime, &mut runtime,
&mut clipboard, &mut clipboard,
&mut control_sender,
&mut proxy, &mut proxy,
&mut debug, &mut debug,
&mut windows, &mut windows,
&mut ui_caches, &mut ui_caches,
); );
runtime.track( runtime.track(application.subscription().into_recipes());
application
.subscription()
.map(Event::Application)
.into_recipes(),
);
let mut mouse_interaction = mouse::Interaction::default(); let mut mouse_interaction = mouse::Interaction::default();
@ -360,6 +368,46 @@ async fn run_instance<A, E, C>(
debug.startup_finished(); debug.startup_finished();
'main: while let Some(event) = event_receiver.next().await { 'main: while let Some(event) = event_receiver.next().await {
match event {
Event::WindowCreated {
id,
window,
exit_on_close_request,
} => {
let bounds = logical_bounds_of(&window);
let (inner_size, i) = windows.add(
&application,
&mut compositor,
id,
window,
exit_on_close_request,
);
user_interfaces.push(build_user_interface(
&application,
user_interface::Cache::default(),
&mut windows.renderers[i],
inner_size,
&mut debug,
id,
));
ui_caches.push(user_interface::Cache::default());
if let Some(bounds) = bounds {
events.push((
Some(id),
core::Event::Window(
id,
window::Event::Created {
position: bounds.0,
size: bounds.1,
},
),
));
}
}
Event::EventLoopAwakened(event) => {
match event { match event {
event::Event::NewEvents(start_cause) => { event::Event::NewEvents(start_cause) => {
redraw_pending = matches!( redraw_pending = matches!(
@ -377,7 +425,9 @@ async fn run_instance<A, E, C>(
let mut window_events = vec![]; let mut window_events = vec![];
events.retain(|(window_id, event)| { events.retain(|(window_id, event)| {
if *window_id == Some(*id) || window_id.is_none() { if *window_id == Some(*id)
|| window_id.is_none()
{
window_events.push(event.clone()); window_events.push(event.clone());
false false
} else { } else {
@ -392,7 +442,8 @@ async fn run_instance<A, E, C>(
continue; continue;
} }
let (ui_state, statuses) = user_interfaces[i].update( let (ui_state, statuses) = user_interfaces[i]
.update(
&window_events, &window_events,
windows.states[i].cursor(), windows.states[i].cursor(),
&mut windows.renderers[i], &mut windows.renderers[i],
@ -401,12 +452,15 @@ async fn run_instance<A, E, C>(
); );
if !uis_stale { if !uis_stale {
uis_stale = uis_stale = matches!(
matches!(ui_state, user_interface::State::Outdated); ui_state,
user_interface::State::Outdated
);
} }
for (event, status) in for (event, status) in window_events
window_events.into_iter().zip(statuses.into_iter()) .into_iter()
.zip(statuses.into_iter())
{ {
runtime.broadcast(event, status); runtime.broadcast(event, status);
} }
@ -416,8 +470,9 @@ async fn run_instance<A, E, C>(
// TODO mw application update returns which window IDs to update // TODO mw application update returns which window IDs to update
if !messages.is_empty() || uis_stale { if !messages.is_empty() || uis_stale {
let mut cached_interfaces: Vec<user_interface::Cache> = let mut cached_interfaces: Vec<
ManuallyDrop::into_inner(user_interfaces) user_interface::Cache,
> = ManuallyDrop::into_inner(user_interfaces)
.drain(..) .drain(..)
.map(UserInterface::into_cache) .map(UserInterface::into_cache)
.collect(); .collect();
@ -428,6 +483,7 @@ async fn run_instance<A, E, C>(
&mut compositor, &mut compositor,
&mut runtime, &mut runtime,
&mut clipboard, &mut clipboard,
&mut control_sender,
&mut proxy, &mut proxy,
&mut debug, &mut debug,
&mut messages, &mut messages,
@ -446,7 +502,8 @@ async fn run_instance<A, E, C>(
} }
// rebuild UIs with the synchronized states // rebuild UIs with the synchronized states
user_interfaces = ManuallyDrop::new(build_user_interfaces( user_interfaces =
ManuallyDrop::new(build_user_interfaces(
&application, &application,
&mut debug, &mut debug,
&mut windows, &mut windows,
@ -509,7 +566,8 @@ async fn run_instance<A, E, C>(
core::event::Status::Ignored, core::event::Status::Ignored,
); );
let _ = control_sender.start_send(match ui_state { let _ = control_sender.start_send(
Control::ChangeFlow(match ui_state {
user_interface::State::Updated { user_interface::State::Updated {
redraw_request: Some(redraw_request), redraw_request: Some(redraw_request),
} => match redraw_request { } => match redraw_request {
@ -521,16 +579,19 @@ async fn run_instance<A, E, C>(
} }
}, },
_ => ControlFlow::Wait, _ => ControlFlow::Wait,
}); }),
);
} }
redraw_pending = false; redraw_pending = false;
debug.draw_finished(); debug.draw_finished();
} }
event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( event::Event::PlatformSpecific(
event::PlatformSpecific::MacOS(
event::MacOS::ReceivedUrl(url), event::MacOS::ReceivedUrl(url),
)) => { ),
) => {
use crate::core::event; use crate::core::event;
events.push(( events.push((
@ -542,71 +603,23 @@ async fn run_instance<A, E, C>(
), ),
)); ));
} }
event::Event::UserEvent(event) => match event { event::Event::UserEvent(message) => {
Event::Application(message) => {
messages.push(message); messages.push(message);
} }
Event::WindowCreated {
id,
window,
exit_on_close_request,
} => {
let bounds = logical_bounds_of(&window);
let (inner_size, i) = windows.add(
&application,
&mut compositor,
id,
window,
exit_on_close_request,
);
user_interfaces.push(build_user_interface(
&application,
user_interface::Cache::default(),
&mut windows.renderers[i],
inner_size,
&mut debug,
id,
));
ui_caches.push(user_interface::Cache::default());
if let Some(bounds) = bounds {
events.push((
Some(id),
core::Event::Window(
id,
window::Event::Created {
position: bounds.0,
size: bounds.1,
},
),
));
}
}
Event::CloseWindow(id) => {
let i = windows.delete(id);
let _ = user_interfaces.remove(i);
let _ = ui_caches.remove(i);
if windows.is_empty() {
break 'main;
}
}
Event::NewWindow { .. } => unreachable!(),
},
event::Event::RedrawRequested(id) => { event::Event::RedrawRequested(id) => {
let i = windows.index_from_raw(id); let i = windows.index_from_raw(id);
let state = &windows.states[i]; let state = &windows.states[i];
let physical_size = state.physical_size(); let physical_size = state.physical_size();
if physical_size.width == 0 || physical_size.height == 0 { if physical_size.width == 0 || physical_size.height == 0
{
continue; continue;
} }
debug.render_started(); debug.render_started();
let current_viewport_version = state.viewport_version(); let current_viewport_version = state.viewport_version();
let window_viewport_version = windows.viewport_versions[i]; let window_viewport_version =
windows.viewport_versions[i];
if window_viewport_version != current_viewport_version { if window_viewport_version != current_viewport_version {
let logical_size = state.logical_size(); let logical_size = state.logical_size();
@ -622,7 +635,8 @@ async fn run_instance<A, E, C>(
debug.layout_finished(); debug.layout_finished();
debug.draw_started(); debug.draw_started();
let new_mouse_interaction = user_interfaces[i].draw( let new_mouse_interaction = user_interfaces[i]
.draw(
renderer, renderer,
state.theme(), state.theme(),
&renderer::Style { &renderer::Style {
@ -648,7 +662,8 @@ async fn run_instance<A, E, C>(
physical_size.height, physical_size.height,
); );
windows.viewport_versions[i] = current_viewport_version; windows.viewport_versions[i] =
current_viewport_version;
} }
match compositor.present( match compositor.present(
@ -687,8 +702,10 @@ async fn run_instance<A, E, C>(
event: window_event, event: window_event,
window_id, window_id,
} => { } => {
let window_index = let window_index = windows
windows.raw.iter().position(|w| w.id() == window_id); .raw
.iter()
.position(|w| w.id() == window_id);
match window_index { match window_index {
Some(i) => { Some(i) => {
@ -711,14 +728,20 @@ async fn run_instance<A, E, C>(
} }
} else { } else {
let state = &mut windows.states[i]; let state = &mut windows.states[i];
state.update(raw, &window_event, &mut debug); state.update(
raw,
&window_event,
&mut debug,
);
if let Some(event) = conversion::window_event( if let Some(event) =
conversion::window_event(
id, id,
&window_event, &window_event,
state.scale_factor(), state.scale_factor(),
state.modifiers(), state.modifiers(),
) { )
{
events.push((Some(id), event)); events.push((Some(id), event));
} }
} }
@ -732,7 +755,8 @@ async fn run_instance<A, E, C>(
window_event, window_event,
winit::event::WindowEvent::Destroyed winit::event::WindowEvent::Destroyed
) { ) {
let id = windows.get_pending_destroy(window_id); let id =
windows.get_pending_destroy(window_id);
events.push(( events.push((
None, None,
@ -748,6 +772,8 @@ async fn run_instance<A, E, C>(
_ => {} _ => {}
} }
} }
}
}
let _ = ManuallyDrop::into_inner(user_interfaces); let _ = ManuallyDrop::into_inner(user_interfaces);
} }
@ -780,9 +806,10 @@ where
fn update<A: Application, C, E: Executor>( fn update<A: Application, C, E: Executor>(
application: &mut A, application: &mut A,
compositor: &mut C, compositor: &mut C,
runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
clipboard: &mut Clipboard, clipboard: &mut Clipboard,
proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>, control_sender: &mut mpsc::UnboundedSender<Control>,
proxy: &mut winit::event_loop::EventLoopProxy<A::Message>,
debug: &mut Debug, debug: &mut Debug,
messages: &mut Vec<A::Message>, messages: &mut Vec<A::Message>,
windows: &mut Windows<A, C>, windows: &mut Windows<A, C>,
@ -804,6 +831,7 @@ fn update<A: Application, C, E: Executor>(
command, command,
runtime, runtime,
clipboard, clipboard,
control_sender,
proxy, proxy,
debug, debug,
windows, windows,
@ -811,7 +839,7 @@ fn update<A: Application, C, E: Executor>(
); );
} }
let subscription = application.subscription().map(Event::Application); let subscription = application.subscription();
runtime.track(subscription.into_recipes()); runtime.track(subscription.into_recipes());
} }
@ -820,9 +848,10 @@ fn run_command<A, C, E>(
application: &A, application: &A,
compositor: &mut C, compositor: &mut C,
command: Command<A::Message>, command: Command<A::Message>,
runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
clipboard: &mut Clipboard, clipboard: &mut Clipboard,
proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>, control_sender: &mut mpsc::UnboundedSender<Control>,
proxy: &mut winit::event_loop::EventLoopProxy<A::Message>,
debug: &mut Debug, debug: &mut Debug,
windows: &mut Windows<A, C>, windows: &mut Windows<A, C>,
ui_caches: &mut Vec<user_interface::Cache>, ui_caches: &mut Vec<user_interface::Cache>,
@ -839,17 +868,17 @@ fn run_command<A, C, E>(
for action in command.actions() { for action in command.actions() {
match action { match action {
command::Action::Future(future) => { command::Action::Future(future) => {
runtime.spawn(Box::pin(future.map(Event::Application))); runtime.spawn(Box::pin(future));
} }
command::Action::Stream(stream) => { command::Action::Stream(stream) => {
runtime.run(Box::pin(stream.map(Event::Application))); runtime.run(Box::pin(stream));
} }
command::Action::Clipboard(action) => match action { command::Action::Clipboard(action) => match action {
clipboard::Action::Read(tag) => { clipboard::Action::Read(tag) => {
let message = tag(clipboard.read()); let message = tag(clipboard.read());
proxy proxy
.send_event(Event::Application(message)) .send_event(message)
.expect("Send message to event loop"); .expect("Send message to event loop");
} }
clipboard::Action::Write(contents) => { clipboard::Action::Write(contents) => {
@ -860,19 +889,28 @@ fn run_command<A, C, E>(
window::Action::Spawn { settings } => { window::Action::Spawn { settings } => {
let monitor = windows.last_monitor(); let monitor = windows.last_monitor();
proxy control_sender
.send_event(Event::NewWindow { .start_send(Control::CreateWindow {
id, id,
settings, settings,
title: application.title(id), title: application.title(id),
monitor, monitor,
}) })
.expect("Send message to event loop"); .expect("Send control action");
} }
window::Action::Close => { window::Action::Close => {
proxy use winit::event_loop::ControlFlow;
.send_event(Event::CloseWindow(id))
.expect("Send message to event loop"); let i = windows.delete(id);
let _ = ui_caches.remove(i);
if windows.is_empty() {
control_sender
.start_send(Control::ChangeFlow(
ControlFlow::ExitWithCode(0),
))
.expect("Send control action");
}
} }
window::Action::Drag => { window::Action::Drag => {
let _ = windows.with_raw(id).drag_window(); let _ = windows.with_raw(id).drag_window();
@ -890,10 +928,10 @@ fn run_command<A, C, E>(
let size = window.inner_size(); let size = window.inner_size();
proxy proxy
.send_event(Event::Application(callback(Size::new( .send_event(callback(Size::new(
size.width, size.width,
size.height, size.height,
)))) )))
.expect("Send message to event loop"); .expect("Send message to event loop");
} }
window::Action::Maximize(maximized) => { window::Action::Maximize(maximized) => {
@ -929,7 +967,7 @@ fn run_command<A, C, E>(
}; };
proxy proxy
.send_event(Event::Application(tag(mode))) .send_event(tag(mode))
.expect("Event loop doesn't exist."); .expect("Event loop doesn't exist.");
} }
window::Action::ToggleMaximize => { window::Action::ToggleMaximize => {
@ -955,10 +993,7 @@ fn run_command<A, C, E>(
} }
window::Action::FetchId(tag) => { window::Action::FetchId(tag) => {
proxy proxy
.send_event(Event::Application(tag(windows .send_event(tag(windows.with_raw(id).id().into()))
.with_raw(id)
.id()
.into())))
.expect("Event loop doesn't exist."); .expect("Event loop doesn't exist.");
} }
window::Action::Screenshot(tag) => { window::Action::Screenshot(tag) => {
@ -976,11 +1011,9 @@ fn run_command<A, C, E>(
); );
proxy proxy
.send_event(Event::Application(tag( .send_event(tag(window::Screenshot::new(
window::Screenshot::new(
bytes, bytes,
state.physical_size(), state.physical_size(),
),
))) )))
.expect("Event loop doesn't exist."); .expect("Event loop doesn't exist.");
} }
@ -999,7 +1032,7 @@ fn run_command<A, C, E>(
let message = _tag(information); let message = _tag(information);
proxy proxy
.send_event(Event::Application(message)) .send_event(message)
.expect("Event loop doesn't exist."); .expect("Event loop doesn't exist.");
}); });
} }
@ -1025,7 +1058,7 @@ fn run_command<A, C, E>(
operation::Outcome::None => {} operation::Outcome::None => {}
operation::Outcome::Some(message) => { operation::Outcome::Some(message) => {
proxy proxy
.send_event(Event::Application(message)) .send_event(message)
.expect("Event loop doesn't exist."); .expect("Event loop doesn't exist.");
// operation completed, don't need to try to operate on rest of UIs // operation completed, don't need to try to operate on rest of UIs
@ -1051,7 +1084,7 @@ fn run_command<A, C, E>(
} }
proxy proxy
.send_event(Event::Application(tagger(Ok(())))) .send_event(tagger(Ok(())))
.expect("Send message to event loop"); .expect("Send message to event loop");
} }
} }