1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
// Copyright 2020 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Simple handle for submitting external events.
use std::any::Any;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use crate::command::SelectorSymbol;
use crate::shell::IdleHandle;
use crate::win_handler::EXT_EVENT_IDLE_TOKEN;
use crate::{Command, Data, DruidHandler, Selector, Target, WindowId};
pub(crate) type ExtCommand = (SelectorSymbol, Box<dyn Any + Send>, Target);
/// A thing that can move into other threads and be used to submit commands back
/// to the running application.
///
/// This API is preliminary, and may be changed or removed without warning.
#[derive(Clone)]
pub struct ExtEventSink {
queue: Arc<Mutex<VecDeque<ExtCommand>>>,
handle: Arc<Mutex<Option<IdleHandle>>>,
}
/// The stuff that we hold onto inside the app that is related to the
/// handling of external events.
#[derive(Default)]
pub(crate) struct ExtEventHost {
/// A shared queue of items that have been sent to us.
queue: Arc<Mutex<VecDeque<ExtCommand>>>,
/// This doesn't exist when the app starts and it can go away if a window closes, so we keep a
/// reference here and can update it when needed. Note that this reference is shared with all
/// [`ExtEventSink`]s, so that we can update them too.
handle: Arc<Mutex<Option<IdleHandle>>>,
/// The window that the handle belongs to, so we can keep track of when
/// we need to get a new handle.
pub(crate) handle_window_id: Option<WindowId>,
}
/// An error that occurs if an external event cannot be submitted.
/// This probably means that the application has gone away.
#[derive(Debug, Clone)]
pub struct ExtEventError;
impl ExtEventHost {
pub(crate) fn new() -> Self {
Default::default()
}
pub(crate) fn make_sink(&self) -> ExtEventSink {
ExtEventSink {
queue: self.queue.clone(),
handle: self.handle.clone(),
}
}
pub(crate) fn set_idle(&mut self, handle: IdleHandle, window_id: WindowId) {
self.handle.lock().unwrap().replace(handle);
self.handle_window_id = Some(window_id);
}
pub(crate) fn has_pending_items(&self) -> bool {
!self.queue.lock().unwrap().is_empty()
}
pub(crate) fn recv(&mut self) -> Option<Command> {
self.queue
.lock()
.unwrap()
.pop_front()
.map(|(selector, payload, target)| Command::from_ext(selector, payload, target))
}
}
impl ExtEventSink {
/// Submit a [`Command`] to the running application.
///
/// [`Command`] is not thread safe, so you cannot submit it directly;
/// instead you have to pass the [`Selector`] and the payload
/// separately, and it will be turned into a [`Command`] when it is received.
///
/// The `payload` must implement `Any + Send`.
///
/// If the [`Target::Auto`] is equivalent to [`Target::Global`].
pub fn submit_command<T: Any + Send>(
&self,
selector: Selector<T>,
payload: impl Into<Box<T>>,
target: impl Into<Target>,
) -> Result<(), ExtEventError> {
let target = target.into();
let payload = payload.into();
if let Some(handle) = self.handle.lock().unwrap().as_mut() {
handle.schedule_idle(EXT_EVENT_IDLE_TOKEN);
}
self.queue.lock().map_err(|_| ExtEventError)?.push_back((
selector.symbol(),
payload,
target,
));
Ok(())
}
/// Schedule an idle callback.
///
/// `T` must be the application's root `Data` type (the type provided to [`AppLauncher::launch`]).
///
/// Add an idle callback, which is called (once) when the message loop
/// is empty. The idle callback will be run from the main UI thread.
///
/// Note: the name "idle" suggests that it will be scheduled with a lower
/// priority than other UI events, but that's not necessarily the case.
///
/// [`AppLauncher::launch`]: crate::AppLauncher::launch
pub fn add_idle_callback<T: 'static + Data>(&self, cb: impl FnOnce(&mut T) + Send + 'static) {
let mut handle = self.handle.lock().unwrap();
if let Some(handle) = handle.as_mut() {
handle.add_idle(|win_handler| {
if let Some(win_handler) = win_handler.as_any().downcast_mut::<DruidHandler<T>>() {
win_handler.app_state.handle_idle_callback(cb);
} else {
debug_panic!(
"{} is not the type of root data",
std::any::type_name::<T>()
);
}
});
}
}
}
impl std::fmt::Display for ExtEventError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Window missing for external event")
}
}
impl std::error::Error for ExtEventError {}