Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre

# Unreleased

- `EventLoopProxy` now implements `Sync` as well as `Send`.
- `EventLoopProxy` now implements `Send` on WebAssembly.
- Build docs on `docs.rs` for iOS and Android as well.
- **Breaking:** Removed the `WindowAttributes` struct, since all its functionality is accessible from `WindowBuilder`.
- Added `WindowBuilder::transparent` getter to check if the user set `transparent` attribute.
Expand Down
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,10 @@ features = [
'WheelEvent'
]

[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen]
version = "0.2.45"
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2.45"
wasm-bindgen-futures = "0.4.31"
async-channel = "1.6.1"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
console_log = "0.2"
Expand Down
16 changes: 11 additions & 5 deletions src/platform_impl/ios/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
fmt::{self, Debug},
marker::PhantomData,
mem, ptr,
sync::mpsc::{self, Receiver, Sender},
sync::mpsc::{self, Receiver, SyncSender},
};

use crate::{
Expand Down Expand Up @@ -48,7 +48,7 @@ pub enum EventProxy {

pub struct EventLoopWindowTarget<T: 'static> {
receiver: Receiver<T>,
sender_to_clone: Sender<T>,
sender_to_clone: SyncSender<T>,
}

impl<T: 'static> EventLoopWindowTarget<T> {
Expand Down Expand Up @@ -86,7 +86,7 @@ impl<T: 'static> EventLoop<T> {
view::create_delegate_class();
}

let (sender_to_clone, receiver) = mpsc::channel();
let (sender_to_clone, receiver) = mpsc::sync_channel(10);

// this line sets up the main run loop before `UIApplicationMain`
setup_control_flow_observers();
Expand Down Expand Up @@ -148,11 +148,17 @@ impl<T: 'static> EventLoop<T> {
}

pub struct EventLoopProxy<T> {
sender: Sender<T>,
sender: SyncSender<T>,
source: CFRunLoopSourceRef,
}

unsafe impl<T: Send> Send for EventLoopProxy<T> {}
// Looking at the source code for `CFRunLoopSourceSignal` (https://github.com/opensource-apple/CF/blob/3cc41a76b1491f50813e28a4ec09954ffa359e6f/CFRunLoop.c#L3418-L3425),
// it locks the source before doing anything, so I think it should be fine to
// use from behind a reference.
//
// `SyncSender` is already `Sync`, so that's not an issue.
unsafe impl<T: Send> Sync for EventLoopProxy<T> {}

impl<T> Clone for EventLoopProxy<T> {
fn clone(&self) -> EventLoopProxy<T> {
Expand All @@ -170,7 +176,7 @@ impl<T> Drop for EventLoopProxy<T> {
}

impl<T> EventLoopProxy<T> {
fn new(sender: Sender<T>) -> EventLoopProxy<T> {
fn new(sender: SyncSender<T>) -> EventLoopProxy<T> {
unsafe {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *mut c_void) {}
Expand Down
4 changes: 2 additions & 2 deletions src/platform_impl/linux/wayland/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub struct EventLoop<T: 'static> {
pending_user_events: Rc<RefCell<Vec<T>>>,

/// Sender of user events.
user_events_sender: calloop::channel::Sender<T>,
user_events_sender: calloop::channel::SyncSender<T>,

/// Dispatcher of Wayland events.
pub wayland_dispatcher: WinitDispatcher,
Expand Down Expand Up @@ -139,7 +139,7 @@ impl<T: 'static> EventLoop<T> {
// A source of user events.
let pending_user_events = Rc::new(RefCell::new(Vec::new()));
let pending_user_events_clone = pending_user_events.clone();
let (user_events_sender, user_events_channel) = calloop::channel::channel();
let (user_events_sender, user_events_channel) = calloop::channel::sync_channel(10);

// User events channel.
event_loop
Expand Down
6 changes: 3 additions & 3 deletions src/platform_impl/linux/wayland/event_loop/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

use std::sync::mpsc::SendError;

use sctk::reexports::calloop::channel::Sender;
use sctk::reexports::calloop::channel::SyncSender;

use crate::event_loop::EventLoopClosed;

/// A handle that can be sent across the threads and used to wake up the `EventLoop`.
pub struct EventLoopProxy<T: 'static> {
user_events_sender: Sender<T>,
user_events_sender: SyncSender<T>,
}

impl<T: 'static> Clone for EventLoopProxy<T> {
Expand All @@ -20,7 +20,7 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
}

impl<T: 'static> EventLoopProxy<T> {
pub fn new(user_events_sender: Sender<T>) -> Self {
pub fn new(user_events_sender: SyncSender<T>) -> Self {
Self { user_events_sender }
}

Expand Down
8 changes: 4 additions & 4 deletions src/platform_impl/linux/x11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use std::{
ptr,
rc::Rc,
slice,
sync::mpsc::{Receiver, Sender, TryRecvError},
sync::mpsc::{Receiver, Sender, SyncSender, TryRecvError},
sync::{mpsc, Arc, Weak},
time::{Duration, Instant},
};
Expand Down Expand Up @@ -118,12 +118,12 @@ pub struct EventLoop<T: 'static> {
event_processor: EventProcessor<T>,
redraw_receiver: PeekableReceiver<WindowId>,
user_receiver: PeekableReceiver<T>, //waker.wake needs to be called whenever something gets sent
user_sender: Sender<T>,
user_sender: SyncSender<T>,
target: Rc<RootELW<T>>,
}

pub struct EventLoopProxy<T: 'static> {
user_sender: Sender<T>,
user_sender: SyncSender<T>,
waker: Arc<Waker>,
}

Expand Down Expand Up @@ -230,7 +230,7 @@ impl<T: 'static> EventLoop<T> {
.register(&mut SourceFd(&xconn.x11_fd), X_TOKEN, Interest::READABLE)
.unwrap();

let (user_sender, user_channel) = std::sync::mpsc::channel();
let (user_sender, user_channel) = std::sync::mpsc::sync_channel(10);
let (redraw_sender, redraw_channel) = std::sync::mpsc::channel();

let window_target = EventLoopWindowTarget {
Expand Down
14 changes: 10 additions & 4 deletions src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ impl PanicInfo {
}

pub struct EventLoopWindowTarget<T: 'static> {
pub sender: mpsc::Sender<T>, // this is only here to be cloned elsewhere
pub sender: mpsc::SyncSender<T>, // this is only here to be cloned elsewhere
pub receiver: mpsc::Receiver<T>,
}

impl<T> Default for EventLoopWindowTarget<T> {
fn default() -> Self {
let (sender, receiver) = mpsc::channel();
let (sender, receiver) = mpsc::sync_channel(10);
EventLoopWindowTarget { sender, receiver }
}
}
Expand Down Expand Up @@ -282,11 +282,17 @@ pub fn stop_app_on_panic<F: FnOnce() -> R + UnwindSafe, R>(
}

pub struct EventLoopProxy<T> {
sender: mpsc::Sender<T>,
sender: mpsc::SyncSender<T>,
source: CFRunLoopSourceRef,
}

unsafe impl<T: Send> Send for EventLoopProxy<T> {}
// Looking at the source code for `CFRunLoopSourceSignal` (https://github.com/opensource-apple/CF/blob/3cc41a76b1491f50813e28a4ec09954ffa359e6f/CFRunLoop.c#L3418-L3425),
// it locks the source before doing anything, so I think it should be fine to
// use from behind a reference.
//
// `SyncSender` is already `Sync`, so that's not an issue.
unsafe impl<T: Send> Sync for EventLoopProxy<T> {}

impl<T> Drop for EventLoopProxy<T> {
fn drop(&mut self) {
Expand All @@ -303,7 +309,7 @@ impl<T> Clone for EventLoopProxy<T> {
}

impl<T> EventLoopProxy<T> {
fn new(sender: mpsc::Sender<T>) -> Self {
fn new(sender: mpsc::SyncSender<T>) -> Self {
unsafe {
// just wake up the eventloop
extern "C" fn event_loop_proxy_handler(_: *mut c_void) {}
Expand Down
21 changes: 11 additions & 10 deletions src/platform_impl/web/event_loop/proxy.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
use super::runner;
use crate::event::Event;
use async_channel::{Sender, TrySendError};

use crate::event_loop::EventLoopClosed;

pub struct EventLoopProxy<T: 'static> {
runner: runner::Shared<T>,
pub sender: Sender<T>,
}

impl<T: 'static> EventLoopProxy<T> {
pub fn new(runner: runner::Shared<T>) -> Self {
Self { runner }
}

pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.runner.send_event(Event::UserEvent(event));
Ok(())
match self.sender.try_send(event) {
Ok(()) => Ok(()),
Err(TrySendError::Closed(val)) => Err(EventLoopClosed(val)),
// Note: `async-channel` has no way to block on sending something,
// so this is our only option for making this synchronous.
Err(TrySendError::Full(_)) => unreachable!("`EventLoopProxy` channels are unbounded"),
}
}
}

impl<T: 'static> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
Self {
runner: self.runner.clone(),
sender: self.sender.clone(),
}
}
}
36 changes: 34 additions & 2 deletions src/platform_impl/web/event_loop/runner.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::EventLoopProxy;
use super::{super::ScaleChangeArgs, backend, state::State};
use crate::event::{Event, StartCause};
use crate::event_loop::ControlFlow;
use crate::window::WindowId;

use async_channel::Sender;
use instant::{Duration, Instant};
use std::{
cell::RefCell,
Expand Down Expand Up @@ -30,6 +32,9 @@ pub struct Execution<T: 'static> {
destroy_pending: RefCell<VecDeque<WindowId>>,
scale_change_detector: RefCell<Option<backend::ScaleChangeDetector>>,
unload_event_handle: RefCell<Option<backend::UnloadEventHandle>>,
/// A channel through which user events can be sent, to be cloned and put
/// into `EventLoopProxy`s.
proxy_sender: Sender<T>,
}

enum RunnerEnum<T: 'static> {
Expand Down Expand Up @@ -97,7 +102,9 @@ impl<T: 'static> Runner<T> {

impl<T: 'static> Shared<T> {
pub fn new() -> Self {
Shared(Rc::new(Execution {
let (proxy_sender, proxy_receiver) = async_channel::unbounded();

let this = Shared(Rc::new(Execution {
runner: RefCell::new(RunnerEnum::Pending),
events: RefCell::new(VecDeque::new()),
id: RefCell::new(0),
Expand All @@ -106,7 +113,22 @@ impl<T: 'static> Shared<T> {
destroy_pending: RefCell::new(VecDeque::new()),
scale_change_detector: RefCell::new(None),
unload_event_handle: RefCell::new(None),
}))
proxy_sender,
}));

{
let runner = this.clone();
wasm_bindgen_futures::spawn_local(async move {
while let Ok(value) = proxy_receiver.recv().await {
runner.send_event(Event::UserEvent(value))
}

// An error was returned because the channel was closed, which
// happens when the event loop gets closed, so we can stop now.
})
}

this
}

pub fn add_canvas(&self, id: WindowId, canvas: &Rc<RefCell<backend::Canvas>>) {
Expand Down Expand Up @@ -153,6 +175,12 @@ impl<T: 'static> Shared<T> {
*id
}

pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
sender: self.0.proxy_sender.clone(),
}
}

pub fn request_redraw(&self, id: WindowId) {
self.0.redraw_pending.borrow_mut().insert(id);
}
Expand Down Expand Up @@ -463,6 +491,10 @@ impl<T: 'static> Shared<T> {

fn handle_loop_destroyed(&self, control: &mut ControlFlow) {
self.handle_event(Event::LoopDestroyed, control);
// Close the `EventLoopProxy` channel, causing any more calls to
// `EventLoopProxy::send_event` to fail and indirectly stopping the receiver
// task.
self.0.proxy_sender.close();
let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut());
*self.0.scale_change_detector.borrow_mut() = None;
*self.0.unload_event_handle.borrow_mut() = None;
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl<T> EventLoopWindowTarget<T> {
}

pub fn proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.runner.clone())
self.runner.create_proxy()
}

pub fn run(&self, event_handler: Box<dyn FnMut(Event<'_, T>, &mut ControlFlow)>) {
Expand Down
10 changes: 5 additions & 5 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::{
mem, panic, ptr,
rc::Rc,
sync::{
mpsc::{self, Receiver, Sender},
mpsc::{self, Receiver, SyncSender},
Arc,
},
thread,
Expand Down Expand Up @@ -150,7 +150,7 @@ impl<T> ThreadMsgTargetData<T> {
}

pub struct EventLoop<T: 'static> {
thread_msg_sender: Sender<T>,
thread_msg_sender: SyncSender<T>,
window_target: RootELW<T>,
msg_hook: Option<Box<dyn FnMut(*const c_void) -> bool + 'static>>,
}
Expand Down Expand Up @@ -552,7 +552,7 @@ type ThreadExecFn = Box<Box<dyn FnMut()>>;

pub struct EventLoopProxy<T: 'static> {
target_window: HWND,
event_send: Sender<T>,
event_send: SyncSender<T>,
}
unsafe impl<T: Send + 'static> Send for EventLoopProxy<T> {}

Expand Down Expand Up @@ -672,8 +672,8 @@ fn create_event_target_window<T: 'static>() -> HWND {
fn insert_event_target_window_data<T>(
thread_msg_target: HWND,
event_loop_runner: EventLoopRunnerShared<T>,
) -> Sender<T> {
let (tx, rx) = mpsc::channel();
) -> SyncSender<T> {
let (tx, rx) = mpsc::sync_channel(10);

let userdata = ThreadMsgTargetData {
event_loop_runner,
Expand Down
1 change: 0 additions & 1 deletion tests/send_objects.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#[allow(dead_code)]
fn needs_send<T: Send>() {}

#[cfg(not(target_arch = "wasm32"))]
#[test]
fn event_loop_proxy_send() {
#[allow(dead_code)]
Expand Down
9 changes: 9 additions & 0 deletions tests/sync_object.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
#[allow(dead_code)]
fn needs_sync<T: Sync>() {}

#[test]
fn event_loop_proxy_sync() {
#[allow(dead_code)]
fn is_send<T: 'static + Send>() {
// ensures that `winit::EventLoopProxy` implements `Sync`
needs_sync::<winit::event_loop::EventLoopProxy<T>>();
}
}

#[cfg(not(target_arch = "wasm32"))]
#[test]
fn window_sync() {
Expand Down