Skip to main content

accesskit_winit/
lib.rs

1// Copyright 2022 The AccessKit Authors. All rights reserved.
2// Licensed under the Apache License, Version 2.0 (found in
3// the LICENSE-APACHE file).
4
5/// ## Compatibility with async runtimes
6///
7/// The following only applies on Linux/Unix:
8///
9/// While this crate's API is purely blocking, it internally spawns asynchronous tasks on an executor.
10///
11/// - If you use tokio, make sure to enable the `tokio` feature of this crate.
12/// - If you use another async runtime or if you don't use one at all, the default feature will suit your needs.
13
14#[cfg(all(
15    feature = "accesskit_unix",
16    any(
17        target_os = "linux",
18        target_os = "dragonfly",
19        target_os = "freebsd",
20        target_os = "netbsd",
21        target_os = "openbsd"
22    ),
23    not(feature = "async-io"),
24    not(feature = "tokio")
25))]
26compile_error!("Either \"async-io\" (default) or \"tokio\" feature must be enabled.");
27
28#[cfg(all(
29    feature = "accesskit_unix",
30    any(
31        target_os = "linux",
32        target_os = "dragonfly",
33        target_os = "freebsd",
34        target_os = "netbsd",
35        target_os = "openbsd"
36    ),
37    feature = "async-io",
38    feature = "tokio"
39))]
40compile_error!(
41    "Both \"async-io\" (default) and \"tokio\" features cannot be enabled at the same time."
42);
43
44#[cfg(all(not(feature = "rwh_05"), not(feature = "rwh_06")))]
45compile_error!("Either \"rwh_06\" (default) or \"rwh_05\" feature must be enabled.");
46
47#[cfg(all(feature = "rwh_05", feature = "rwh_06"))]
48compile_error!(
49    "Both \"rwh_06\" (default) and \"rwh_05\" features cannot be enabled at the same time."
50);
51
52use accesskit::{ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, TreeUpdate};
53use winit::{
54    event::WindowEvent as WinitWindowEvent,
55    event_loop::{ActiveEventLoop, EventLoopProxy},
56    window::{Window, WindowId},
57};
58
59#[cfg(feature = "rwh_05")]
60#[allow(unused)]
61use rwh_05 as raw_window_handle;
62#[cfg(feature = "rwh_06")]
63#[allow(unused)]
64use rwh_06 as raw_window_handle;
65
66mod platform_impl;
67
68#[derive(Debug)]
69pub struct Event {
70    pub window_id: WindowId,
71    pub window_event: WindowEvent,
72}
73
74#[derive(Clone, Debug, PartialEq)]
75pub enum WindowEvent {
76    InitialTreeRequested,
77    ActionRequested(ActionRequest),
78    AccessibilityDeactivated,
79}
80
81struct WinitActivationHandler<T: From<Event> + Send + 'static> {
82    window_id: WindowId,
83    proxy: EventLoopProxy<T>,
84}
85
86impl<T: From<Event> + Send + 'static> ActivationHandler for WinitActivationHandler<T> {
87    fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
88        let event = Event {
89            window_id: self.window_id,
90            window_event: WindowEvent::InitialTreeRequested,
91        };
92        self.proxy.send_event(event.into()).ok();
93        None
94    }
95}
96
97struct WinitActionHandler<T: From<Event> + Send + 'static> {
98    window_id: WindowId,
99    proxy: EventLoopProxy<T>,
100}
101
102impl<T: From<Event> + Send + 'static> ActionHandler for WinitActionHandler<T> {
103    fn do_action(&mut self, request: ActionRequest) {
104        let event = Event {
105            window_id: self.window_id,
106            window_event: WindowEvent::ActionRequested(request),
107        };
108        self.proxy.send_event(event.into()).ok();
109    }
110}
111
112struct WinitDeactivationHandler<T: From<Event> + Send + 'static> {
113    window_id: WindowId,
114    proxy: EventLoopProxy<T>,
115}
116
117impl<T: From<Event> + Send + 'static> DeactivationHandler for WinitDeactivationHandler<T> {
118    fn deactivate_accessibility(&mut self) {
119        let event = Event {
120            window_id: self.window_id,
121            window_event: WindowEvent::AccessibilityDeactivated,
122        };
123        self.proxy.send_event(event.into()).ok();
124    }
125}
126
127pub struct Adapter {
128    inner: platform_impl::Adapter,
129}
130
131impl Adapter {
132    /// Creates a new AccessKit adapter for a winit window. This must be done
133    /// before the window is shown for the first time. This means that you must
134    /// use [`winit::window::WindowAttributes::with_visible`] to make the window
135    /// initially invisible, then create the adapter, then show the window.
136    ///
137    /// This constructor uses a winit event loop proxy to deliver AccessKit
138    /// events to the main event loop. The primary disadvantage of this approach
139    /// is that it's not possible to synchronously return an initial tree
140    /// in response to the [`WindowEvent::InitialTreeRequested`] event,
141    /// so some platform adapters will have to use a temporary placeholder tree
142    /// until you send the first update. For an optimal implementation,
143    /// consider using [`Adapter::with_direct_handlers`] or
144    /// [`Adapter::with_mixed_handlers`] instead.
145    ///
146    /// # Panics
147    ///
148    /// Panics if the window is already visible.
149    pub fn with_event_loop_proxy<T: From<Event> + Send + 'static>(
150        event_loop: &ActiveEventLoop,
151        window: &Window,
152        proxy: EventLoopProxy<T>,
153    ) -> Self {
154        let window_id = window.id();
155        let activation_handler = WinitActivationHandler {
156            window_id,
157            proxy: proxy.clone(),
158        };
159        let action_handler = WinitActionHandler {
160            window_id,
161            proxy: proxy.clone(),
162        };
163        let deactivation_handler = WinitDeactivationHandler { window_id, proxy };
164        Self::with_direct_handlers(
165            event_loop,
166            window,
167            activation_handler,
168            action_handler,
169            deactivation_handler,
170        )
171    }
172
173    /// Creates a new AccessKit adapter for a winit window. This must be done
174    /// before the window is shown for the first time. This means that you must
175    /// use [`winit::window::WindowAttributes::with_visible`] to make the window
176    /// initially invisible, then create the adapter, then show the window.
177    ///
178    /// Use this if you want to provide your own AccessKit handler callbacks
179    /// rather than dispatching requests through the winit event loop. This is
180    /// especially useful for the activation handler, because depending on
181    /// your application's architecture, implementing the handler directly may
182    /// allow you to return an initial tree synchronously, rather than requiring
183    /// some platform adapters to use a placeholder tree until you send
184    /// the first update. However, remember that each of these handlers may be
185    /// called on any thread, depending on the underlying platform adapter.
186    ///
187    /// # Panics
188    ///
189    /// Panics if the window is already visible.
190    pub fn with_direct_handlers(
191        event_loop: &ActiveEventLoop,
192        window: &Window,
193        activation_handler: impl 'static + ActivationHandler + Send,
194        action_handler: impl 'static + ActionHandler + Send,
195        deactivation_handler: impl 'static + DeactivationHandler + Send,
196    ) -> Self {
197        if window.is_visible() == Some(true) {
198            panic!(
199                "The AccessKit winit adapter must be created before the window is shown (made visible) for the first time."
200            );
201        }
202
203        let inner = platform_impl::Adapter::new(
204            event_loop,
205            window,
206            activation_handler,
207            action_handler,
208            deactivation_handler,
209        );
210        Self { inner }
211    }
212
213    /// Creates a new AccessKit adapter for a winit window. This must be done
214    /// before the window is shown for the first time. This means that you must
215    /// use [`winit::window::WindowAttributes::with_visible`] to make the window
216    /// initially invisible, then create the adapter, then show the window.
217    ///
218    /// This constructor provides a mix of the approaches used by
219    /// [`Adapter::with_event_loop_proxy`] and [`Adapter::with_direct_handlers`].
220    /// It uses the event loop proxy for the action request and deactivation
221    /// events, which can be handled asynchronously with no drawback,
222    /// while using a direct, caller-provided activation handler that can
223    /// return the initial tree synchronously. Remember that the thread on which
224    /// the activation handler is called is platform-dependent.
225    ///
226    /// # Panics
227    ///
228    /// Panics if the window is already visible.
229    pub fn with_mixed_handlers<T: From<Event> + Send + 'static>(
230        event_loop: &ActiveEventLoop,
231        window: &Window,
232        activation_handler: impl 'static + ActivationHandler + Send,
233        proxy: EventLoopProxy<T>,
234    ) -> Self {
235        let window_id = window.id();
236        let action_handler = WinitActionHandler {
237            window_id,
238            proxy: proxy.clone(),
239        };
240        let deactivation_handler = WinitDeactivationHandler { window_id, proxy };
241        Self::with_direct_handlers(
242            event_loop,
243            window,
244            activation_handler,
245            action_handler,
246            deactivation_handler,
247        )
248    }
249
250    /// Allows reacting to window events.
251    ///
252    /// This must be called whenever a new window event is received
253    /// and before it is handled by the application.
254    pub fn process_event(&mut self, window: &Window, event: &WinitWindowEvent) {
255        self.inner.process_event(window, event);
256    }
257
258    /// If and only if the tree has been initialized, call the provided function
259    /// and apply the resulting update. Note: If the caller's implementation of
260    /// [`ActivationHandler::request_initial_tree`] initially returned `None`,
261    /// or if the caller created the adapter using [`EventLoopProxy`], then
262    /// the [`TreeUpdate`] returned by the provided function must contain
263    /// a full tree.
264    pub fn update_if_active(&mut self, updater: impl FnOnce() -> TreeUpdate) {
265        self.inner.update_if_active(updater);
266    }
267}