kas_core/runner/
mod.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Runner, platforms and backends
7
8mod common;
9mod event_loop;
10mod runner;
11mod shared;
12mod window;
13
14use crate::messages::Erased;
15use crate::window::{PopupDescriptor, WindowId};
16use event_loop::Loop;
17pub(crate) use shared::RunnerT;
18use shared::State;
19use std::fmt::Debug;
20pub use window::Window;
21pub(crate) use window::WindowDataErased;
22
23pub use common::{Error, Platform, Result};
24pub use runner::{ClosedError, PreLaunchState, Proxy};
25
26#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
27#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
28pub use common::{GraphicsInstance, WindowSurface};
29
30#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
31#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
32pub extern crate raw_window_handle;
33
34/// A type-erased message stack
35///
36/// This is a stack over [`Erased`], with some downcasting methods.
37/// It is a component of [`EventCx`](crate::event::EventCx) and usually only
38/// used through that, thus the interface here is incomplete.
39#[must_use]
40#[derive(Debug, Default)]
41pub struct MessageStack {
42    base: usize,
43    count: usize,
44    stack: Vec<Erased>,
45}
46
47impl MessageStack {
48    /// Construct an empty stack
49    #[inline]
50    pub fn new() -> Self {
51        MessageStack::default()
52    }
53
54    /// Set the "stack base" to the current length
55    ///
56    /// Any messages on the stack before this method is called cannot be removed
57    /// until the base has been reset. This allows multiple widget tree
58    /// traversals with a single stack.
59    #[inline]
60    pub(crate) fn set_base(&mut self) {
61        self.base = self.stack.len();
62    }
63
64    /// Get the current operation count
65    ///
66    /// This is incremented every time the message stack is changed.
67    #[inline]
68    pub(crate) fn get_op_count(&self) -> usize {
69        self.count
70    }
71
72    /// Reset the base; return true if messages are available after reset
73    #[inline]
74    pub(crate) fn reset_and_has_any(&mut self) -> bool {
75        self.base = 0;
76        !self.stack.is_empty()
77    }
78
79    /// True if the stack has messages available
80    #[inline]
81    pub fn has_any(&self) -> bool {
82        self.stack.len() > self.base
83    }
84
85    /// Push a type-erased message to the stack
86    #[inline]
87    pub(crate) fn push_erased(&mut self, msg: Erased) {
88        self.count = self.count.wrapping_add(1);
89        self.stack.push(msg);
90    }
91
92    /// Pop a type-erased message from the stack, if non-empty
93    #[inline]
94    pub fn pop_erased(&mut self) -> Option<Erased> {
95        self.count = self.count.wrapping_add(1);
96        self.stack.pop()
97    }
98
99    /// Try popping the last message from the stack with the given type
100    pub fn try_pop<M: Debug + 'static>(&mut self) -> Option<M> {
101        if self.has_any() && self.stack.last().map(|m| m.is::<M>()).unwrap_or(false) {
102            self.count = self.count.wrapping_add(1);
103            self.stack.pop().unwrap().downcast::<M>().ok().map(|m| *m)
104        } else {
105            None
106        }
107    }
108
109    /// Try observing the last message on the stack without popping
110    pub fn try_peek<M: Debug + 'static>(&self) -> Option<&M> {
111        if self.has_any() {
112            self.stack.last().and_then(|m| m.downcast_ref::<M>())
113        } else {
114            None
115        }
116    }
117
118    /// Debug the last message on the stack, if any
119    pub fn peek_debug(&self) -> Option<&dyn Debug> {
120        self.stack.last().map(Erased::debug)
121    }
122}
123
124impl Drop for MessageStack {
125    fn drop(&mut self) {
126        for msg in self.stack.drain(..) {
127            if msg.is::<crate::event::components::KineticStart>() {
128                // We can safely ignore this message
129                continue;
130            }
131
132            log::warn!(target: "kas_core::erased", "unhandled: {msg:?}");
133        }
134    }
135}
136
137/// Application state
138///
139/// Kas allows application state to be stored both in the  widget tree (in
140/// `Adapt` nodes and user-defined widgets) and by the application root (shared
141/// across windows). This trait must be implemented by the latter.
142///
143/// When no top-level data is required, use `()` which implements this trait.
144///
145/// TODO: should we pass some type of interface to the runner to these methods?
146/// We could pass a `&mut dyn RunnerT` easily, but that trait is not public.
147pub trait AppData: 'static {
148    /// Handle messages
149    ///
150    /// This is the last message handler: it is called when, after traversing
151    /// the widget tree (see [kas::event] module doc), a message is left on the
152    /// stack. Unhandled messages will result in warnings in the log.
153    fn handle_messages(&mut self, messages: &mut MessageStack);
154
155    /// Application is being suspended
156    ///
157    /// The application should ensure any important state is saved.
158    ///
159    /// This method is called when the application has been suspended or is
160    /// about to exit (on Android/iOS/Web platforms, the application may resume
161    /// after this method is called; on other platforms this probably indicates
162    /// imminent closure). Widget state may still exist, but is not live
163    /// (widgets will not process events or messages).
164    fn suspended(&mut self) {}
165}
166
167impl AppData for () {
168    fn handle_messages(&mut self, _: &mut MessageStack) {}
169    fn suspended(&mut self) {}
170}
171
172#[crate::autoimpl(Debug)]
173enum Pending<A: AppData, G: GraphicsInstance, T: kas::theme::Theme<G::Shared>> {
174    AddPopup(WindowId, WindowId, PopupDescriptor),
175    RepositionPopup(WindowId, PopupDescriptor),
176    // NOTE: we don't need G, T here if we construct the Window later.
177    // But this way we can pass a single boxed value.
178    AddWindow(WindowId, Box<Window<A, G, T>>),
179    CloseWindow(WindowId),
180    Action(kas::Action),
181    Exit,
182}
183
184#[derive(Debug)]
185enum ProxyAction {
186    CloseAll,
187    Close(WindowId),
188    Message(kas::messages::SendErased),
189    WakeAsync,
190    #[cfg(feature = "accesskit")]
191    AccessKit(winit::window::WindowId, accesskit_winit::WindowEvent),
192}
193
194#[cfg(feature = "accesskit")]
195impl From<accesskit_winit::Event> for ProxyAction {
196    fn from(event: accesskit_winit::Event) -> Self {
197        ProxyAction::AccessKit(event.window_id, event.window_event)
198    }
199}
200
201#[cfg(test)]
202mod test {
203    use super::*;
204    use raw_window_handle as rwh;
205    use std::time::Instant;
206
207    struct Draw;
208    impl crate::draw::DrawImpl for Draw {
209        fn common_mut(&mut self) -> &mut crate::draw::WindowCommon {
210            todo!()
211        }
212
213        fn new_pass(
214            &mut self,
215            _: crate::draw::PassId,
216            _: crate::prelude::Rect,
217            _: crate::prelude::Offset,
218            _: crate::draw::PassType,
219        ) -> crate::draw::PassId {
220            todo!()
221        }
222
223        fn get_clip_rect(&self, _: crate::draw::PassId) -> crate::prelude::Rect {
224            todo!()
225        }
226
227        fn rect(
228            &mut self,
229            _: crate::draw::PassId,
230            _: crate::geom::Quad,
231            _: crate::draw::color::Rgba,
232        ) {
233            todo!()
234        }
235
236        fn frame(
237            &mut self,
238            _: crate::draw::PassId,
239            _: crate::geom::Quad,
240            _: crate::geom::Quad,
241            _: crate::draw::color::Rgba,
242        ) {
243            todo!()
244        }
245    }
246    impl crate::draw::DrawRoundedImpl for Draw {
247        fn rounded_line(
248            &mut self,
249            _: crate::draw::PassId,
250            _: crate::geom::Vec2,
251            _: crate::geom::Vec2,
252            _: f32,
253            _: crate::draw::color::Rgba,
254        ) {
255            todo!()
256        }
257
258        fn circle(
259            &mut self,
260            _: crate::draw::PassId,
261            _: crate::geom::Quad,
262            _: f32,
263            _: crate::draw::color::Rgba,
264        ) {
265            todo!()
266        }
267
268        fn circle_2col(
269            &mut self,
270            _: crate::draw::PassId,
271            _: crate::geom::Quad,
272            _: crate::draw::color::Rgba,
273            _: crate::draw::color::Rgba,
274        ) {
275            todo!()
276        }
277
278        fn rounded_frame(
279            &mut self,
280            _: crate::draw::PassId,
281            _: crate::geom::Quad,
282            _: crate::geom::Quad,
283            _: f32,
284            _: crate::draw::color::Rgba,
285        ) {
286            todo!()
287        }
288
289        fn rounded_frame_2col(
290            &mut self,
291            _: crate::draw::PassId,
292            _: crate::geom::Quad,
293            _: crate::geom::Quad,
294            _: crate::draw::color::Rgba,
295            _: crate::draw::color::Rgba,
296        ) {
297            todo!()
298        }
299    }
300
301    struct DrawShared;
302    impl crate::draw::DrawSharedImpl for DrawShared {
303        type Draw = Draw;
304
305        fn max_texture_dimension_2d(&self) -> u32 {
306            todo!()
307        }
308
309        fn set_raster_config(&mut self, _: &crate::config::RasterConfig) {
310            todo!()
311        }
312
313        fn image_alloc(
314            &mut self,
315            _: (u32, u32),
316        ) -> std::result::Result<crate::draw::ImageId, crate::draw::AllocError> {
317            todo!()
318        }
319
320        fn image_upload(&mut self, _: crate::draw::ImageId, _: &[u8], _: crate::draw::ImageFormat) {
321            todo!()
322        }
323
324        fn image_free(&mut self, _: crate::draw::ImageId) {
325            todo!()
326        }
327
328        fn image_size(&self, _: crate::draw::ImageId) -> Option<(u32, u32)> {
329            todo!()
330        }
331
332        fn draw_image(
333            &self,
334            _: &mut Self::Draw,
335            _: crate::draw::PassId,
336            _: crate::draw::ImageId,
337            _: crate::geom::Quad,
338        ) {
339            todo!()
340        }
341
342        fn draw_text(
343            &mut self,
344            _: &mut Self::Draw,
345            _: crate::draw::PassId,
346            _: crate::geom::Vec2,
347            _: crate::geom::Quad,
348            _: &kas_text::TextDisplay,
349            _: crate::draw::color::Rgba,
350        ) {
351            todo!()
352        }
353
354        fn draw_text_effects(
355            &mut self,
356            _: &mut Self::Draw,
357            _: crate::draw::PassId,
358            _: crate::geom::Vec2,
359            _: crate::geom::Quad,
360            _: &kas_text::TextDisplay,
361            _: &[kas_text::Effect],
362            _: &[crate::draw::color::Rgba],
363        ) {
364            todo!()
365        }
366    }
367
368    struct Surface;
369    impl WindowSurface for Surface {
370        type Shared = DrawShared;
371
372        fn size(&self) -> crate::prelude::Size {
373            todo!()
374        }
375
376        fn configure(&mut self, _: &mut Self::Shared, _: crate::prelude::Size) -> bool {
377            todo!()
378        }
379
380        fn draw_iface<'iface>(
381            &'iface mut self,
382            _: &'iface mut crate::draw::SharedState<Self::Shared>,
383        ) -> crate::draw::DrawIface<'iface, Self::Shared> {
384            todo!()
385        }
386
387        fn common_mut(&mut self) -> &mut crate::draw::WindowCommon {
388            todo!()
389        }
390
391        fn present(&mut self, _: &mut Self::Shared, _: crate::draw::color::Rgba) -> Instant {
392            todo!()
393        }
394    }
395
396    struct AGB;
397    impl GraphicsInstance for AGB {
398        type Shared = DrawShared;
399
400        type Surface<'a> = Surface;
401
402        fn new_shared(&mut self, _: Option<&Self::Surface<'_>>) -> Result<Self::Shared> {
403            todo!()
404        }
405
406        fn new_surface<'window, W>(&mut self, _: W, _: bool) -> Result<Self::Surface<'window>>
407        where
408            W: rwh::HasWindowHandle + rwh::HasDisplayHandle + Send + Sync + 'window,
409            Self: Sized,
410        {
411            todo!()
412        }
413    }
414
415    #[test]
416    fn size_of_pending() {
417        assert_eq!(
418            std::mem::size_of::<Pending<(), AGB, crate::theme::SimpleTheme>>(),
419            40
420        );
421    }
422}