Rename iced_sentinel to iced_beacon and refactor its API

This commit is contained in:
Héctor Ramón Jiménez 2024-05-10 20:08:09 +02:00
parent aaf396256e
commit 57033dc4d0
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
19 changed files with 596 additions and 438 deletions

View file

@ -106,7 +106,7 @@ members = [
"highlighter", "highlighter",
"renderer", "renderer",
"runtime", "runtime",
"sentinel", "beacon",
"tiny_skia", "tiny_skia",
"wgpu", "wgpu",
"widget", "widget",
@ -126,6 +126,7 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]
[workspace.dependencies] [workspace.dependencies]
iced = { version = "0.13.0-dev", path = "." } iced = { version = "0.13.0-dev", path = "." }
iced_beacon = { version = "0.13.0-dev", path = "beacon" }
iced_core = { version = "0.13.0-dev", path = "core" } iced_core = { version = "0.13.0-dev", path = "core" }
iced_debug = { version = "0.13.0-dev", path = "debug" } iced_debug = { version = "0.13.0-dev", path = "debug" }
iced_futures = { version = "0.13.0-dev", path = "futures" } iced_futures = { version = "0.13.0-dev", path = "futures" }
@ -133,7 +134,6 @@ iced_graphics = { version = "0.13.0-dev", path = "graphics" }
iced_highlighter = { version = "0.13.0-dev", path = "highlighter" } iced_highlighter = { version = "0.13.0-dev", path = "highlighter" }
iced_renderer = { version = "0.13.0-dev", path = "renderer" } iced_renderer = { version = "0.13.0-dev", path = "renderer" }
iced_runtime = { version = "0.13.0-dev", path = "runtime" } iced_runtime = { version = "0.13.0-dev", path = "runtime" }
iced_sentinel = { version = "0.13.0-dev", path = "sentinel" }
iced_tiny_skia = { version = "0.13.0-dev", path = "tiny_skia" } iced_tiny_skia = { version = "0.13.0-dev", path = "tiny_skia" }
iced_wgpu = { version = "0.13.0-dev", path = "wgpu" } iced_wgpu = { version = "0.13.0-dev", path = "wgpu" }
iced_widget = { version = "0.13.0-dev", path = "widget" } iced_widget = { version = "0.13.0-dev", path = "widget" }

View file

@ -1,5 +1,5 @@
[package] [package]
name = "iced_sentinel" name = "iced_beacon"
description = "A client/server protocol to monitor and supervise iced applications" description = "A client/server protocol to monitor and supervise iced applications"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
@ -17,6 +17,7 @@ iced_core.features = ["serde"]
bincode.workspace = true bincode.workspace = true
futures.workspace = true futures.workspace = true
log.workspace = true log.workspace = true
thiserror.workspace = true
tokio.workspace = true tokio.workspace = true
tokio.features = ["rt", "rt-multi-thread", "net", "sync", "time", "io-util", "macros"] tokio.features = ["rt", "rt-multi-thread", "net", "sync", "time", "io-util", "macros"]

123
beacon/src/client.rs Normal file
View file

@ -0,0 +1,123 @@
use crate::core::time::{Duration, SystemTime};
use crate::span;
use crate::theme;
use semver::Version;
use serde::{Deserialize, Serialize};
use tokio::io::{self, AsyncWriteExt};
use tokio::net;
use tokio::sync::mpsc;
use tokio::time;
use std::sync::Arc;
use std::thread;
pub const SERVER_ADDRESS: &str = "127.0.0.1:9167";
#[derive(Debug, Clone)]
pub struct Client {
sender: mpsc::Sender<Message>,
_handle: Arc<thread::JoinHandle<()>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Message {
Connected {
at: SystemTime,
name: String,
version: Version,
},
EventLogged {
at: SystemTime,
event: Event,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Event {
ThemeChanged(theme::Palette),
SpanStarted(span::Stage),
SpanFinished(span::Stage, Duration),
}
impl Client {
pub fn log(&self, event: Event) {
let _ = self.sender.try_send(Message::EventLogged {
at: SystemTime::now(),
event,
});
}
}
#[must_use]
pub fn connect(name: String) -> Client {
let (sender, receiver) = mpsc::channel(100);
let handle = std::thread::spawn(move || run(name, receiver));
Client {
sender,
_handle: Arc::new(handle),
}
}
#[tokio::main]
async fn run(name: String, mut receiver: mpsc::Receiver<Message>) {
let version = semver::Version::parse(env!("CARGO_PKG_VERSION"))
.expect("Parse package version");
loop {
match _connect().await {
Ok(mut stream) => {
let _ = send(
&mut stream,
Message::Connected {
at: SystemTime::now(),
name: name.clone(),
version: version.clone(),
},
)
.await;
while let Some(output) = receiver.recv().await {
match send(&mut stream, output).await {
Ok(()) => {}
Err(error) => {
log::warn!(
"Error sending message to server: {error}"
);
break;
}
}
}
}
Err(_) => {
time::sleep(time::Duration::from_secs(2)).await;
}
}
}
}
async fn _connect() -> Result<net::TcpStream, io::Error> {
log::debug!("Attempting to connect to server...");
let stream = net::TcpStream::connect(SERVER_ADDRESS).await?;
stream.set_nodelay(true)?;
stream.writable().await?;
Ok(stream)
}
async fn send(
stream: &mut net::TcpStream,
message: Message,
) -> Result<(), io::Error> {
let bytes = bincode::serialize(&message).expect("Encode input message");
let size = bytes.len() as u64;
stream.write_all(&size.to_be_bytes()).await?;
stream.write_all(&bytes).await?;
stream.flush().await?;
Ok(())
}

184
beacon/src/lib.rs Normal file
View file

@ -0,0 +1,184 @@
pub use iced_core as core;
pub use semver::Version;
pub mod client;
pub mod span;
mod stream;
pub use client::Client;
pub use span::Span;
use crate::core::theme;
use crate::core::time::{Duration, SystemTime};
use futures::{SinkExt, Stream};
use tokio::io::{self, AsyncReadExt};
use tokio::net;
#[derive(Debug, Clone)]
pub enum Event {
Connected {
at: SystemTime,
name: String,
version: Version,
},
Disconnected {
at: SystemTime,
},
ThemeChanged {
at: SystemTime,
palette: theme::Palette,
},
SpanFinished {
at: SystemTime,
duration: Duration,
span: Span,
},
}
impl Event {
pub fn at(&self) -> SystemTime {
match self {
Self::Connected { at, .. }
| Self::Disconnected { at, .. }
| Self::ThemeChanged { at, .. }
| Self::SpanFinished { at, .. } => *at,
}
}
}
pub fn run() -> impl Stream<Item = Event> {
stream::channel(|mut output| async move {
let mut buffer = Vec::new();
loop {
let Ok(mut stream) = connect().await else {
delay().await;
continue;
};
loop {
match receive(&mut stream, &mut buffer).await {
Ok(message) => {
match message {
client::Message::Connected {
at,
name,
version,
} => {
let _ = output
.send(Event::Connected {
at,
name,
version,
})
.await;
}
client::Message::EventLogged { at, event } => {
match event {
client::Event::ThemeChanged(palette) => {
let _ = output
.send(Event::ThemeChanged {
at,
palette,
})
.await;
}
client::Event::SpanStarted(_) => {}
client::Event::SpanFinished(
stage,
duration,
) => {
let span = match stage {
span::Stage::Boot => Span::Boot,
span::Stage::Update => Span::Update,
span::Stage::View(window) => {
Span::View { window }
}
span::Stage::Layout(window) => {
Span::Layout { window }
}
span::Stage::Interact(window) => {
Span::Interact { window }
}
span::Stage::Draw(window) => {
Span::Draw { window }
}
span::Stage::Present(window) => {
Span::Present { window }
}
span::Stage::Custom(
window,
name,
) => Span::Custom { window, name },
};
let _ = output
.send(Event::SpanFinished {
at,
duration,
span,
})
.await;
}
}
}
};
}
Err(Error::IOFailed(_)) => {
let _ = output
.send(Event::Disconnected {
at: SystemTime::now(),
})
.await;
delay().await;
break;
}
Err(Error::DecodingFailed(error)) => {
log::warn!("Error decoding beacon output: {error}")
}
}
}
}
})
}
async fn connect() -> Result<net::TcpStream, io::Error> {
let listener = net::TcpListener::bind(client::SERVER_ADDRESS).await?;
let (stream, _) = listener.accept().await?;
stream.set_nodelay(true)?;
stream.readable().await?;
Ok(stream)
}
async fn receive(
stream: &mut net::TcpStream,
buffer: &mut Vec<u8>,
) -> Result<client::Message, Error> {
let size = stream.read_u64().await? as usize;
if buffer.len() < size {
buffer.resize(size, 0);
}
let _n = stream.read_exact(&mut buffer[..size]).await?;
Ok(bincode::deserialize(buffer)?)
}
async fn delay() {
tokio::time::sleep(Duration::from_secs(2)).await;
}
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("input/output operation failed: {0}")]
IOFailed(#[from] io::Error),
#[error("decoding failed: {0}")]
DecodingFailed(#[from] Box<bincode::ErrorKind>),
}

61
beacon/src/span.rs Normal file
View file

@ -0,0 +1,61 @@
use crate::core::window;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Span {
Boot,
Update,
View { window: window::Id },
Layout { window: window::Id },
Interact { window: window::Id },
Draw { window: window::Id },
Present { window: window::Id },
Custom { window: window::Id, name: String },
}
impl Span {
pub fn stage(&self) -> Stage {
match self {
Span::Boot => Stage::Boot,
Span::Update => Stage::Update,
Span::View { window } => Stage::View(*window),
Span::Layout { window } => Stage::Layout(*window),
Span::Interact { window } => Stage::Interact(*window),
Span::Draw { window } => Stage::Draw(*window),
Span::Present { window } => Stage::Present(*window),
Span::Custom { window, name } => {
Stage::Custom(*window, name.clone())
}
}
}
}
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub enum Stage {
Boot,
Update,
View(window::Id),
Layout(window::Id),
Interact(window::Id),
Draw(window::Id),
Present(window::Id),
Custom(window::Id, String),
}
impl std::fmt::Display for Stage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Stage::Boot => "Boot",
Stage::Update => "Update",
Stage::View(_) => "View",
Stage::Layout(_) => "Layout",
Stage::Interact(_) => "Interact",
Stage::Draw(_) => "Draw",
Stage::Present(_) => "Present",
Stage::Custom(_, name) => name,
})
}
}

15
beacon/src/stream.rs Normal file
View file

@ -0,0 +1,15 @@
use futures::channel::mpsc;
use futures::stream::{self, Stream, StreamExt};
use futures::Future;
pub fn channel<T, F>(f: impl Fn(mpsc::Sender<T>) -> F) -> impl Stream<Item = T>
where
F: Future<Output = ()>,
{
let (sender, receiver) = mpsc::channel(1);
stream::select(
receiver,
stream::once(f(sender)).filter_map(|_| async { None }),
)
}

View file

@ -11,13 +11,13 @@ categories.workspace = true
keywords.workspace = true keywords.workspace = true
[features] [features]
enable = ["dep:iced_sentinel", "dep:once_cell"] enable = ["dep:iced_beacon", "dep:once_cell"]
[dependencies] [dependencies]
iced_core.workspace = true iced_core.workspace = true
iced_sentinel.workspace = true iced_beacon.workspace = true
iced_sentinel.optional = true iced_beacon.optional = true
once_cell.workspace = true once_cell.workspace = true
once_cell.optional = true once_cell.optional = true

View file

@ -3,45 +3,49 @@ pub use iced_core as core;
use crate::core::theme; use crate::core::theme;
use crate::core::window; use crate::core::window;
pub use internal::Timer; pub use internal::Span;
pub fn open_axe() {} pub fn init(name: &str) {
internal::init(name);
}
pub fn open_comet() {}
pub fn log_message(_message: &impl std::fmt::Debug) {} pub fn log_message(_message: &impl std::fmt::Debug) {}
pub fn theme_changed(palette: theme::Palette) { pub fn theme_changed(f: impl FnOnce() -> Option<theme::Palette>) {
internal::theme_changed(palette); internal::theme_changed(f);
} }
pub fn boot_time() -> Timer { pub fn boot() -> Span {
internal::boot_time() internal::boot()
} }
pub fn update_time() -> Timer { pub fn update() -> Span {
internal::update_time() internal::update()
} }
pub fn view_time(window: window::Id) -> Timer { pub fn view(window: window::Id) -> Span {
internal::view_time(window) internal::view(window)
} }
pub fn layout_time(window: window::Id) -> Timer { pub fn layout(window: window::Id) -> Span {
internal::layout_time(window) internal::layout(window)
} }
pub fn interact_time(window: window::Id) -> Timer { pub fn interact(window: window::Id) -> Span {
internal::interact_time(window) internal::interact(window)
} }
pub fn draw_time(window: window::Id) -> Timer { pub fn draw(window: window::Id) -> Span {
internal::draw_time(window) internal::draw(window)
} }
pub fn render_time(window: window::Id) -> Timer { pub fn present(window: window::Id) -> Span {
internal::render_time(window) internal::present(window)
} }
pub fn time(window: window::Id, name: impl AsRef<str>) -> Timer { pub fn time(window: window::Id, name: impl AsRef<str>) -> Span {
internal::time(window, name) internal::time(window, name)
} }
@ -52,158 +56,156 @@ pub fn skip_next_timing() {
#[cfg(feature = "enable")] #[cfg(feature = "enable")]
mod internal { mod internal {
use crate::core::theme; use crate::core::theme;
use crate::core::time::{Instant, SystemTime}; use crate::core::time::Instant;
use crate::core::window; use crate::core::window;
use iced_sentinel::client::{self, Client}; use iced_beacon as beacon;
use iced_sentinel::timing::{self, Timing};
use beacon::client::{self, Client};
use beacon::span;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::sync::{Mutex, MutexGuard}; use std::sync::atomic::{self, AtomicBool};
use std::sync::RwLock;
pub fn theme_changed(palette: theme::Palette) { pub fn init(name: &str) {
let mut debug = lock(); name.clone_into(&mut NAME.write().expect("Write application name"));
}
if debug.last_palette.as_ref() != Some(&palette) { pub fn theme_changed(f: impl FnOnce() -> Option<theme::Palette>) {
debug.sentinel.report_theme_change(palette); let Some(palette) = f() else {
return;
};
debug.last_palette = Some(palette); if LAST_PALETTE.read().expect("Read last palette").as_ref()
!= Some(&palette)
{
BEACON.log(client::Event::ThemeChanged(palette));
*LAST_PALETTE.write().expect("Write last palette") = Some(palette);
} }
} }
pub fn boot_time() -> Timer { pub fn boot() -> Span {
timer(timing::Stage::Boot) span(span::Stage::Boot)
} }
pub fn update_time() -> Timer { pub fn update() -> Span {
timer(timing::Stage::Update) span(span::Stage::Update)
} }
pub fn view_time(window: window::Id) -> Timer { pub fn view(window: window::Id) -> Span {
timer(timing::Stage::View(window)) span(span::Stage::View(window))
} }
pub fn layout_time(window: window::Id) -> Timer { pub fn layout(window: window::Id) -> Span {
timer(timing::Stage::Layout(window)) span(span::Stage::Layout(window))
} }
pub fn interact_time(window: window::Id) -> Timer { pub fn interact(window: window::Id) -> Span {
timer(timing::Stage::Interact(window)) span(span::Stage::Interact(window))
} }
pub fn draw_time(window: window::Id) -> Timer { pub fn draw(window: window::Id) -> Span {
timer(timing::Stage::Draw(window)) span(span::Stage::Draw(window))
} }
pub fn render_time(window: window::Id) -> Timer { pub fn present(window: window::Id) -> Span {
timer(timing::Stage::Render(window)) span(span::Stage::Present(window))
} }
pub fn time(window: window::Id, name: impl AsRef<str>) -> Timer { pub fn time(window: window::Id, name: impl AsRef<str>) -> Span {
timer(timing::Stage::Custom(window, name.as_ref().to_owned())) span(span::Stage::Custom(window, name.as_ref().to_owned()))
} }
pub fn skip_next_timing() { pub fn skip_next_timing() {
lock().skip_next_timing = true; SKIP_NEXT_SPAN.store(true, atomic::Ordering::Relaxed);
} }
fn timer(stage: timing::Stage) -> Timer { fn span(span: span::Stage) -> Span {
Timer { BEACON.log(client::Event::SpanStarted(span.clone()));
stage,
Span {
span,
start: Instant::now(), start: Instant::now(),
start_system_time: SystemTime::now(),
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Timer { pub struct Span {
stage: timing::Stage, span: span::Stage,
start: Instant, start: Instant,
start_system_time: SystemTime,
} }
impl Timer { impl Span {
pub fn finish(self) { pub fn finish(self) {
let mut debug = lock(); if SKIP_NEXT_SPAN.fetch_and(false, atomic::Ordering::Relaxed) {
if debug.skip_next_timing {
debug.skip_next_timing = false;
return; return;
} }
debug.sentinel.report_timing(Timing { BEACON.log(client::Event::SpanFinished(
stage: self.stage, self.span,
start: self.start_system_time, self.start.elapsed(),
duration: self.start.elapsed(), ));
});
} }
} }
#[derive(Debug)] static BEACON: Lazy<Client> = Lazy::new(|| {
struct Debug { client::connect(NAME.read().expect("Read application name").to_owned())
sentinel: Client, });
last_palette: Option<theme::Palette>,
skip_next_timing: bool,
}
fn lock() -> MutexGuard<'static, Debug> { static NAME: RwLock<String> = RwLock::new(String::new());
static DEBUG: Lazy<Mutex<Debug>> = Lazy::new(|| { static LAST_PALETTE: RwLock<Option<theme::Palette>> = RwLock::new(None);
Mutex::new(Debug { static SKIP_NEXT_SPAN: AtomicBool = AtomicBool::new(false);
sentinel: client::connect(),
last_palette: None,
skip_next_timing: false,
})
});
DEBUG.lock().expect("Acquire debug lock")
}
} }
#[cfg(not(feature = "enable"))] #[cfg(not(feature = "enable"))]
mod internal { mod internal {
use crate::core::theme;
use crate::core::window; use crate::core::window;
use crate::style::theme;
pub fn theme_changed(_palette: theme::Palette) {} pub fn init(_name: &str) {}
pub fn boot_time() -> Timer { pub fn theme_changed(_f: impl FnOnce() -> Option<theme::Palette>) {}
Timer
pub fn boot() -> Span {
Span
} }
pub fn update_time() -> Timer { pub fn update() -> Span {
Timer Span
} }
pub fn view_time(_window: window::Id) -> Timer { pub fn view(_window: window::Id) -> Span {
Timer Span
} }
pub fn layout_time(_window: window::Id) -> Timer { pub fn layout(_window: window::Id) -> Span {
Timer Span
} }
pub fn interact_time(_window: window::Id) -> Timer { pub fn interact(_window: window::Id) -> Span {
Timer Span
} }
pub fn draw_time(_window: window::Id) -> Timer { pub fn draw(_window: window::Id) -> Span {
Timer Span
} }
pub fn render_time(_window: window::Id) -> Timer { pub fn present(_window: window::Id) -> Span {
Timer Span
} }
pub fn time(_window: window::Id, _name: impl AsRef<str>) -> Timer { pub fn time(_window: window::Id, _name: impl AsRef<str>) -> Span {
Timer Span
} }
pub fn skip_next_timing() {} pub fn skip_next_timing() {}
#[derive(Debug)] #[derive(Debug)]
pub struct Timer; pub struct Span;
impl Timer { impl Span {
pub fn finish(self) {} pub fn finish(self) {}
} }
} }

2
docs/logo-no-shadow.svg Normal file
View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="140" height="140" fill="none" version="1.1" viewBox="35 31 179 171"><rect x="42" y="31.001" width="169.9" height="169.9" rx="49.815" fill="url(#paint1_linear)"/><path d="m182.62 65.747-28.136 28.606-6.13-6.0291 28.136-28.606 6.13 6.0291zm-26.344 0.218-42.204 42.909-6.13-6.029 42.204-42.909 6.13 6.0291zm-61.648 23.913c5.3254-5.3831 10.65-10.765 21.569-21.867l6.13 6.0291c-10.927 11.11-16.258 16.498-21.587 21.885-4.4007 4.4488-8.8009 8.8968-16.359 16.573l31.977 8.358 25.968-26.402 6.13 6.0292-25.968 26.402 8.907 31.908 42.138-42.087 6.076 6.083-49.109 49.05-45.837-12.628-13.394-45.646 1.7714-1.801c10.928-11.111 16.258-16.499 21.588-21.886zm28.419 70.99-8.846-31.689-31.831-8.32 9.1945 31.335 31.482 8.674zm47.734-56.517 7.122-7.1221-6.08-6.0797-7.147 7.1474-30.171 30.674 6.13 6.029 30.146-30.649z" clip-rule="evenodd" fill="url(#paint2_linear)" fill-rule="evenodd"/><defs><filter id="filter0_f" x="55" y="47.001" width="144" height="168" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur" stdDeviation="2"/></filter><linearGradient id="paint0_linear" x1="127" x2="127" y1="51.001" y2="211" gradientUnits="userSpaceOnUse"><stop offset=".052083"/><stop stop-opacity=".08" offset="1"/></linearGradient><linearGradient id="paint1_linear" x1="212" x2="57.5" y1="31.001" y2="189" gradientUnits="userSpaceOnUse"><stop stop-color="#00A3FF" offset="0"/><stop stop-color="#30f" offset="1"/></linearGradient><linearGradient id="paint2_linear" x1="86.098" x2="206.01" y1="158.28" y2="35.327" gradientUnits="userSpaceOnUse"><stop stop-color="#fff" offset="0"/><stop stop-color="#fff" offset="1"/></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -102,7 +102,7 @@ where
bounds, bounds,
); );
let interact_timer = debug::interact_time(window::Id::MAIN); let interact_span = debug::interact(window::Id::MAIN);
let mut messages = Vec::new(); let mut messages = Vec::new();
let (_, event_statuses) = user_interface.update( let (_, event_statuses) = user_interface.update(
@ -125,13 +125,13 @@ where
self.queued_events.clear(); self.queued_events.clear();
messages.append(&mut self.queued_messages); messages.append(&mut self.queued_messages);
drop(interact_timer); interact_span.finish();
let command = if messages.is_empty() { let command = if messages.is_empty() {
let draw_timer = debug::draw_time(window::Id::MAIN); let draw_span = debug::draw(window::Id::MAIN);
self.mouse_interaction = self.mouse_interaction =
user_interface.draw(renderer, theme, style, cursor); user_interface.draw(renderer, theme, style, cursor);
drop(draw_timer); draw_span.finish();
self.cache = Some(user_interface.into_cache()); self.cache = Some(user_interface.into_cache());
@ -145,9 +145,9 @@ where
Command::batch(messages.into_iter().map(|message| { Command::batch(messages.into_iter().map(|message| {
debug::log_message(&message); debug::log_message(&message);
let update_timer = debug::update_time(); let update_span = debug::update();
let command = self.program.update(message); let command = self.program.update(message);
drop(update_timer); update_span.finish();
command command
})); }));
@ -159,10 +159,10 @@ where
bounds, bounds,
); );
let draw_timer = debug::draw_time(window::Id::MAIN); let draw_spawn = debug::draw(window::Id::MAIN);
self.mouse_interaction = self.mouse_interaction =
user_interface.draw(renderer, theme, style, cursor); user_interface.draw(renderer, theme, style, cursor);
drop(draw_timer); draw_spawn.finish();
self.cache = Some(user_interface.into_cache()); self.cache = Some(user_interface.into_cache());
@ -214,13 +214,13 @@ fn build_user_interface<'a, P: Program>(
renderer: &mut P::Renderer, renderer: &mut P::Renderer,
size: Size, size: Size,
) -> UserInterface<'a, P::Message, P::Theme, P::Renderer> { ) -> UserInterface<'a, P::Message, P::Theme, P::Renderer> {
let view_timer = debug::view_time(window::Id::MAIN); let view_span = debug::view(window::Id::MAIN);
let view = program.view(); let view = program.view();
drop(view_timer); view_span.finish();
let layout_timer = debug::layout_time(window::Id::MAIN); let layout_span = debug::layout(window::Id::MAIN);
let user_interface = UserInterface::build(view, size, cache, renderer); let user_interface = UserInterface::build(view, size, cache, renderer);
drop(layout_timer); layout_span.finish();
user_interface user_interface
} }

View file

@ -1,93 +0,0 @@
use crate::core::time::SystemTime;
use crate::theme;
use crate::{Input, Timing, SOCKET_ADDRESS};
use tokio::io::{self, AsyncWriteExt};
use tokio::net;
use tokio::sync::mpsc;
use tokio::time;
#[derive(Debug, Clone)]
pub struct Client {
sender: mpsc::Sender<Input>,
}
impl Client {
pub fn report_theme_change(&mut self, palette: theme::Palette) {
let _ = self.sender.try_send(Input::ThemeChanged {
at: SystemTime::now(),
palette,
});
}
pub fn report_timing(&mut self, timing: Timing) {
let _ = self.sender.try_send(Input::TimingMeasured(timing));
}
}
#[must_use]
pub fn connect() -> Client {
let (sender, receiver) = mpsc::channel(1_000);
std::thread::spawn(move || run(receiver));
Client { sender }
}
#[tokio::main]
async fn run(mut receiver: mpsc::Receiver<Input>) {
let version = semver::Version::parse(env!("CARGO_PKG_VERSION"))
.expect("Parse package version");
loop {
match _connect().await {
Ok(mut stream) => {
let _ = send(
&mut stream,
Input::Connected {
at: SystemTime::now(),
version: version.clone(),
},
)
.await;
while let Some(input) = receiver.recv().await {
match send(&mut stream, input).await {
Ok(()) => {}
Err(error) => {
log::warn!("Error sending message to sentinel server: {error}");
break;
}
}
}
}
Err(_) => {
time::sleep(time::Duration::from_secs(2)).await;
}
}
}
}
async fn _connect() -> Result<io::BufStream<net::TcpStream>, io::Error> {
log::debug!("Attempting to connect sentinel to server...");
let stream = net::TcpStream::connect(SOCKET_ADDRESS).await?;
stream.set_nodelay(true)?;
stream.writable().await?;
Ok(io::BufStream::new(stream))
}
async fn send(
stream: &mut io::BufStream<net::TcpStream>,
input: Input,
) -> Result<(), io::Error> {
let bytes = bincode::serialize(&input).expect("Encode input message");
let size = bytes.len() as u64;
stream.write_all(&size.to_be_bytes()).await?;
stream.write_all(&bytes).await?;
stream.flush().await?;
Ok(())
}

View file

@ -1,137 +0,0 @@
pub use iced_core as core;
pub use semver::Version;
pub mod client;
pub mod timing;
use crate::core::theme;
use crate::core::time::SystemTime;
use crate::timing::Timing;
use futures::future;
use futures::stream::{self, Stream, StreamExt};
use serde::{Deserialize, Serialize};
use tokio::io::{self, AsyncReadExt, BufStream};
use tokio::net;
pub const SOCKET_ADDRESS: &str = "127.0.0.1:9167";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Input {
Connected {
at: SystemTime,
version: Version,
},
ThemeChanged {
at: SystemTime,
palette: theme::Palette,
},
TimingMeasured(Timing),
}
#[derive(Debug, Clone)]
pub enum Event {
Connected {
at: SystemTime,
version: Version,
},
Disconnected {
at: SystemTime,
},
ThemeChanged {
at: SystemTime,
palette: theme::Palette,
},
TimingMeasured(Timing),
}
impl Event {
pub fn at(&self) -> SystemTime {
match self {
Self::Connected { at, .. }
| Self::Disconnected { at }
| Self::ThemeChanged { at, .. } => *at,
Self::TimingMeasured(timing) => timing.start,
}
}
}
pub fn run() -> impl Stream<Item = Event> {
enum State {
Disconnected,
Connected(BufStream<net::TcpStream>),
}
stream::unfold(State::Disconnected, |state| async {
match state {
State::Disconnected => match connect().await {
Ok(stream) => {
let stream = BufStream::new(stream);
Some((None, State::Connected(stream)))
}
Err(_error) => Some((None, State::Disconnected)),
},
State::Connected(stream) => match receive(stream).await {
Ok((stream, input)) => {
let event = match input {
Input::Connected { at, version } => {
Event::Connected { at, version }
}
Input::TimingMeasured(timing) => {
Event::TimingMeasured(timing)
}
Input::ThemeChanged { at, palette } => {
Event::ThemeChanged { at, palette }
}
};
Some((Some(event), State::Connected(stream)))
}
Err(_) => Some((
Some(Event::Disconnected {
at: SystemTime::now(),
}),
State::Disconnected,
)),
},
}
})
.filter_map(future::ready)
}
async fn connect() -> Result<net::TcpStream, io::Error> {
let listener = net::TcpListener::bind(SOCKET_ADDRESS).await?;
let (stream, _) = listener.accept().await?;
stream.set_nodelay(true)?;
stream.readable().await?;
Ok(stream)
}
async fn receive(
mut stream: BufStream<net::TcpStream>,
) -> Result<(BufStream<net::TcpStream>, Input), io::Error> {
let mut bytes = Vec::new();
loop {
let size = stream.read_u64().await? as usize;
if bytes.len() < size {
bytes.resize(size, 0);
}
let _n = stream.read_exact(&mut bytes[..size]).await?;
match bincode::deserialize(&bytes) {
Ok(input) => {
return Ok((stream, input));
}
Err(_) => {
log::warn!("Error decoding sentinel message");
}
}
}
}

View file

@ -1,43 +0,0 @@
use crate::core::time::{Duration, SystemTime};
use crate::core::window;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct Timing {
pub stage: Stage,
pub start: SystemTime,
pub duration: Duration,
}
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub enum Stage {
Boot,
Update,
View(window::Id),
Layout(window::Id),
Interact(window::Id),
Draw(window::Id),
Render(window::Id),
Custom(window::Id, String),
}
impl fmt::Display for Stage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Boot => write!(f, "Boot"),
Self::Update => write!(f, "Update"),
Self::View(_) => write!(f, "View"),
Self::Layout(_) => write!(f, "Layout"),
Self::Interact(_) => write!(f, "Interact"),
Self::Draw(_) => write!(f, "Draw"),
Self::Render(_) => write!(f, "Render"),
Self::Custom(_, name) => f.write_str(name),
}
}
}

View file

@ -118,6 +118,9 @@ where
/// The data needed to initialize your [`Application`]. /// The data needed to initialize your [`Application`].
type Flags; type Flags;
/// Returns the unique name of the [`Application`].
fn name() -> &'static str;
/// Initializes the [`Application`] with the flags provided to /// Initializes the [`Application`] with the flags provided to
/// [`run`] as part of the [`Settings`]. /// [`run`] as part of the [`Settings`].
/// ///
@ -250,6 +253,10 @@ where
{ {
type Flags = A::Flags; type Flags = A::Flags;
fn name() -> &'static str {
A::name()
}
fn new(flags: Self::Flags) -> (Self, Command<A::Message>) { fn new(flags: Self::Flags) -> (Self, Command<A::Message>) {
let (app, command) = A::new(flags); let (app, command) = A::new(flags);

View file

@ -106,6 +106,12 @@ where
type Renderer = Renderer; type Renderer = Renderer;
type Executor = executor::Default; type Executor = executor::Default;
fn name() -> &'static str {
let type_name = std::any::type_name::<State>();
type_name.split("::").next().unwrap_or(type_name)
}
fn load(&self) -> Command<Self::Message> { fn load(&self) -> Command<Self::Message> {
Command::none() Command::none()
} }
@ -211,6 +217,10 @@ impl<P: Definition> Program<P> {
) )
} }
fn name() -> &'static str {
P::name()
}
fn title(&self) -> String { fn title(&self) -> String {
self.program.title(&self.state) self.program.title(&self.state)
} }
@ -431,6 +441,8 @@ pub trait Definition: Sized {
/// The executor of the program. /// The executor of the program.
type Executor: Executor; type Executor: Executor;
fn name() -> &'static str;
fn load(&self) -> Command<Self::Message>; fn load(&self) -> Command<Self::Message>;
fn update( fn update(
@ -484,12 +496,16 @@ fn with_title<P: Definition>(
type Renderer = P::Renderer; type Renderer = P::Renderer;
type Executor = P::Executor; type Executor = P::Executor;
fn title(&self, state: &Self::State) -> String {
self.title.title(state)
}
fn load(&self) -> Command<Self::Message> { fn load(&self) -> Command<Self::Message> {
self.program.load() self.program.load()
} }
fn title(&self, state: &Self::State) -> String { fn name() -> &'static str {
self.title.title(state) P::name()
} }
fn update( fn update(
@ -553,6 +569,10 @@ fn with_load<P: Definition>(
Command::batch([self.program.load(), (self.load)()]) Command::batch([self.program.load(), (self.load)()])
} }
fn name() -> &'static str {
P::name()
}
fn update( fn update(
&self, &self,
state: &mut Self::State, state: &mut Self::State,
@ -621,6 +641,10 @@ fn with_subscription<P: Definition>(
(self.subscription)(state) (self.subscription)(state)
} }
fn name() -> &'static str {
P::name()
}
fn load(&self) -> Command<Self::Message> { fn load(&self) -> Command<Self::Message> {
self.program.load() self.program.load()
} }
@ -686,6 +710,10 @@ fn with_theme<P: Definition>(
(self.theme)(state) (self.theme)(state)
} }
fn name() -> &'static str {
P::name()
}
fn load(&self) -> Command<Self::Message> { fn load(&self) -> Command<Self::Message> {
self.program.load() self.program.load()
} }
@ -755,6 +783,10 @@ fn with_style<P: Definition>(
(self.style)(state, theme) (self.style)(state, theme)
} }
fn name() -> &'static str {
P::name()
}
fn load(&self) -> Command<Self::Message> { fn load(&self) -> Command<Self::Message> {
self.program.load() self.program.load()
} }

View file

@ -61,7 +61,7 @@ pub use settings::Settings;
pub use geometry::Geometry; pub use geometry::Geometry;
use crate::core::{ use crate::core::{
Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, Background, Color, Font, Pixels, Point, Rectangle, Transformation,
}; };
use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::text::{Editor, Paragraph};
use crate::graphics::Viewport; use crate::graphics::Viewport;
@ -477,7 +477,7 @@ impl core::text::Renderer for Renderer {
impl core::image::Renderer for Renderer { impl core::image::Renderer for Renderer {
type Handle = core::image::Handle; type Handle = core::image::Handle;
fn measure_image(&self, handle: &Self::Handle) -> Size<u32> { fn measure_image(&self, handle: &Self::Handle) -> core::Size<u32> {
self.image_cache.borrow_mut().measure_image(handle) self.image_cache.borrow_mut().measure_image(handle)
} }
@ -503,7 +503,7 @@ impl core::image::Renderer for Renderer {
#[cfg(feature = "svg")] #[cfg(feature = "svg")]
impl core::svg::Renderer for Renderer { impl core::svg::Renderer for Renderer {
fn measure_svg(&self, handle: &core::svg::Handle) -> Size<u32> { fn measure_svg(&self, handle: &core::svg::Handle) -> core::Size<u32> {
self.image_cache.borrow_mut().measure_svg(handle) self.image_cache.borrow_mut().measure_svg(handle)
} }
@ -539,7 +539,7 @@ impl graphics::geometry::Renderer for Renderer {
type Geometry = Geometry; type Geometry = Geometry;
type Frame = geometry::Frame; type Frame = geometry::Frame;
fn new_frame(&self, size: Size) -> Self::Frame { fn new_frame(&self, size: core::Size) -> Self::Frame {
geometry::Frame::new(size) geometry::Frame::new(size)
} }

View file

@ -48,6 +48,9 @@ where
/// The data needed to initialize your [`Application`]. /// The data needed to initialize your [`Application`].
type Flags; type Flags;
/// Returns the unique name of the [`Application`].
fn name() -> &'static str;
/// Initializes the [`Application`] with the flags provided to /// Initializes the [`Application`] with the flags provided to
/// [`run`] as part of the [`Settings`]. /// [`run`] as part of the [`Settings`].
/// ///
@ -156,7 +159,8 @@ where
use futures::Future; use futures::Future;
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
let boot_timer = debug::boot_time(); debug::init(A::name());
let boot_span = debug::boot();
let event_loop = EventLoop::with_user_event() let event_loop = EventLoop::with_user_event()
.build() .build()
@ -193,7 +197,7 @@ where
control_sender, control_sender,
init_command, init_command,
settings.fonts, settings.fonts,
boot_timer, boot_span,
)); ));
let context = task::Context::from_waker(task::noop_waker_ref()); let context = task::Context::from_waker(task::noop_waker_ref());
@ -498,7 +502,7 @@ async fn run_instance<A, E, C>(
mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>, mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,
init_command: Command<A::Message>, init_command: Command<A::Message>,
fonts: Vec<Cow<'static, [u8]>>, fonts: Vec<Cow<'static, [u8]>>,
boot_timer: debug::Timer, boot_span: debug::Span,
) where ) where
A: Application + 'static, A: Application + 'static,
E: Executor + 'static, E: Executor + 'static,
@ -554,7 +558,7 @@ async fn run_instance<A, E, C>(
&window, &window,
); );
runtime.track(application.subscription().into_recipes()); runtime.track(application.subscription().into_recipes());
boot_timer.finish(); boot_span.finish();
let mut user_interface = ManuallyDrop::new(build_user_interface( let mut user_interface = ManuallyDrop::new(build_user_interface(
&application, &application,
@ -608,12 +612,12 @@ async fn run_instance<A, E, C>(
if viewport_version != current_viewport_version { if viewport_version != current_viewport_version {
let logical_size = state.logical_size(); let logical_size = state.logical_size();
let layout_timer = debug::layout_time(window::Id::MAIN); let layout_span = debug::layout(window::Id::MAIN);
user_interface = ManuallyDrop::new( user_interface = ManuallyDrop::new(
ManuallyDrop::into_inner(user_interface) ManuallyDrop::into_inner(user_interface)
.relayout(logical_size, &mut renderer), .relayout(logical_size, &mut renderer),
); );
layout_timer.finish(); layout_span.finish();
compositor.configure_surface( compositor.configure_surface(
&mut surface, &mut surface,
@ -660,7 +664,7 @@ async fn run_instance<A, E, C>(
runtime.broadcast(redraw_event, core::event::Status::Ignored); runtime.broadcast(redraw_event, core::event::Status::Ignored);
let draw_timer = debug::draw_time(window::Id::MAIN); let draw_span = debug::draw(window::Id::MAIN);
let new_mouse_interaction = user_interface.draw( let new_mouse_interaction = user_interface.draw(
&mut renderer, &mut renderer,
state.theme(), state.theme(),
@ -670,7 +674,7 @@ async fn run_instance<A, E, C>(
state.cursor(), state.cursor(),
); );
redraw_pending = false; redraw_pending = false;
draw_timer.finish(); draw_span.finish();
if new_mouse_interaction != mouse_interaction { if new_mouse_interaction != mouse_interaction {
window.set_cursor(conversion::mouse_interaction( window.set_cursor(conversion::mouse_interaction(
@ -680,7 +684,7 @@ async fn run_instance<A, E, C>(
mouse_interaction = new_mouse_interaction; mouse_interaction = new_mouse_interaction;
} }
let render_timer = debug::render_time(window::Id::MAIN); let present_span = debug::present(window::Id::MAIN);
match compositor.present( match compositor.present(
&mut renderer, &mut renderer,
&mut surface, &mut surface,
@ -688,7 +692,7 @@ async fn run_instance<A, E, C>(
state.background_color(), state.background_color(),
) { ) {
Ok(()) => { Ok(()) => {
render_timer.finish(); present_span.finish();
} }
Err(error) => match error { Err(error) => match error {
// This is an unrecoverable error. // This is an unrecoverable error.
@ -733,7 +737,7 @@ async fn run_instance<A, E, C>(
redraw_request: None, redraw_request: None,
} }
} else { } else {
let interact_timer = debug::interact_time(window::Id::MAIN); let interact_span = debug::interact(window::Id::MAIN);
let (interface_state, statuses) = user_interface.update( let (interface_state, statuses) = user_interface.update(
&events, &events,
state.cursor(), state.cursor(),
@ -747,7 +751,7 @@ async fn run_instance<A, E, C>(
{ {
runtime.broadcast(event, status); runtime.broadcast(event, status);
} }
interact_timer.finish(); interact_span.finish();
interface_state interface_state
}; };
@ -842,13 +846,13 @@ pub fn build_user_interface<'a, A: Application>(
where where
A::Theme: DefaultStyle, A::Theme: DefaultStyle,
{ {
let view_timer = debug::view_time(window::Id::MAIN); let view_span = debug::view(window::Id::MAIN);
let view = application.view(); let view = application.view();
view_timer.finish(); view_span.finish();
let layout_timer = debug::layout_time(window::Id::MAIN); let layout_span = debug::layout(window::Id::MAIN);
let user_interface = UserInterface::build(view, size, cache, renderer); let user_interface = UserInterface::build(view, size, cache, renderer);
layout_timer.finish(); layout_span.finish();
user_interface user_interface
} }
@ -875,9 +879,9 @@ pub fn update<A: Application, C, E: Executor>(
for message in messages.drain(..) { for message in messages.drain(..) {
debug::log_message(&message); debug::log_message(&message);
let update_timer = debug::update_time(); let update_span = debug::update();
let command = runtime.enter(|| application.update(message)); let command = runtime.enter(|| application.update(message));
update_timer.finish(); update_span.finish();
run_command( run_command(
application, application,

View file

@ -38,8 +38,7 @@ where
let theme = application.theme(); let theme = application.theme();
let appearance = application.style(&theme); let appearance = application.style(&theme);
let _ = application::DefaultStyle::palette(&theme) debug::theme_changed(|| application::DefaultStyle::palette(&theme));
.map(debug::theme_changed);
let viewport = { let viewport = {
let physical_size = window.inner_size(); let physical_size = window.inner_size();
@ -216,7 +215,8 @@ where
self.theme = application.theme(); self.theme = application.theme();
self.appearance = application.style(&self.theme); self.appearance = application.style(&self.theme);
let _ = application::DefaultStyle::palette(&self.theme) debug::theme_changed(|| {
.map(debug::theme_changed); application::DefaultStyle::palette(&self.theme)
});
} }
} }

View file

@ -117,7 +117,7 @@ where
{ {
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
let boot_timer = debug::boot_time(); let boot_span = debug::boot();
let event_loop = EventLoop::with_user_event() let event_loop = EventLoop::with_user_event()
.build() .build()
@ -153,7 +153,7 @@ where
event_receiver, event_receiver,
control_sender, control_sender,
init_command, init_command,
boot_timer, boot_span,
)); ));
let context = task::Context::from_waker(task::noop_waker_ref()); let context = task::Context::from_waker(task::noop_waker_ref());
@ -452,7 +452,7 @@ async fn run_instance<A, E, C>(
mut event_receiver: mpsc::UnboundedReceiver<Event<A::Message>>, mut event_receiver: mpsc::UnboundedReceiver<Event<A::Message>>,
mut control_sender: mpsc::UnboundedSender<Control>, mut control_sender: mpsc::UnboundedSender<Control>,
init_command: Command<A::Message>, init_command: Command<A::Message>,
boot_timer: debug::Timer, boot_span: debug::Span,
) where ) where
A: Application + 'static, A: Application + 'static,
E: Executor + 'static, E: Executor + 'static,
@ -524,7 +524,7 @@ async fn run_instance<A, E, C>(
); );
runtime.track(application.subscription().into_recipes()); runtime.track(application.subscription().into_recipes());
boot_timer.finish(); boot_span.finish();
let mut messages = Vec::new(); let mut messages = Vec::new();
let mut user_events = 0; let mut user_events = 0;
@ -636,7 +636,7 @@ async fn run_instance<A, E, C>(
&mut messages, &mut messages,
); );
let draw_timer = debug::draw_time(id); let draw_span = debug::draw(id);
let new_mouse_interaction = ui.draw( let new_mouse_interaction = ui.draw(
&mut window.renderer, &mut window.renderer,
window.state.theme(), window.state.theme(),
@ -645,7 +645,7 @@ async fn run_instance<A, E, C>(
}, },
cursor, cursor,
); );
draw_timer.finish(); draw_span.finish();
if new_mouse_interaction != window.mouse_interaction { if new_mouse_interaction != window.mouse_interaction {
window.raw.set_cursor( window.raw.set_cursor(
@ -692,7 +692,7 @@ async fn run_instance<A, E, C>(
{ {
let logical_size = window.state.logical_size(); let logical_size = window.state.logical_size();
let layout_time = debug::layout_time(id); let layout = debug::layout(id);
let ui = user_interfaces let ui = user_interfaces
.remove(&id) .remove(&id)
.expect("Remove user interface"); .expect("Remove user interface");
@ -701,9 +701,9 @@ async fn run_instance<A, E, C>(
id, id,
ui.relayout(logical_size, &mut window.renderer), ui.relayout(logical_size, &mut window.renderer),
); );
layout_time.finish(); layout.finish();
let draw_time = debug::draw_time(id); let draw = debug::draw(id);
let new_mouse_interaction = user_interfaces let new_mouse_interaction = user_interfaces
.get_mut(&id) .get_mut(&id)
.expect("Get user interface") .expect("Get user interface")
@ -715,7 +715,7 @@ async fn run_instance<A, E, C>(
}, },
window.state.cursor(), window.state.cursor(),
); );
draw_time.finish(); draw.finish();
if new_mouse_interaction != window.mouse_interaction if new_mouse_interaction != window.mouse_interaction
{ {
@ -739,7 +739,7 @@ async fn run_instance<A, E, C>(
window.state.viewport_version(); window.state.viewport_version();
} }
let render_time = debug::render_time(id); let present_span = debug::present(id);
match compositor.present( match compositor.present(
&mut window.renderer, &mut window.renderer,
&mut window.surface, &mut window.surface,
@ -747,7 +747,7 @@ async fn run_instance<A, E, C>(
window.state.background_color(), window.state.background_color(),
) { ) {
Ok(()) => { Ok(()) => {
render_time.finish(); present_span.finish();
// TODO: Handle animations! // TODO: Handle animations!
// Maybe we can use `ControlFlow::WaitUntil` for this. // Maybe we can use `ControlFlow::WaitUntil` for this.
@ -821,7 +821,7 @@ async fn run_instance<A, E, C>(
let mut uis_stale = false; let mut uis_stale = false;
for (id, window) in window_manager.iter_mut() { for (id, window) in window_manager.iter_mut() {
let interact_time = debug::interact_time(id); let interact = debug::interact(id);
let mut window_events = vec![]; let mut window_events = vec![];
events.retain(|(window_id, event)| { events.retain(|(window_id, event)| {
@ -864,7 +864,7 @@ async fn run_instance<A, E, C>(
{ {
runtime.broadcast(event, status); runtime.broadcast(event, status);
} }
interact_time.finish(); interact.finish();
} }
// TODO mw application update returns which window IDs to update // TODO mw application update returns which window IDs to update
@ -938,13 +938,13 @@ fn build_user_interface<'a, A: Application>(
where where
A::Theme: DefaultStyle, A::Theme: DefaultStyle,
{ {
let view_timer = debug::view_time(id); let view_span = debug::view(id);
let view = application.view(id); let view = application.view(id);
view_timer.finish(); view_span.finish();
let layout_timer = debug::layout_time(id); let layout_span = debug::layout(id);
let user_interface = UserInterface::build(view, size, cache, renderer); let user_interface = UserInterface::build(view, size, cache, renderer);
layout_timer.finish(); layout_span.finish();
user_interface user_interface
} }
@ -968,9 +968,9 @@ fn update<A: Application, C, E: Executor>(
for message in messages.drain(..) { for message in messages.drain(..) {
debug::log_message(&message); debug::log_message(&message);
let update_timer = debug::update_time(); let update_span = debug::update();
let command = runtime.enter(|| application.update(message)); let command = runtime.enter(|| application.update(message));
update_timer.finish(); update_span.finish();
run_command( run_command(
application, application,