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