zng_view_api/
lib.rs

1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! The View Process API.
5//!
6//! Zng isolates all render and windowing related code to a different process (the view-process), this crate
7//! provides the API that must be implemented to create a view-process backend, plus the [`Controller`] that
8//! can be used from an app-process to spawn and communicate with a view-process.
9//!
10//! # VERSION
11//!
12//! The [`VERSION`] of this crate must match exactly in both *App-Process* and *View-Process*, otherwise a runtime
13//! panic error is generated.
14//!
15//! # Same Process Patch
16//!
17//! Dynamically loaded same process implementers must propagate a [`StaticPatch`], otherwise the view will not connect.
18//!
19//! # Crate
20//!
21#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
22#![warn(missing_docs)]
23#![warn(unused_extern_crates)]
24
25use drag_drop::{DragDropData, DragDropEffect, DragDropError};
26use serde::{Deserialize, Serialize};
27
28/// The *App Process* and *View Process* must be build using the same exact version and this is
29/// validated during run-time, causing a panic if the versions don't match.
30pub const VERSION: &str = env!("CARGO_PKG_VERSION");
31
32pub mod access;
33pub mod api_extension;
34pub mod audio;
35pub mod clipboard;
36pub mod config;
37pub mod dialog;
38pub mod display_list;
39pub mod drag_drop;
40pub mod font;
41pub mod image;
42pub mod ipc;
43pub mod keyboard;
44pub mod mouse;
45pub mod raw_input;
46pub mod touch;
47pub mod window;
48
49mod types;
50pub use types::*;
51
52mod app_process;
53pub use app_process::*;
54
55mod view_process;
56pub use view_process::*;
57use zng_txt::Txt;
58
59use std::fmt;
60
61use api_extension::{ApiExtensionId, ApiExtensionPayload};
62use clipboard::{ClipboardData, ClipboardError};
63use dialog::DialogId;
64use font::{FontFaceId, FontId, FontOptions, FontVariationName};
65use image::{ImageId, ImageMaskMode, ImageRequest, ImageTextureId};
66use window::WindowId;
67use zng_task::channel::{IpcBytes, IpcReceiver};
68use zng_unit::{DipPoint, DipRect, DipSize, Factor, Px, PxRect};
69
70/// Packaged API request.
71#[derive(Debug, Serialize, Deserialize)]
72pub struct Request(RequestData);
73impl Request {
74    /// Returns `true` if the request can only be made after the *init* event.
75    pub fn must_be_connected(&self) -> bool {
76        !matches!(&self.0, RequestData::init { .. })
77    }
78
79    /// Returns `true` if the request represents a new frame or frame update for the window with the same wait ID.
80    pub fn is_frame(&self, window_id: WindowId, wait_id: Option<window::FrameWaitId>) -> bool {
81        match &self.0 {
82            RequestData::render { id, frame } if *id == window_id && frame.wait_id == wait_id => true,
83            RequestData::render_update { id, frame } if *id == window_id && frame.wait_id == wait_id => true,
84            _ => false,
85        }
86    }
87
88    /// Returns `true` if the request affects position or size of the window.
89    pub fn affects_window_rect(&self, window_id: WindowId) -> bool {
90        matches!(
91            &self.0,
92            RequestData::set_state { id, .. }
93            if *id == window_id
94        )
95    }
96
97    /// Returns `true` if this request will receive a response. Only [`Api`] methods
98    /// that have a return value send back a response.
99    pub fn expect_response(&self) -> bool {
100        self.0.expect_response()
101    }
102}
103
104/// Packaged API response.
105#[derive(Debug, Serialize, Deserialize)]
106pub struct Response(ResponseData);
107impl Response {
108    /// If this response must be send back to the app process. Only [`Api`] methods
109    /// that have a return value send back a response.
110    pub fn must_be_send(&self) -> bool {
111        self.0.must_be_send()
112    }
113}
114
115macro_rules! TypeOrNil {
116    ($T:ty) => {
117        $T
118    };
119    () => {
120        ()
121    };
122}
123
124macro_rules! type_is_some {
125    (if $T:ty { $($t_true:tt)* } else { $($t_false:tt)* }) => {
126        $($t_true)*
127    };
128    (if { $($t_true:tt)* } else { $($t_false:tt)* }) => {
129        $($t_false)*
130    };
131}
132
133/// Declares the internal `Request` and `Response` enums, public methods in `Controller` and the public trait `ViewApp`, in the
134/// controller it packs and sends the request and receives and unpacks the response. In the view it implements
135/// the method.
136macro_rules! declare_api {
137    (
138        $(
139            $(#[$meta:meta])*
140            $vis:vis fn $method:ident(
141                &mut $self:ident
142                $(, $input:ident : $RequestType:ty)* $(,)?
143            ) $(-> $ResponseType:ty)?;
144        )*
145    ) => {
146        #[derive(Serialize, Deserialize)]
147        #[allow(non_camel_case_types)]
148        #[allow(clippy::large_enum_variant)]
149        #[repr(u32)]
150        enum RequestData {
151            $(
152                $(#[$meta])*
153                $method { $($input: $RequestType),* },
154            )*
155        }
156        impl RequestData {
157            #[allow(unused_doc_comments)]
158            pub fn expect_response(&self) -> bool {
159                match self {
160                    $(
161                        $(#[$meta])*
162                        Self::$method { .. } => type_is_some! {
163                            if $($ResponseType)? {
164                                true
165                            } else {
166                                false
167                            }
168                        },
169                    )*
170                }
171            }
172        }
173        impl fmt::Debug for RequestData {
174            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175                #[allow(unused_doc_comments)]
176                if f.alternate() {
177                    match self {
178                        $(
179                            $(#[$meta])*
180                            RequestData::$method { $($input),* } => write!(f, "{}{:#?}", stringify!($method), ($($input),*)),
181                        )+
182                    }
183                } else {
184                    match self {
185                        $(
186                            $(#[$meta])*
187                            RequestData::$method { .. } => write!(f, "{}(..)", stringify!($method)),
188                        )+
189                    }
190                }
191            }
192        }
193
194        #[derive(Debug)]
195        #[derive(Serialize, Deserialize)]
196        #[allow(non_camel_case_types)]
197        #[repr(u32)]
198        enum ResponseData {
199            $(
200                $(#[$meta])*
201                $method(TypeOrNil![$($ResponseType)?]),
202            )*
203        }
204        impl ResponseData {
205            #[allow(unused_doc_comments)]
206            pub fn must_be_send(&self) -> bool {
207                match self {
208                    $(
209                        $(#[$meta])*
210                        Self::$method(_) => type_is_some! {
211                            if $($ResponseType)? {
212                                true
213                            } else {
214                                false
215                            }
216                        },
217                    )*
218                }
219            }
220        }
221
222        #[allow(unused_parens)]
223        impl Controller {
224            $(
225                $(#[$meta])*
226                #[allow(clippy::too_many_arguments)]
227                $vis fn $method(&mut self $(, $input: $RequestType)*) -> VpResult<TypeOrNil![$($ResponseType)?]> {
228                    let req = Request(RequestData::$method { $($input),* });
229                    type_is_some! {
230                        if $($ResponseType)? {
231                            match self.talk(req)?.0 {
232                                ResponseData::$method(r) => Ok(r),
233                                r => panic!("view-process did not respond correctly for `{}`, {r:?}", stringify!($method))
234                            }
235                        } else {
236                            self.command(req)
237                        }
238                    }
239                }
240            )*
241        }
242
243        /// The view-process API.
244        pub trait Api {
245            /// Already implemented, matches a request, calls the corresponding method and packages the response.
246            fn respond(&mut self, request: Request) -> Response {
247                match request.0 {
248                    $(
249                        #[allow(unused_doc_comments)]
250                        $(#[$meta])* // for the cfg
251                        RequestData::$method { $($input),* } => {
252                            let r = self.$method($($input),*);
253                            Response(ResponseData::$method(r))
254                        }
255                    )*
256                }
257            }
258
259            $(
260                $(#[$meta])*
261                #[allow(clippy::too_many_arguments)]
262                fn $method(&mut self, $($input: $RequestType),*) $(-> $ResponseType)?;
263            )*
264        }
265    };
266}
267declare_api! {
268    /// Called once on init.
269    ///
270    /// Sends an [`Event::Inited`] once the view is completely connected.
271    /// Other methods may only be called after this event.
272    fn init(&mut self, vp_gen: ViewProcessGen, is_respawn: bool, headless: bool);
273
274    /// Called once after exit, if running in a managed external process it will be killed after this call.
275    fn exit(&mut self);
276
277    /// Enable/disable global device events.
278    ///
279    /// This filter affects device events not targeted at windows, such as mouse move outside windows or
280    /// key presses when the app has no focused window.
281    pub fn set_device_events_filter(&mut self, filter: DeviceEventsFilter);
282
283    /// Open a window.
284    ///
285    /// Sends an [`Event::WindowOpened`] once the window, context and renderer have finished initializing or a
286    /// [`Event::WindowOrHeadlessOpenError`] if it failed.
287    pub fn open_window(&mut self, request: window::WindowRequest);
288
289    /// Open a headless surface.
290    ///
291    /// This is a real renderer but not connected to any window, you can requests pixels to get the
292    /// rendered frames.
293    ///
294    /// Sends an [`Event::HeadlessOpened`] once the context and renderer have finished initializing or a
295    /// [`Event::WindowOrHeadlessOpenError`] if it failed.
296    pub fn open_headless(&mut self, request: window::HeadlessRequest);
297
298    /// Close the window or headless surface.
299    ///
300    /// All documents associated with the window or surface are also closed.
301    pub fn close(&mut self, id: WindowId);
302
303    /// Set window title.
304    pub fn set_title(&mut self, id: WindowId, title: Txt);
305
306    /// Set window visible.
307    pub fn set_visible(&mut self, id: WindowId, visible: bool);
308
309    /// Set if the window is "top-most".
310    pub fn set_always_on_top(&mut self, id: WindowId, always_on_top: bool);
311
312    /// Set if the user can drag-move the window when it is in `Normal` mode.
313    pub fn set_movable(&mut self, id: WindowId, movable: bool);
314
315    /// Set if the user can resize the window when it is in `Normal` mode.
316    pub fn set_resizable(&mut self, id: WindowId, resizable: bool);
317
318    /// Set the window taskbar icon visibility.
319    pub fn set_taskbar_visible(&mut self, id: WindowId, visible: bool);
320
321    /// Bring the window to the Z top, without focusing it.
322    pub fn bring_to_top(&mut self, id: WindowId);
323
324    /// Set the window state, position, size.
325    pub fn set_state(&mut self, id: WindowId, state: window::WindowStateAll);
326
327    /// Set the headless surface or document area size (viewport size).
328    pub fn set_headless_size(&mut self, id: WindowId, size: DipSize, scale_factor: Factor);
329
330    /// Set the window icon, the icon image must be loaded.
331    pub fn set_icon(&mut self, id: WindowId, icon: Option<ImageId>);
332
333    /// Set the window cursor icon and visibility.
334    pub fn set_cursor(&mut self, id: WindowId, cursor: Option<window::CursorIcon>);
335
336    /// Set the window cursor to a custom image.
337    ///
338    /// Falls back to cursor icon if not supported or if set to `None`.
339    pub fn set_cursor_image(&mut self, id: WindowId, cursor: Option<window::CursorImage>);
340
341    /// Sets the user attention request indicator, the indicator is cleared when the window is focused or
342    /// if canceled by setting to `None`.
343    pub fn set_focus_indicator(&mut self, id: WindowId, indicator: Option<window::FocusIndicator>);
344
345    /// Set enabled window chrome buttons.
346    pub fn set_enabled_buttons(&mut self, id: WindowId, buttons: window::WindowButton);
347
348    /// Brings the window to the front and sets input focus.
349    ///
350    /// Sends an [`Event::FocusChanged`] if the window is focused, the request can be ignored by the window manager, or if the
351    /// window is not visible, minimized or already focused.
352    ///
353    /// This request can steal focus from other apps disrupting the user, be careful with it.
354    pub fn focus(&mut self, id: WindowId) -> FocusResult;
355
356    /// Moves the window with the left mouse button until the button is released.
357    ///
358    /// There's no guarantee that this will work unless the left mouse button was pressed immediately before this function is called.
359    pub fn drag_move(&mut self, id: WindowId);
360
361    /// Resizes the window with the left mouse button until the button is released.
362    ///
363    /// There's no guarantee that this will work unless the left mouse button was pressed immediately before this function is called.
364    pub fn drag_resize(&mut self, id: WindowId, direction: window::ResizeDirection);
365
366    /// Open the system title bar context menu.
367    pub fn open_title_bar_context_menu(&mut self, id: WindowId, position: DipPoint);
368
369    /// Cache an image resource.
370    ///
371    /// The image is decoded asynchronously, the events [`Event::ImageMetadataLoaded`], [`Event::ImageLoaded`]
372    /// or [`Event::ImageLoadError`] will be send when the image is ready for use or failed.
373    ///
374    /// The [`ImageRequest::data`] handle must contain the full image data already, it will be dropped after the image finishes decoding.
375    ///
376    /// Images are shared between renderers, to use an image in a window you must first call [`use_image`]
377    /// this will register the image data with the renderer.
378    ///
379    /// [`use_image`]: Api::use_image
380    pub fn add_image(&mut self, request: ImageRequest<IpcBytes>) -> ImageId;
381
382    /// Cache an image from data that has not fully loaded.
383    ///
384    /// If the view-process implementation supports **progressive decoding** it will start decoding the image
385    /// as more data is received, otherwise it will collect all data first and then [`add_image`]. Each
386    /// [`ImageRequest::`data`] package is the continuation of the previous call, send an empty package to indicate finish.
387    ///
388    /// The events [`Event::ImageMetadataLoaded`], [`Event::ImageLoaded`] or [`Event::ImageLoadError`] will
389    /// be send while decoding.
390    ///
391    /// [`add_image`]: Api::add_image
392    pub fn add_image_pro(&mut self, request: ImageRequest<IpcReceiver<IpcBytes>>) -> ImageId;
393
394    /// Remove an image from cache.
395    ///
396    /// Note that if the image is in use in a renderer it will remain in memory until [`delete_image_use`] is
397    /// called or the renderer is deinited by closing the window.
398    ///
399    /// [`delete_image_use`]: Api::delete_image_use
400    pub fn forget_image(&mut self, id: ImageId);
401
402    /// Add an image resource to the window renderer.
403    ///
404    /// Returns the new image texture ID. If the `image_id` is not loaded returns the [`INVALID`] texture ID.
405    ///
406    /// [`INVALID`]: ImageTextureId::INVALID
407    pub fn use_image(&mut self, id: WindowId, image_id: ImageId) -> ImageTextureId;
408
409    /// Replace the image resource in the window renderer.
410    ///
411    /// The new `image_id` must represent an image with same dimensions and format as the previous. If the
412    /// image cannot be updated an error is logged and `false` is returned.
413    ///
414    /// The `dirty_rect` can be set to optimize texture upload to the GPU, if not set the entire image region updates.
415    ///
416    /// The [`ImageTextureId`] will be associated with the new [`ImageId`].
417    pub fn update_image_use(&mut self, id: WindowId, texture_id: ImageTextureId, image_id: ImageId, dirty_rect: Option<PxRect>)
418    -> bool;
419
420    /// Delete the image resource in the window renderer.
421    pub fn delete_image_use(&mut self, id: WindowId, texture_id: ImageTextureId);
422
423    /// Returns a list of image decoders supported by this implementation.
424    ///
425    /// Each text is the lower-case file extension, without the dot.
426    pub fn image_decoders(&mut self) -> Vec<Txt>;
427
428    /// Returns a list of image encoders supported by this implementation.
429    ///
430    /// Each text is the lower-case file extension, without the dot.
431    pub fn image_encoders(&mut self) -> Vec<Txt>;
432
433    /// Encode the image into the `format`.
434    ///
435    /// The format must be one of the values returned by [`image_encoders`].
436    ///
437    /// Returns immediately. The encoded data will be send as the event
438    /// [`Event::ImageEncoded`] or [`Event::ImageEncodeError`].
439    ///
440    /// [`image_encoders`]: Api::image_encoders
441    pub fn encode_image(&mut self, id: ImageId, format: Txt);
442
443    /// Cache an audio resource.
444    ///
445    /// # Unimplemented
446    ///
447    /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
448    pub fn add_audio(&mut self, request: audio::AudioRequest<IpcBytes>) -> audio::AudioId;
449
450    /// Cache an streaming audio resource.
451    ///
452    /// The audio is decoded as bytes are buffered in.
453    ///
454    /// # Unimplemented
455    ///
456    /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
457    pub fn add_audio_pro(&mut self, request: audio::AudioRequest<IpcReceiver<IpcBytes>>) -> audio::AudioId;
458
459    /// Remove an audio from cache.
460    ///
461    /// Note that if the audio is in use by a playback it will remain in memory until the playback ends.
462    ///
463    /// # Unimplemented
464    ///
465    /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
466    pub fn forget_audio(&mut self, id: audio::AudioId);
467
468    /// Returns a list of image decoders supported by this implementation.
469    ///
470    /// Each text is the lower-case file extension, without the dot.
471    ///
472    /// # Unimplemented
473    ///
474    /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
475    pub fn audio_decoders(&mut self) -> Vec<Txt>;
476
477    /// Start playing audio.
478    ///
479    /// # Unimplemented
480    ///
481    /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
482    pub fn playback(&mut self, request: audio::PlaybackRequest) -> audio::PlaybackId;
483
484    /// Update an existing playback.
485    ///
486    /// # Unimplemented
487    ///
488    /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
489    pub fn playback_update(&mut self, id: audio::PlaybackId, request: audio::PlaybackUpdateRequest);
490
491    /// Add a raw font resource to the window renderer.
492    ///
493    /// Returns the new font key.
494    pub fn add_font_face(&mut self, id: WindowId, bytes: font::IpcFontBytes, index: u32) -> FontFaceId;
495
496    /// Delete the font resource in the window renderer.
497    pub fn delete_font_face(&mut self, id: WindowId, font_face_id: FontFaceId);
498
499    /// Add a sized font to the window renderer.
500    ///
501    /// Returns the new fond ID.
502    pub fn add_font(
503        &mut self,
504        id: WindowId,
505        font_face_id: FontFaceId,
506        glyph_size: Px,
507        options: FontOptions,
508        variations: Vec<(FontVariationName, f32)>,
509    ) -> FontId;
510
511    /// Delete a font instance.
512    pub fn delete_font(&mut self, id: WindowId, font_id: FontId);
513
514    /// Sets if the headed window is in *capture-mode*. If `true` the resources used to capture
515    /// a screenshot may be kept in memory to be reused in the next screenshot capture.
516    ///
517    /// Note that capture must still be requested in each frame request.
518    pub fn set_capture_mode(&mut self, id: WindowId, enable: bool);
519
520    /// Create a new image resource from the current rendered frame.
521    ///
522    /// If `mask` is set captures an A8 mask, otherwise captures a full BGRA8 image.
523    ///
524    /// Returns immediately if an [`Event::FrameImageReady`] will be send when the image is ready.
525    /// Returns `0` if the window is not found.
526    pub fn frame_image(&mut self, id: WindowId, mask: Option<ImageMaskMode>) -> ImageId;
527
528    /// Create a new image from a selection of the current rendered frame.
529    ///
530    /// If `mask` is set captures an A8 mask, otherwise captures a full BGRA8 image.
531    ///
532    /// Returns immediately if an [`Event::FrameImageReady`] will be send when the image is ready.
533    /// Returns `0` if the window is not found.
534    pub fn frame_image_rect(&mut self, id: WindowId, rect: PxRect, mask: Option<ImageMaskMode>) -> ImageId;
535
536    /// Set the video mode used when the window is in exclusive fullscreen.
537    pub fn set_video_mode(&mut self, id: WindowId, mode: window::VideoMode);
538
539    /// Render a new frame.
540    pub fn render(&mut self, id: WindowId, frame: window::FrameRequest);
541
542    /// Update the current frame and re-render it.
543    pub fn render_update(&mut self, id: WindowId, frame: window::FrameUpdateRequest);
544
545    /// Update the window's accessibility info tree.
546    pub fn access_update(&mut self, id: WindowId, update: access::AccessTreeUpdate);
547
548    /// Shows a native message dialog for the window.
549    ///
550    /// Returns an ID that identifies the response event.
551    pub fn message_dialog(&mut self, id: WindowId, dialog: dialog::MsgDialog) -> DialogId;
552
553    /// Shows a native file/folder picker for the window.
554    ///
555    /// Returns the ID that identifies the response event.
556    pub fn file_dialog(&mut self, id: WindowId, dialog: dialog::FileDialog) -> DialogId;
557
558    /// Get the clipboard content that matches the `data_type`.
559    pub fn read_clipboard(&mut self, data_type: clipboard::ClipboardType) -> Result<ClipboardData, ClipboardError>;
560
561    /// Set the clipboard content.
562    pub fn write_clipboard(&mut self, data: ClipboardData) -> Result<(), ClipboardError>;
563
564    /// Start a drag and drop operation, if the window is pressed.
565    pub fn start_drag_drop(
566        &mut self,
567        id: WindowId,
568        data: Vec<DragDropData>,
569        allowed_effects: DragDropEffect,
570    ) -> Result<DragDropId, DragDropError>;
571
572    /// Cancel a drag and drop operation.
573    pub fn cancel_drag_drop(&mut self, id: WindowId, drag_id: DragDropId);
574
575    /// Notify the drag source of what effect was applied for a received drag&drop.
576    pub fn drag_dropped(&mut self, id: WindowId, drop_id: DragDropId, applied: DragDropEffect);
577
578    /// Enable or disable IME by setting a cursor area.
579    ///
580    /// In mobile platforms also shows the software keyboard for `Some(_)` and hides it for `None`.
581    pub fn set_ime_area(&mut self, id: WindowId, area: Option<DipRect>);
582
583    /// Attempt to set a system wide shutdown warning associated with the window.
584    ///
585    /// Operating systems that support this show the `reason` in a warning for the user, it must be a short text
586    /// that identifies the critical operation that cannot be cancelled.
587    ///
588    /// Note that there is no guarantee that the view-process or operating system will actually set a block, there
589    /// is no error result because operating systems can silently ignore block requests at any moment, even after
590    /// an initial successful block.
591    ///
592    /// Set to an empty text to remove the warning.
593    pub fn set_system_shutdown_warn(&mut self, id: WindowId, reason: Txt);
594
595    /// Licenses that may be required to be displayed in the app about screen.
596    ///
597    /// This is specially important for prebuilt view users, as the tools that scrap licenses
598    /// may not find the prebuilt dependencies.
599    pub fn third_party_licenses(&mut self) -> Vec<zng_tp_licenses::LicenseUsed>;
600
601    /// Call the API extension.
602    ///
603    /// The `extension_id` is the index of an extension in the extensions list provided by the view-process on init.
604    /// The `extension_request` is any data required by the extension.
605    ///
606    /// Returns the extension response or [`ApiExtensionPayload::unknown_extension`] if the `extension_id` is
607    /// not on the list, or [`ApiExtensionPayload::invalid_request`] if the `extension_request` is not in a
608    /// format expected by the extension.
609    pub fn app_extension(&mut self, extension_id: ApiExtensionId, extension_request: ApiExtensionPayload) -> ApiExtensionPayload;
610
611    /// Call the API extension.
612    ///
613    /// This is similar to [`Api::app_extension`], but is targeting the instance of an extension associated
614    /// with the `id` window or headless surface.
615    pub fn window_extension(
616        &mut self,
617        id: WindowId,
618        extension_id: ApiExtensionId,
619        extension_request: ApiExtensionPayload,
620    ) -> ApiExtensionPayload;
621
622    /// Call the API extension.
623    ///
624    /// This is similar to [`Api::app_extension`], but is targeting the instance of an extension associated
625    /// with the `id` renderer.
626    pub fn render_extension(
627        &mut self,
628        id: WindowId,
629        extension_id: ApiExtensionId,
630        extension_request: ApiExtensionPayload,
631    ) -> ApiExtensionPayload;
632
633    /// Returns the `count` and notifies [`Event::Pong`] after ensuring the view-process is responsive.
634    ///
635    /// The app-process and view-process automatically monitor message frequency to detect when the paired process
636    /// is stuck. View-process implementers must only ensure the response event goes through its *main loop* to get an
637    /// accurate read of it is stuck.
638    pub fn ping(&mut self, count: u16) -> u16;
639}
640
641pub(crate) type AnyResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;