diff options
Diffstat (limited to 'winit')
-rw-r--r-- | winit/Cargo.toml | 3 | ||||
-rw-r--r-- | winit/src/clipboard.rs | 41 | ||||
-rw-r--r-- | winit/src/conversion.rs | 22 | ||||
-rw-r--r-- | winit/src/program.rs | 199 | ||||
-rw-r--r-- | winit/src/program/window_manager.rs | 8 | ||||
-rw-r--r-- | winit/src/system.rs | 2 |
6 files changed, 200 insertions, 75 deletions
diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 68368aa1..f5a47952 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -33,7 +33,6 @@ log.workspace = true rustc-hash.workspace = true thiserror.workspace = true tracing.workspace = true -wasm-bindgen-futures.workspace = true window_clipboard.workspace = true winit.workspace = true @@ -46,4 +45,4 @@ winapi.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] web-sys.workspace = true web-sys.features = ["Document", "Window"] - +wasm-bindgen-futures.workspace = true diff --git a/winit/src/clipboard.rs b/winit/src/clipboard.rs index 5237ca01..d54a1fe0 100644 --- a/winit/src/clipboard.rs +++ b/winit/src/clipboard.rs @@ -1,6 +1,8 @@ //! Access the clipboard. use crate::core::clipboard::Kind; +use std::sync::Arc; +use winit::window::{Window, WindowId}; /// A buffer for short-term storage and transfer within and between /// applications. @@ -10,18 +12,33 @@ pub struct Clipboard { } enum State { - Connected(window_clipboard::Clipboard), + Connected { + clipboard: window_clipboard::Clipboard, + // Held until drop to satisfy the safety invariants of + // `window_clipboard::Clipboard`. + // + // Note that the field ordering is load-bearing. + #[allow(dead_code)] + window: Arc<Window>, + }, Unavailable, } impl Clipboard { /// Creates a new [`Clipboard`] for the given window. - pub fn connect(window: &winit::window::Window) -> Clipboard { + pub fn connect(window: Arc<Window>) -> Clipboard { + // SAFETY: The window handle will stay alive throughout the entire + // lifetime of the `window_clipboard::Clipboard` because we hold + // the `Arc<Window>` together with `State`, and enum variant fields + // get dropped in declaration order. #[allow(unsafe_code)] - let state = unsafe { window_clipboard::Clipboard::connect(window) } - .ok() - .map(State::Connected) - .unwrap_or(State::Unavailable); + let clipboard = + unsafe { window_clipboard::Clipboard::connect(&window) }; + + let state = match clipboard { + Ok(clipboard) => State::Connected { clipboard, window }, + Err(_) => State::Unavailable, + }; Clipboard { state } } @@ -37,7 +54,7 @@ impl Clipboard { /// Reads the current content of the [`Clipboard`] as text. pub fn read(&self, kind: Kind) -> Option<String> { match &self.state { - State::Connected(clipboard) => match kind { + State::Connected { clipboard, .. } => match kind { Kind::Standard => clipboard.read().ok(), Kind::Primary => clipboard.read_primary().and_then(Result::ok), }, @@ -48,7 +65,7 @@ impl Clipboard { /// Writes the given text contents to the [`Clipboard`]. pub fn write(&mut self, kind: Kind, contents: String) { match &mut self.state { - State::Connected(clipboard) => { + State::Connected { clipboard, .. } => { let result = match kind { Kind::Standard => clipboard.write(contents), Kind::Primary => { @@ -66,6 +83,14 @@ impl Clipboard { State::Unavailable => {} } } + + /// Returns the identifier of the window used to create the [`Clipboard`], if any. + pub fn window_id(&self) -> Option<WindowId> { + match &self.state { + State::Connected { window, .. } => Some(window.id()), + State::Unavailable => None, + } + } } impl crate::core::Clipboard for Clipboard { diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 0ed10c88..aaaca1a9 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -79,6 +79,10 @@ pub fn window_attributes( attributes = attributes .with_skip_taskbar(settings.platform_specific.skip_taskbar); + + attributes = attributes.with_undecorated_shadow( + settings.platform_specific.undecorated_shadow, + ); } #[cfg(target_os = "macos")] @@ -101,10 +105,14 @@ pub fn window_attributes( { use winit::platform::x11::WindowAttributesExtX11; - attributes = attributes.with_name( - &settings.platform_specific.application_id, - &settings.platform_specific.application_id, - ); + attributes = attributes + .with_override_redirect( + settings.platform_specific.override_redirect, + ) + .with_name( + &settings.platform_specific.application_id, + &settings.platform_specific.application_id, + ); } #[cfg(feature = "wayland")] { @@ -132,10 +140,10 @@ pub fn window_event( WindowEvent::Resized(new_size) => { let logical_size = new_size.to_logical(scale_factor); - Some(Event::Window(window::Event::Resized { + Some(Event::Window(window::Event::Resized(Size { width: logical_size.width, height: logical_size.height, - })) + }))) } WindowEvent::CloseRequested => { Some(Event::Window(window::Event::CloseRequested)) @@ -277,7 +285,7 @@ pub fn window_event( let winit::dpi::LogicalPosition { x, y } = position.to_logical(scale_factor); - Some(Event::Window(window::Event::Moved { x, y })) + Some(Event::Window(window::Event::Moved(Point::new(x, y)))) } _ => None, } diff --git a/winit/src/program.rs b/winit/src/program.rs index d55aedf1..52d8eb5f 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -202,12 +202,25 @@ where }; let (program, task) = runtime.enter(|| P::new(flags)); + let is_daemon = window_settings.is_none(); - if let Some(stream) = task.into_stream() { + let task = if let Some(window_settings) = window_settings { + let mut task = Some(task); + + let (_id, open) = runtime::window::open(window_settings); + + open.then(move |_| task.take().unwrap_or(Task::none())) + } else { + task + }; + + if let Some(stream) = runtime::task::into_stream(task) { runtime.run(stream); } - runtime.track(program.subscription().map(Action::Output).into_recipes()); + runtime.track(subscription::into_recipes( + program.subscription().map(Action::Output), + )); let (boot_sender, boot_receiver) = oneshot::channel(); let (event_sender, event_receiver) = mpsc::unbounded(); @@ -221,6 +234,7 @@ where boot_receiver, event_receiver, control_sender, + is_daemon, )); let context = task::Context::from_waker(task::noop_waker_ref()); @@ -229,7 +243,7 @@ where instance: std::pin::Pin<Box<F>>, context: task::Context<'static>, id: Option<String>, - boot: Option<BootConfig<Message, C>>, + boot: Option<BootConfig<C>>, sender: mpsc::UnboundedSender<Event<Action<Message>>>, receiver: mpsc::UnboundedReceiver<Control>, error: Option<Error>, @@ -240,11 +254,9 @@ where queued_events: Vec<Event<Action<Message>>>, } - struct BootConfig<Message: 'static, C> { - proxy: Proxy<Message>, + struct BootConfig<C> { sender: oneshot::Sender<Boot<C>>, fonts: Vec<Cow<'static, [u8]>>, - window_settings: Option<window::Settings>, graphics_settings: graphics::Settings, } @@ -253,10 +265,8 @@ where context, id: settings.id, boot: Some(BootConfig { - proxy, sender: boot_sender, fonts: settings.fonts, - window_settings, graphics_settings, }), sender: event_sender, @@ -278,10 +288,8 @@ where { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { let Some(BootConfig { - mut proxy, sender, fonts, - window_settings, graphics_settings, }) = self.boot.take() else { @@ -299,8 +307,6 @@ where } }; - let clipboard = Clipboard::connect(&window); - let finish_boot = async move { let mut compositor = C::new(graphics_settings, window.clone()).await?; @@ -310,27 +316,10 @@ where } sender - .send(Boot { - compositor, - clipboard, - window: window.id(), - is_daemon: window_settings.is_none(), - }) + .send(Boot { compositor }) .ok() .expect("Send boot event"); - if let Some(window_settings) = window_settings { - let (sender, _receiver) = oneshot::channel(); - - proxy.send_action(Action::Window( - runtime::window::Action::Open( - window::Id::unique(), - window_settings, - sender, - ), - )); - } - Ok::<_, graphics::Error>(()) }; @@ -420,6 +409,23 @@ where ); } + fn received_url( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + url: String, + ) { + self.process_event( + event_loop, + Event::EventLoopAwakened( + winit::event::Event::PlatformSpecific( + winit::event::PlatformSpecific::MacOS( + winit::event::MacOS::ReceivedUrl(url), + ), + ), + ), + ); + } + fn about_to_wait( &mut self, event_loop: &winit::event_loop::ActiveEventLoop, @@ -488,10 +494,13 @@ where settings, title, monitor, + on_open, } => { let exit_on_close_request = settings.exit_on_close_request; + let visible = settings.visible; + #[cfg(target_arch = "wasm32")] let target = settings.platform_specific.target.clone(); @@ -505,7 +514,8 @@ where .or(event_loop .primary_monitor()), self.id.clone(), - ), + ) + .with_visible(false), ) .expect("Create window"); @@ -561,6 +571,8 @@ where id, window, exit_on_close_request, + make_visible: visible, + on_open, }, ); } @@ -600,20 +612,21 @@ where struct Boot<C> { compositor: C, - clipboard: Clipboard, - window: winit::window::WindowId, - is_daemon: bool, } +#[derive(Debug)] enum Event<Message: 'static> { WindowCreated { id: window::Id, window: winit::window::Window, exit_on_close_request: bool, + make_visible: bool, + on_open: oneshot::Sender<window::Id>, }, EventLoopAwakened(winit::event::Event<Message>), } +#[derive(Debug)] enum Control { ChangeFlow(winit::event_loop::ControlFlow), Exit, @@ -622,6 +635,7 @@ enum Control { settings: window::Settings, title: String, monitor: Option<winit::monitor::MonitorHandle>, + on_open: oneshot::Sender<window::Id>, }, } @@ -630,9 +644,10 @@ async fn run_instance<P, C>( mut runtime: Runtime<P::Executor, Proxy<P::Message>, Action<P::Message>>, mut proxy: Proxy<P::Message>, mut debug: Debug, - mut boot: oneshot::Receiver<Boot<C>>, + boot: oneshot::Receiver<Boot<C>>, mut event_receiver: mpsc::UnboundedReceiver<Event<Action<P::Message>>>, mut control_sender: mpsc::UnboundedSender<Control>, + is_daemon: bool, ) where P: Program + 'static, C: Compositor<Renderer = P::Renderer> + 'static, @@ -641,14 +656,10 @@ async fn run_instance<P, C>( use winit::event; use winit::event_loop::ControlFlow; - let Boot { - mut compositor, - mut clipboard, - window: boot_window, - is_daemon, - } = boot.try_recv().ok().flatten().expect("Receive boot"); + let Boot { mut compositor } = boot.await.expect("Receive boot"); let mut window_manager = WindowManager::new(); + let mut is_window_opening = !is_daemon; let mut events = Vec::new(); let mut messages = Vec::new(); @@ -656,15 +667,29 @@ async fn run_instance<P, C>( let mut ui_caches = FxHashMap::default(); let mut user_interfaces = ManuallyDrop::new(FxHashMap::default()); + let mut clipboard = Clipboard::unconnected(); debug.startup_finished(); - 'main: while let Some(event) = event_receiver.next().await { + loop { + // Empty the queue if possible + let event = if let Ok(event) = event_receiver.try_next() { + event + } else { + event_receiver.next().await + }; + + let Some(event) = event else { + break; + }; + match event { Event::WindowCreated { id, window, exit_on_close_request, + make_visible, + on_open, } => { let window = window_manager.insert( id, @@ -689,6 +714,10 @@ async fn run_instance<P, C>( ); let _ = ui_caches.insert(id, user_interface::Cache::default()); + if make_visible { + window.raw.set_visible(true); + } + events.push(( id, core::Event::Window(window::Event::Opened { @@ -696,6 +725,13 @@ async fn run_instance<P, C>( size: window.size(), }), )); + + if clipboard.window_id().is_none() { + clipboard = Clipboard::connect(window.raw.clone()); + } + + let _ = on_open.send(id); + is_window_opening = false; } Event::EventLoopAwakened(event) => { match event { @@ -725,6 +761,7 @@ async fn run_instance<P, C>( action, &program, &mut compositor, + &mut events, &mut messages, &mut clipboard, &mut control_sender, @@ -732,6 +769,7 @@ async fn run_instance<P, C>( &mut user_interfaces, &mut window_manager, &mut ui_caches, + &mut is_window_opening, ); actions += 1; } @@ -916,10 +954,14 @@ async fn run_instance<P, C>( window_event, winit::event::WindowEvent::Destroyed ) - && window_id != boot_window + && !is_window_opening && window_manager.is_empty() { - break 'main; + control_sender + .start_send(Control::Exit) + .expect("Send control action"); + + continue; } let Some((id, window)) = @@ -933,14 +975,22 @@ async fn run_instance<P, C>( winit::event::WindowEvent::CloseRequested ) && window.exit_on_close_request { - let _ = window_manager.remove(id); - let _ = user_interfaces.remove(&id); - let _ = ui_caches.remove(&id); - - events.push(( - id, - core::Event::Window(window::Event::Closed), - )); + run_action( + Action::Window(runtime::window::Action::Close( + id, + )), + &program, + &mut compositor, + &mut events, + &mut messages, + &mut clipboard, + &mut control_sender, + &mut debug, + &mut user_interfaces, + &mut window_manager, + &mut ui_caches, + &mut is_window_opening, + ); } else { window.state.update( &window.raw, @@ -1114,19 +1164,20 @@ fn update<P: Program, E: Executor>( let task = runtime.enter(|| program.update(message)); debug.update_finished(); - if let Some(stream) = task.into_stream() { + if let Some(stream) = runtime::task::into_stream(task) { runtime.run(stream); } } let subscription = program.subscription(); - runtime.track(subscription.map(Action::Output).into_recipes()); + runtime.track(subscription::into_recipes(subscription.map(Action::Output))); } fn run_action<P, C>( action: Action<P::Message>, program: &P, compositor: &mut C, + events: &mut Vec<(window::Id, core::Event)>, messages: &mut Vec<P::Message>, clipboard: &mut Clipboard, control_sender: &mut mpsc::UnboundedSender<Control>, @@ -1137,6 +1188,7 @@ fn run_action<P, C>( >, window_manager: &mut WindowManager<P, C>, ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>, + is_window_opening: &mut bool, ) where P: Program, C: Compositor<Renderer = P::Renderer> + 'static, @@ -1168,14 +1220,30 @@ fn run_action<P, C>( settings, title: program.title(id), monitor, + on_open: channel, }) .expect("Send control action"); - let _ = channel.send(id); + *is_window_opening = true; } window::Action::Close(id) => { - let _ = window_manager.remove(id); let _ = ui_caches.remove(&id); + let _ = interfaces.remove(&id); + + if let Some(window) = window_manager.remove(id) { + if clipboard.window_id() == Some(window.raw.id()) { + *clipboard = window_manager + .first() + .map(|window| window.raw.clone()) + .map(Clipboard::connect) + .unwrap_or_else(Clipboard::unconnected); + } + + events.push(( + id, + core::Event::Window(core::window::Event::Closed), + )); + } } window::Action::GetOldest(channel) => { let id = @@ -1235,7 +1303,7 @@ fn run_action<P, C>( } } window::Action::GetPosition(id, channel) => { - if let Some(window) = window_manager.get_mut(id) { + if let Some(window) = window_manager.get(id) { let position = window .raw .inner_position() @@ -1250,6 +1318,13 @@ fn run_action<P, C>( let _ = channel.send(position); } } + window::Action::GetScaleFactor(id, channel) => { + if let Some(window) = window_manager.get_mut(id) { + let scale_factor = window.raw.scale_factor(); + + let _ = channel.send(scale_factor as f32); + } + } window::Action::Move(id, position) => { if let Some(window) = window_manager.get_mut(id) { window.raw.set_outer_position( @@ -1360,6 +1435,16 @@ fn run_action<P, C>( )); } } + window::Action::EnableMousePassthrough(id) => { + if let Some(window) = window_manager.get_mut(id) { + let _ = window.raw.set_cursor_hittest(false); + } + } + window::Action::DisableMousePassthrough(id) => { + if let Some(window) = window_manager.get_mut(id) { + let _ = window.raw.set_cursor_hittest(true); + } + } }, Action::System(action) => match action { system::Action::QueryInformation(_channel) => { diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index fcbf79f6..3d22e155 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -74,12 +74,20 @@ where self.entries.is_empty() } + pub fn first(&self) -> Option<&Window<P, C>> { + self.entries.first_key_value().map(|(_id, window)| window) + } + pub fn iter_mut( &mut self, ) -> impl Iterator<Item = (Id, &mut Window<P, C>)> { self.entries.iter_mut().map(|(k, v)| (*k, v)) } + pub fn get(&self, id: Id) -> Option<&Window<P, C>> { + self.entries.get(&id) + } + pub fn get_mut(&mut self, id: Id) -> Option<&mut Window<P, C>> { self.entries.get_mut(&id) } diff --git a/winit/src/system.rs b/winit/src/system.rs index 7997f311..361135be 100644 --- a/winit/src/system.rs +++ b/winit/src/system.rs @@ -5,7 +5,7 @@ use crate::runtime::{self, Task}; /// Query for available system information. pub fn fetch_information() -> Task<Information> { - Task::oneshot(|channel| { + runtime::task::oneshot(|channel| { runtime::Action::System(Action::QueryInformation(channel)) }) } |