summaryrefslogtreecommitdiffstats
path: root/winit
diff options
context:
space:
mode:
Diffstat (limited to 'winit')
-rw-r--r--winit/Cargo.toml3
-rw-r--r--winit/src/clipboard.rs41
-rw-r--r--winit/src/conversion.rs22
-rw-r--r--winit/src/program.rs199
-rw-r--r--winit/src/program/window_manager.rs8
-rw-r--r--winit/src/system.rs2
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))
})
}