From c15fa6e43316be53039db584e7753dd2e431fca8 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 18 Apr 2024 19:43:39 +0200 Subject: [PATCH] Re-introduce Web examples (#3637) --- .cargo/config.toml | 3 - Cargo.toml | 71 +++++++++--- examples/control_flow.rs | 30 +++-- examples/util/tracing.rs | 27 +++++ examples/window.rs | 235 ++++++++++++++++++++++++++------------- run-wasm/Cargo.toml | 11 -- run-wasm/src/main.rs | 3 - 7 files changed, 259 insertions(+), 121 deletions(-) create mode 100644 examples/util/tracing.rs delete mode 100644 run-wasm/Cargo.toml delete mode 100644 run-wasm/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index b858add4..df87c4f8 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,3 @@ -[alias] -run-wasm = ["run", "--release", "--package", "run-wasm", "--"] - # Allow rust-analyzer and local `cargo doc` invocations to pick up unreleased changelog entries # # Note that these flags are (intentionally) not included when building from the downloaded crate. diff --git a/Cargo.toml b/Cargo.toml index 2846c35e..6920d059 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "winit" version = "0.29.15" -authors = ["The winit contributors", "Pierre Krieger "] +authors = [ + "The winit contributors", + "Pierre Krieger ", +] description = "Cross-platform window creation library." keywords = ["windowing"] readme = "README.md" @@ -45,7 +48,15 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"] -wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"] +wayland = [ + "wayland-client", + "wayland-backend", + "wayland-protocols", + "wayland-protocols-plasma", + "sctk", + "ahash", + "memmap2", +] wayland-dlopen = ["wayland-backend/dlopen"] wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"] wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"] @@ -66,8 +77,12 @@ bitflags = "2" cursor-icon = "1.1.0" dpi = { version = "0.1.1", path = "dpi" } rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true } -rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true } -rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } +rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = [ + "std", +], optional = true } +rwh_06 = { package = "raw-window-handle", version = "0.6", features = [ + "std", +], optional = true } serde = { workspace = true, optional = true } smol_str = "0.2.0" tracing = { version = "0.1.40", default_features = false } @@ -79,7 +94,12 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } winit = { path = ".", features = ["rwh_05"] } [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies] -softbuffer = { version = "0.3.0", default-features = false, features = ["x11", "x11-dlopen", "wayland", "wayland-dlopen"] } +softbuffer = { version = "0.3.0", default-features = false, features = [ + "x11", + "x11-dlopen", + "wayland", + "wayland-dlopen", +] } [target.'cfg(target_os = "android")'.dependencies] android-activity = "0.5.0" @@ -194,15 +214,35 @@ calloop = "0.12.3" libc = "0.2.64" memmap2 = { version = "0.9.0", optional = true } percent-encoding = { version = "2.0", optional = true } -rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] } -sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true } +rustix = { version = "0.38.4", default-features = false, features = [ + "std", + "system", + "thread", + "process", +] } +sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = [ + "calloop", +], optional = true } sctk-adwaita = { version = "0.9.0", default_features = false, optional = true } -wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true } +wayland-backend = { version = "0.3.0", default_features = false, features = [ + "client_system", +], optional = true } wayland-client = { version = "0.31.1", optional = true } -wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true } -wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true } +wayland-protocols = { version = "0.31.0", features = [ + "staging", +], optional = true } +wayland-protocols-plasma = { version = "0.2.0", features = [ + "client", +], optional = true } x11-dl = { version = "2.19.1", optional = true } -x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true } +x11rb = { version = "0.13.0", default-features = false, features = [ + "allow-unsafe-code", + "dl-libxcb", + "randr", + "resource_manager", + "xinput", + "xkb", +], optional = true } xkbcommon-dl = "0.4.2" [target.'cfg(target_os = "redox")'.dependencies] @@ -266,8 +306,8 @@ atomic-waker = "1" concurrent-queue = { version = "2", default-features = false } [target.'cfg(target_family = "wasm")'.dev-dependencies] -console_log = "1" -web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] } +console_error_panic_hook = "0.1" +tracing-web = "0.1" [[example]] doc-scrape-examples = true @@ -275,10 +315,7 @@ name = "window" [workspace] resolver = "2" -members = [ - "dpi", - "run-wasm", -] +members = ["dpi"] [workspace.package] rust-version = "1.70.0" diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 4d1049ac..76032db2 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -4,6 +4,7 @@ use std::thread; #[cfg(not(web_platform))] use std::time; +use ::tracing::{info, warn}; #[cfg(web_platform)] use web_time as time; @@ -15,6 +16,8 @@ use winit::window::{Window, WindowId}; #[path = "util/fill.rs"] mod fill; +#[path = "util/tracing.rs"] +mod tracing; const WAIT_TIME: time::Duration = time::Duration::from_millis(100); const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100); @@ -28,13 +31,16 @@ enum Mode { } fn main() -> Result<(), impl std::error::Error> { - tracing_subscriber::fmt::init(); + #[cfg(web_platform)] + console_error_panic_hook::set_once(); - println!("Press '1' to switch to Wait mode."); - println!("Press '2' to switch to WaitUntil mode."); - println!("Press '3' to switch to Poll mode."); - println!("Press 'R' to toggle request_redraw() calls."); - println!("Press 'Esc' to close the window."); + tracing::init(); + + info!("Press '1' to switch to Wait mode."); + info!("Press '2' to switch to WaitUntil mode."); + info!("Press '3' to switch to Poll mode."); + info!("Press 'R' to toggle request_redraw() calls."); + info!("Press 'Esc' to close the window."); let event_loop = EventLoop::new().unwrap(); @@ -53,7 +59,7 @@ struct ControlFlowDemo { impl ApplicationHandler for ControlFlowDemo { fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) { - println!("new_events: {cause:?}"); + info!("new_events: {cause:?}"); self.wait_cancelled = match cause { StartCause::WaitCancelled { .. } => self.mode == Mode::WaitUntil, @@ -74,7 +80,7 @@ impl ApplicationHandler for ControlFlowDemo { _window_id: WindowId, event: WindowEvent, ) { - println!("{event:?}"); + info!("{event:?}"); match event { WindowEvent::CloseRequested => { @@ -93,19 +99,19 @@ impl ApplicationHandler for ControlFlowDemo { // See the `key_binding` example Key::Character("1") => { self.mode = Mode::Wait; - println!("\nmode: {:?}\n", self.mode); + warn!("mode: {:?}", self.mode); } Key::Character("2") => { self.mode = Mode::WaitUntil; - println!("\nmode: {:?}\n", self.mode); + warn!("mode: {:?}", self.mode); } Key::Character("3") => { self.mode = Mode::Poll; - println!("\nmode: {:?}\n", self.mode); + warn!("mode: {:?}", self.mode); } Key::Character("r") => { self.request_redraw = !self.request_redraw; - println!("\nrequest_redraw: {}\n", self.request_redraw); + warn!("request_redraw: {}", self.request_redraw); } Key::Named(NamedKey::Escape) => { self.close_requested = true; diff --git a/examples/util/tracing.rs b/examples/util/tracing.rs new file mode 100644 index 00000000..f5c8f374 --- /dev/null +++ b/examples/util/tracing.rs @@ -0,0 +1,27 @@ +#[cfg(not(web_platform))] +pub fn init() { + use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(), + ) + .init(); +} + +#[cfg(web_platform)] +pub fn init() { + use tracing_subscriber::layer::SubscriberExt; + use tracing_subscriber::util::SubscriberInitExt; + + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .with_ansi(false) + .without_time() + .with_writer(tracing_web::MakeWebConsoleWriter::new()), + ) + .init(); +} diff --git a/examples/window.rs b/examples/window.rs index ab42f986..82f480ee 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -7,8 +7,8 @@ use std::fmt::Debug; use std::mem; #[cfg(not(any(android_platform, ios_platform)))] use std::num::NonZeroU32; -use std::path::Path; +use ::tracing::{error, info}; use cursor_icon::CursorIcon; #[cfg(not(any(android_platform, ios_platform)))] use rwh_05::HasRawDisplayHandle; @@ -34,10 +34,18 @@ use winit::platform::startup_notify::{ self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify, }; +#[path = "util/tracing.rs"] +mod tracing; + /// The amount of points to around the window for drag resize direction calculations. const BORDER_SIZE: f64 = 20.; fn main() -> Result<(), Box> { + #[cfg(web_platform)] + console_error_panic_hook::set_once(); + + tracing::init(); + let event_loop = EventLoop::::with_user_event().build()?; let _event_loop_proxy = event_loop.create_proxy(); @@ -46,7 +54,7 @@ fn main() -> Result<(), Box> { std::thread::spawn(move || { // Wake up the `event_loop` once every second and dispatch a custom event // from a different thread. - println!("Starting to send user event every second"); + info!("Starting to send user event every second"); loop { let _ = _event_loop_proxy.send_event(UserEvent::WakeUp); std::thread::sleep(std::time::Duration::from_secs(1)); @@ -88,11 +96,9 @@ impl Application { // by WM, and on Windows, you still have to account for screen scaling. Here we use 32px, // since it seems to work well enough in most cases. Be careful about going too high, or // you'll be bitten by the low-quality downscaling built into the WM. - let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/data/icon.png"); + let icon = load_icon(include_bytes!("data/icon.png")); - let icon = load_icon(Path::new(path)); - - println!("Loading cursor assets"); + info!("Loading cursor assets"); let custom_cursors = vec![ event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross.png"))), event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross2.png"))), @@ -124,7 +130,7 @@ impl Application { #[cfg(any(x11_platform, wayland_platform))] if let Some(token) = event_loop.read_token_from_env() { startup_notify::reset_activation_token_env(); - println!("Using token {:?} to activate a window", token); + info!("Using token {:?} to activate a window", token); window_attributes = window_attributes.with_activation_token(token); } @@ -133,6 +139,12 @@ impl Application { window_attributes = window_attributes.with_tabbing_identifier(&tab_id); } + #[cfg(web_platform)] + { + use winit::platform::web::WindowAttributesExtWebSys; + window_attributes = window_attributes.with_append(true); + } + let window = event_loop.create_window(window_attributes)?; #[cfg(ios_platform)] @@ -145,7 +157,7 @@ impl Application { let window_state = WindowState::new(self, window)?; let window_id = window_state.window.id(); - println!("Created new window with id={window_id:?}"); + info!("Created new window with id={window_id:?}"); self.windows.insert(window_id, window_state); Ok(window_id) } @@ -153,7 +165,7 @@ impl Application { fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) { // let cursor_position = self.cursor_position; let window = self.windows.get_mut(&window_id).unwrap(); - println!("Executing action: {action:?}"); + info!("Executing action: {action:?}"); match action { Action::CloseWindow => { let _ = self.windows.remove(&window_id); @@ -161,13 +173,13 @@ impl Application { Action::CreateNewWindow => { #[cfg(any(x11_platform, wayland_platform))] if let Err(err) = window.window.request_activation_token() { - println!("Failed to get activation token: {err}"); + info!("Failed to get activation token: {err}"); } else { return; } if let Err(err) = self.create_window(event_loop, None) { - eprintln!("Error creating new window: {err}"); + error!("Error creating new window: {err}"); } } Action::ToggleResizeIncrements => window.toggle_resize_increments(), @@ -180,6 +192,12 @@ impl Application { Action::Minimize => window.minimize(), Action::NextCursor => window.next_cursor(), Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors), + #[cfg(web_platform)] + Action::UrlCustomCursor => window.url_custom_cursor(event_loop), + #[cfg(web_platform)] + Action::AnimationCustomCursor => { + window.animation_custom_cursor(event_loop, &self.custom_cursors) + } Action::CycleCursorGrab => window.cycle_cursor_grab(), Action::DragWindow => window.drag_window(), Action::DragResizeWindow => window.drag_resize_window(), @@ -191,7 +209,7 @@ impl Application { Action::CreateNewTab => { let tab_id = window.window.tabbing_identifier(); if let Err(err) = self.create_window(event_loop, Some(tab_id)) { - eprintln!("Error creating new window: {err}"); + error!("Error creating new window: {err}"); } } Action::RequestResize => window.swap_dimensions(), @@ -199,7 +217,7 @@ impl Application { } fn dump_monitors(&self, event_loop: &ActiveEventLoop) { - println!("Monitors information"); + info!("Monitors information"); let primary_monitor = event_loop.primary_monitor(); for monitor in event_loop.available_monitors() { let intro = if primary_monitor.as_ref() == Some(&monitor) { @@ -209,30 +227,32 @@ impl Application { }; if let Some(name) = monitor.name() { - println!("{intro}: {name}"); + info!("{intro}: {name}"); } else { - println!("{intro}: [no name]"); + info!("{intro}: [no name]"); } let PhysicalSize { width, height } = monitor.size(); - print!(" Current mode: {width}x{height}"); - if let Some(m_hz) = monitor.refresh_rate_millihertz() { - println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000); - } else { - println!(); - } + info!( + " Current mode: {width}x{height}{}", + if let Some(m_hz) = monitor.refresh_rate_millihertz() { + format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000) + } else { + String::new() + } + ); let PhysicalPosition { x, y } = monitor.position(); - println!(" Position: {x},{y}"); + info!(" Position: {x},{y}"); - println!(" Scale factor: {}", monitor.scale_factor()); + info!(" Scale factor: {}", monitor.scale_factor()); - println!(" Available modes (width x height x bit-depth):"); + info!(" Available modes (width x height x bit-depth):"); for mode in monitor.video_modes() { let PhysicalSize { width, height } = mode.size(); let bits = mode.bit_depth(); let m_hz = mode.refresh_rate_millihertz(); - println!( + info!( " {width}x{height}x{bits} @ {}.{} Hz", m_hz / 1000, m_hz % 1000 @@ -260,9 +280,9 @@ impl Application { } fn print_help(&self) { - println!("Keyboard bindings:"); + info!("Keyboard bindings:"); for binding in KEY_BINDINGS { - println!( + info!( "{}{:<10} - {} ({})", modifiers_to_string(binding.mods), binding.trigger, @@ -270,9 +290,9 @@ impl Application { binding.action.help(), ); } - println!("Mouse bindings:"); + info!("Mouse bindings:"); for binding in MOUSE_BINDINGS { - println!( + info!( "{}{:<10} - {} ({})", modifiers_to_string(binding.mods), mouse_button_to_string(binding.trigger), @@ -285,7 +305,7 @@ impl Application { impl ApplicationHandler for Application { fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) { - println!("User event: {event:?}"); + info!("User event: {event:?}"); } fn window_event( @@ -305,40 +325,40 @@ impl ApplicationHandler for Application { } WindowEvent::Focused(focused) => { if focused { - println!("Window={window_id:?} focused"); + info!("Window={window_id:?} focused"); } else { - println!("Window={window_id:?} unfocused"); + info!("Window={window_id:?} unfocused"); } } WindowEvent::ScaleFactorChanged { scale_factor, .. } => { - println!("Window={window_id:?} changed scale to {scale_factor}"); + info!("Window={window_id:?} changed scale to {scale_factor}"); } WindowEvent::ThemeChanged(theme) => { - println!("Theme changed to {theme:?}"); + info!("Theme changed to {theme:?}"); window.set_theme(theme); } WindowEvent::RedrawRequested => { if let Err(err) = window.draw() { - eprintln!("Error drawing window: {err}"); + error!("Error drawing window: {err}"); } } WindowEvent::Occluded(occluded) => { window.set_occluded(occluded); } WindowEvent::CloseRequested => { - println!("Closing Window={window_id:?}"); + info!("Closing Window={window_id:?}"); self.windows.remove(&window_id); } WindowEvent::ModifiersChanged(modifiers) => { window.modifiers = modifiers.state(); - println!("Modifiers changed to {:?}", window.modifiers); + info!("Modifiers changed to {:?}", window.modifiers); } WindowEvent::MouseWheel { delta, .. } => match delta { MouseScrollDelta::LineDelta(x, y) => { - println!("Mouse wheel Line Delta: ({x},{y})"); + info!("Mouse wheel Line Delta: ({x},{y})"); } MouseScrollDelta::PixelDelta(px) => { - println!("Mouse wheel Pixel Delta: ({},{})", px.x, px.y); + info!("Mouse wheel Pixel Delta: ({},{})", px.x, px.y); } }, WindowEvent::KeyboardInput { @@ -372,11 +392,11 @@ impl ApplicationHandler for Application { } } WindowEvent::CursorLeft { .. } => { - println!("Cursor left Window={window_id:?}"); + info!("Cursor left Window={window_id:?}"); window.cursor_left(); } WindowEvent::CursorMoved { position, .. } => { - println!("Moved cursor to {position:?}"); + info!("Moved cursor to {position:?}"); window.cursor_moved(position); } WindowEvent::ActivationTokenDone { token: _token, .. } => { @@ -384,40 +404,40 @@ impl ApplicationHandler for Application { { startup_notify::set_activation_token_env(_token); if let Err(err) = self.create_window(event_loop, None) { - eprintln!("Error creating new window: {err}"); + error!("Error creating new window: {err}"); } } } WindowEvent::Ime(event) => match event { - Ime::Enabled => println!("IME enabled for Window={window_id:?}"), + Ime::Enabled => info!("IME enabled for Window={window_id:?}"), Ime::Preedit(text, caret_pos) => { - println!("Preedit: {}, with caret at {:?}", text, caret_pos); + info!("Preedit: {}, with caret at {:?}", text, caret_pos); } Ime::Commit(text) => { - println!("Committed: {}", text); + info!("Committed: {}", text); } - Ime::Disabled => println!("IME disabled for Window={window_id:?}"), + Ime::Disabled => info!("IME disabled for Window={window_id:?}"), }, WindowEvent::PinchGesture { delta, .. } => { window.zoom += delta; let zoom = window.zoom; if delta > 0.0 { - println!("Zoomed in {delta:.5} (now: {zoom:.5})"); + info!("Zoomed in {delta:.5} (now: {zoom:.5})"); } else { - println!("Zoomed out {delta:.5} (now: {zoom:.5})"); + info!("Zoomed out {delta:.5} (now: {zoom:.5})"); } } WindowEvent::RotationGesture { delta, .. } => { window.rotated += delta; let rotated = window.rotated; if delta > 0.0 { - println!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})"); + info!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})"); } else { - println!("Rotated clockwise {delta:.5} (now: {rotated:.5})"); + info!("Rotated clockwise {delta:.5} (now: {rotated:.5})"); } } WindowEvent::DoubleTapGesture { .. } => { - println!("Smart zoom"); + info!("Smart zoom"); } WindowEvent::TouchpadPressure { .. } | WindowEvent::HoveredFileCancelled @@ -438,11 +458,11 @@ impl ApplicationHandler for Application { device_id: DeviceId, event: DeviceEvent, ) { - println!("Device {device_id:?} event: {event:?}"); + info!("Device {device_id:?} event: {event:?}"); } fn resumed(&mut self, event_loop: &ActiveEventLoop) { - println!("Resumed the event loop"); + info!("Resumed the event loop"); self.dump_monitors(event_loop); // Create initial window. @@ -454,7 +474,7 @@ impl ApplicationHandler for Application { fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { if self.windows.is_empty() { - println!("No windows left, exiting..."); + info!("No windows left, exiting..."); event_loop.exit(); } } @@ -509,7 +529,7 @@ impl WindowState { let surface = unsafe { Surface::new(app.context.as_ref().unwrap(), &window)? }; let theme = window.theme().unwrap_or(Theme::Dark); - println!("Theme: {theme:?}"); + info!("Theme: {theme:?}"); let named_idx = 0; window.set_cursor(CURSORS[named_idx]); @@ -596,7 +616,7 @@ impl WindowState { Some(_) => None, None => Some(LogicalSize::new(25.0, 25.0)), }; - println!("Had increments: {}", new_increments.is_none()); + info!("Had increments: {}", new_increments.is_none()); self.window.set_resize_increments(new_increments); } @@ -618,9 +638,9 @@ impl WindowState { CursorGrabMode::Confined => CursorGrabMode::Locked, CursorGrabMode::Locked => CursorGrabMode::None, }; - println!("Changing cursor grab mode to {:?}", self.cursor_grab); + info!("Changing cursor grab mode to {:?}", self.cursor_grab); if let Err(err) = self.window.set_cursor_grab(self.cursor_grab) { - eprintln!("Error setting cursor grab: {err}"); + error!("Error setting cursor grab: {err}"); } } @@ -632,7 +652,7 @@ impl WindowState { OptionAsAlt::OnlyRight => OptionAsAlt::Both, OptionAsAlt::Both => OptionAsAlt::None, }; - println!("Setting option as alt {:?}", self.option_as_alt); + info!("Setting option as alt {:?}", self.option_as_alt); self.window.set_option_as_alt(self.option_as_alt); } @@ -642,23 +662,23 @@ impl WindowState { let mut inner_size = old_inner_size; mem::swap(&mut inner_size.width, &mut inner_size.height); - println!("Requesting resize from {old_inner_size:?} to {inner_size:?}"); + info!("Requesting resize from {old_inner_size:?} to {inner_size:?}"); if let Some(new_inner_size) = self.window.request_inner_size(inner_size) { if old_inner_size == new_inner_size { - println!("Inner size change got ignored"); + info!("Inner size change got ignored"); } else { self.resize(new_inner_size); } } else { - println!("Request inner size is asynchronous"); + info!("Request inner size is asynchronous"); } } /// Pick the next cursor. fn next_cursor(&mut self) { self.named_idx = (self.named_idx + 1) % CURSORS.len(); - println!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]); + info!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]); self.window .set_cursor(Cursor::Icon(CURSORS[self.named_idx])); } @@ -670,9 +690,38 @@ impl WindowState { self.window.set_cursor(cursor); } + /// Custom cursor from an URL. + #[cfg(web_platform)] + fn url_custom_cursor(&mut self, event_loop: &ActiveEventLoop) { + let cursor = event_loop.create_custom_cursor(url_custom_cursor()); + + self.window.set_cursor(cursor); + } + + /// Custom cursor from a URL. + #[cfg(web_platform)] + fn animation_custom_cursor( + &mut self, + event_loop: &ActiveEventLoop, + custom_cursors: &[CustomCursor], + ) { + use std::time::Duration; + use winit::platform::web::CustomCursorExtWebSys; + + let cursors = vec![ + custom_cursors[0].clone(), + custom_cursors[1].clone(), + event_loop.create_custom_cursor(url_custom_cursor()), + ]; + let cursor = CustomCursor::from_animation(Duration::from_secs(3), cursors).unwrap(); + let cursor = event_loop.create_custom_cursor(cursor); + + self.window.set_cursor(cursor); + } + /// Resize the window to the new size. fn resize(&mut self, size: PhysicalSize) { - println!("Resized to {size:?}"); + info!("Resized to {size:?}"); #[cfg(not(any(android_platform, ios_platform)))] { let (width, height) = match (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) @@ -703,9 +752,9 @@ impl WindowState { /// Drag the window. fn drag_window(&self) { if let Err(err) = self.window.drag_window() { - println!("Error starting window drag: {err}"); + info!("Error starting window drag: {err}"); } else { - println!("Dragging window Window={:?}", self.window.id()); + info!("Dragging window Window={:?}", self.window.id()); } } @@ -714,7 +763,7 @@ impl WindowState { let position = match self.cursor_position { Some(position) => position, None => { - println!("Drag-resize requires cursor to be inside the window"); + info!("Drag-resize requires cursor to be inside the window"); return; } }; @@ -753,9 +802,9 @@ impl WindowState { }; if let Err(err) = self.window.drag_resize_window(direction) { - println!("Error starting window drag-resize: {err}"); + info!("Error starting window drag-resize: {err}"); } else { - println!("Drag-resizing window Window={:?}", self.window.id()); + info!("Drag-resizing window Window={:?}", self.window.id()); } } @@ -771,7 +820,7 @@ impl WindowState { #[cfg(not(any(android_platform, ios_platform)))] fn draw(&mut self) -> Result<(), Box> { if self.occluded { - println!("Skipping drawing occluded window={:?}", self.window.id()); + info!("Skipping drawing occluded window={:?}", self.window.id()); return Ok(()); } @@ -792,7 +841,7 @@ impl WindowState { #[cfg(any(android_platform, ios_platform))] fn draw(&mut self) -> Result<(), Box> { - println!("Drawing but without rendering..."); + info!("Drawing but without rendering..."); Ok(()) } } @@ -831,6 +880,10 @@ enum Action { Minimize, NextCursor, NextCustomCursor, + #[cfg(web_platform)] + UrlCustomCursor, + #[cfg(web_platform)] + AnimationCustomCursor, CycleCursorGrab, PrintHelp, DragWindow, @@ -858,6 +911,10 @@ impl Action { Action::ToggleResizeIncrements => "Use resize increments when resizing window", Action::NextCursor => "Advance the cursor to the next value", Action::NextCustomCursor => "Advance custom cursor to the next value", + #[cfg(web_platform)] + Action::UrlCustomCursor => "Custom cursor from an URL", + #[cfg(web_platform)] + Action::AnimationCustomCursor => "Custom cursor from an animation", Action::CycleCursorGrab => "Cycle through cursor grab mode", Action::PrintHelp => "Print help", Action::DragWindow => "Start window drag", @@ -886,11 +943,27 @@ fn decode_cursor(bytes: &[u8]) -> CustomCursorSource { CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap() } -fn load_icon(path: &Path) -> Icon { +#[cfg(web_platform)] +fn url_custom_cursor() -> CustomCursorSource { + use std::sync::atomic::{AtomicU64, Ordering}; + + use winit::platform::web::CustomCursorExtWebSys; + + static URL_COUNTER: AtomicU64 = AtomicU64::new(0); + + CustomCursor::from_url( + format!( + "https://picsum.photos/128?random={}", + URL_COUNTER.fetch_add(1, Ordering::Relaxed) + ), + 64, + 64, + ) +} + +fn load_icon(bytes: &[u8]) -> Icon { let (icon_rgba, icon_width, icon_height) = { - let image = image::open(path) - .expect("Failed to open icon path") - .into_rgba8(); + let image = image::load_from_memory(bytes).unwrap().into_rgba8(); let (width, height) = image.dimensions(); let rgba = image.into_raw(); (rgba, width, height) @@ -983,6 +1056,18 @@ const KEY_BINDINGS: &[Binding<&'static str>] = &[ // C. Binding::new("C", ModifiersState::CONTROL, Action::NextCursor), Binding::new("C", ModifiersState::ALT, Action::NextCustomCursor), + #[cfg(web_platform)] + Binding::new( + "C", + ModifiersState::CONTROL.union(ModifiersState::SHIFT), + Action::UrlCustomCursor, + ), + #[cfg(web_platform)] + Binding::new( + "C", + ModifiersState::ALT.union(ModifiersState::SHIFT), + Action::AnimationCustomCursor, + ), Binding::new("Z", ModifiersState::CONTROL, Action::ToggleCursorVisibility), #[cfg(macos_platform)] Binding::new("T", ModifiersState::SUPER, Action::CreateNewTab), diff --git a/run-wasm/Cargo.toml b/run-wasm/Cargo.toml deleted file mode 100644 index ce7e831e..00000000 --- a/run-wasm/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "run-wasm" -version = "0.1.0" -rust-version.workspace = true -repository.workspace = true -license.workspace = true -edition.workspace = true -publish = false - -[dependencies] -cargo-run-wasm = "0.2.0" diff --git a/run-wasm/src/main.rs b/run-wasm/src/main.rs deleted file mode 100644 index 6961358d..00000000 --- a/run-wasm/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - cargo_run_wasm::run_wasm_with_css("body { margin: 0px; }"); -}