This commit is contained in:
Antonio Scandurra 2023-10-22 16:27:23 +02:00
parent 5423012368
commit 6ffeb048b3
5 changed files with 132 additions and 152 deletions

View file

@ -11,12 +11,11 @@ use async_tungstenite::tungstenite::{
http::{Request, StatusCode}, http::{Request, StatusCode},
}; };
use futures::{ use futures::{
future::LocalBoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _, future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _, TryStreamExt,
TryStreamExt,
}; };
use gpui2::{ use gpui2::{
serde_json, AnyHandle, AnyWeakHandle, AnyWindowHandle, AppContext, AsyncAppContext, serde_json, AnyHandle, AnyWeakHandle, AnyWindowHandle, AppContext, AsyncAppContext, Handle,
AsyncWindowContext, Handle, SemanticVersion, Task, ViewContext, WeakHandle, WindowId, SemanticVersion, Task, ViewContext,
}; };
use lazy_static::lazy_static; use lazy_static::lazy_static;
use parking_lot::RwLock; use parking_lot::RwLock;
@ -240,7 +239,7 @@ struct ClientState {
Box<dyn AnyTypedEnvelope>, Box<dyn AnyTypedEnvelope>,
&Arc<Client>, &Arc<Client>,
AsyncAppContext, AsyncAppContext,
) -> LocalBoxFuture<'static, Result<()>>, ) -> BoxFuture<'static, Result<()>>,
>, >,
>, >,
} }
@ -248,7 +247,7 @@ struct ClientState {
enum WeakSubscriber { enum WeakSubscriber {
Entity { Entity {
handle: AnyWeakHandle, handle: AnyWeakHandle,
window_id: Option<WindowId>, window_handle: Option<AnyWindowHandle>,
}, },
Pending(Vec<Box<dyn AnyTypedEnvelope>>), Pending(Vec<Box<dyn AnyTypedEnvelope>>),
} }
@ -336,7 +335,7 @@ where
id, id,
WeakSubscriber::Entity { WeakSubscriber::Entity {
handle: model.downgrade().into(), handle: model.downgrade().into(),
window_id: None, window_handle: None,
}, },
); );
drop(state); drop(state);
@ -511,7 +510,7 @@ impl Client {
}, },
&cx, &cx,
); );
cx.background().timer(delay).await; cx.executor().timer(delay).await;
delay = delay delay = delay
.mul_f32(rng.gen_range(1.0..=2.0)) .mul_f32(rng.gen_range(1.0..=2.0))
.min(reconnect_interval); .min(reconnect_interval);
@ -522,7 +521,7 @@ impl Client {
})); }));
} }
Status::SignedOut | Status::UpgradeRequired => { Status::SignedOut | Status::UpgradeRequired => {
cx.read(|cx| self.telemetry.set_authenticated_user_info(None, false, cx)); cx.update(|cx| self.telemetry.set_authenticated_user_info(None, false, cx));
state._reconnect_task.take(); state._reconnect_task.take();
} }
_ => {} _ => {}
@ -533,13 +532,16 @@ impl Client {
self: &Arc<Self>, self: &Arc<Self>,
remote_id: u64, remote_id: u64,
cx: &mut ViewContext<T>, cx: &mut ViewContext<T>,
) -> Subscription { ) -> Subscription
where
T: 'static + Send + Sync,
{
let id = (TypeId::of::<T>(), remote_id); let id = (TypeId::of::<T>(), remote_id);
self.state.write().entities_by_type_and_remote_id.insert( self.state.write().entities_by_type_and_remote_id.insert(
id, id,
WeakSubscriber::Entity { WeakSubscriber::Entity {
handle: cx.handle().into_any(), handle: cx.handle().into(),
window_id: Some(cx.window_id()), window_handle: Some(cx.window_handle()),
}, },
); );
Subscription::Entity { Subscription::Entity {
@ -573,33 +575,28 @@ impl Client {
#[track_caller] #[track_caller]
pub fn add_message_handler<M, E, H, F>( pub fn add_message_handler<M, E, H, F>(
self: &Arc<Self>, self: &Arc<Self>,
model: Handle<E>, entity: Handle<E>,
handler: H, handler: H,
) -> Subscription ) -> Subscription
where where
M: EnvelopedMessage, M: EnvelopedMessage,
E: 'static + Send + Sync, E: 'static + Send + Sync,
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F, H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
F: 'static + Future<Output = Result<()>>, F: 'static + Future<Output = Result<()>> + Send,
{ {
let message_type_id = TypeId::of::<M>(); let message_type_id = TypeId::of::<M>();
let mut state = self.state.write(); let mut state = self.state.write();
state state
.models_by_message_type .models_by_message_type
.insert(message_type_id, model.downgrade().into_any()); .insert(message_type_id, entity.downgrade().into());
let prev_handler = state.message_handlers.insert( let prev_handler = state.message_handlers.insert(
message_type_id, message_type_id,
Arc::new(move |handle, envelope, client, cx| { Arc::new(move |subscriber, envelope, client, cx| {
let handle = if let Subscriber::Model(handle) = handle { let subscriber = subscriber.handle.downcast::<E>().unwrap();
handle
} else {
unreachable!();
};
let model = handle.downcast::<E>().unwrap();
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap(); let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
handler(model, *envelope, client.clone(), cx).boxed_local() handler(subscriber, *envelope, client.clone(), cx).boxed()
}), }),
); );
if prev_handler.is_some() { if prev_handler.is_some() {
@ -627,7 +624,7 @@ impl Client {
M: RequestMessage, M: RequestMessage,
E: 'static + Send + Sync, E: 'static + Send + Sync,
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F, H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
F: 'static + Future<Output = Result<M::Response>>, F: 'static + Future<Output = Result<M::Response>> + Send,
{ {
self.add_message_handler(model, move |handle, envelope, this, cx| { self.add_message_handler(model, move |handle, envelope, this, cx| {
Self::respond_to_request( Self::respond_to_request(
@ -638,37 +635,12 @@ impl Client {
}) })
} }
pub fn add_view_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
where
M: EntityMessage,
H: 'static
+ Send
+ Sync
+ Fn(WeakHandle<E>, TypedEnvelope<M>, Arc<Self>, AsyncWindowContext) -> F,
F: 'static + Future<Output = Result<()>>,
{
self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, client, cx| {
if let Some(window_handle) = subscriber.window_handle {
cx.update_window(subscriber, |cx| {
handler(
subscriber.handle.downcast::<E>().unwrap(),
message,
client,
cx,
)
})
} else {
panic!()
}
})
}
pub fn add_model_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H) pub fn add_model_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
where where
M: EntityMessage, M: EntityMessage,
E: 'static + Send + Sync, E: 'static + Send + Sync,
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F, H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
F: 'static + Future<Output = Result<()>>, F: 'static + Future<Output = Result<()>> + Send,
{ {
self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, client, cx| { self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, client, cx| {
handler( handler(
@ -687,7 +659,7 @@ impl Client {
+ Send + Send
+ Sync + Sync
+ Fn(Subscriber, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F, + Fn(Subscriber, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
F: 'static + Future<Output = Result<()>>, F: 'static + Future<Output = Result<()>> + Send,
{ {
let model_type_id = TypeId::of::<E>(); let model_type_id = TypeId::of::<E>();
let message_type_id = TypeId::of::<M>(); let message_type_id = TypeId::of::<M>();
@ -713,7 +685,7 @@ impl Client {
message_type_id, message_type_id,
Arc::new(move |handle, envelope, client, cx| { Arc::new(move |handle, envelope, client, cx| {
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap(); let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
handler(handle, *envelope, client.clone(), cx).boxed_local() handler(handle, *envelope, client.clone(), cx).boxed()
}), }),
); );
if prev_handler.is_some() { if prev_handler.is_some() {
@ -726,7 +698,7 @@ impl Client {
M: EntityMessage + RequestMessage, M: EntityMessage + RequestMessage,
E: 'static + Send + Sync, E: 'static + Send + Sync,
H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F, H: 'static + Send + Sync + Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
F: 'static + Future<Output = Result<M::Response>>, F: 'static + Future<Output = Result<M::Response>> + Send,
{ {
self.add_model_message_handler(move |entity, envelope, client, cx| { self.add_model_message_handler(move |entity, envelope, client, cx| {
Self::respond_to_request::<M, _>( Self::respond_to_request::<M, _>(
@ -737,25 +709,6 @@ impl Client {
}) })
} }
pub fn add_view_request_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
where
M: EntityMessage + RequestMessage,
E: 'static + Send + Sync,
H: 'static
+ Send
+ Sync
+ Fn(Handle<E>, TypedEnvelope<M>, Arc<Self>, AsyncWindowContext) -> F,
F: 'static + Future<Output = Result<M::Response>>,
{
self.add_view_message_handler(move |entity, envelope, client, cx| {
Self::respond_to_request::<M, _>(
envelope.receipt(),
handler(entity, envelope, client.clone(), cx),
client,
)
})
}
async fn respond_to_request<T: RequestMessage, F: Future<Output = Result<T::Response>>>( async fn respond_to_request<T: RequestMessage, F: Future<Output = Result<T::Response>>>(
receipt: Receipt<T>, receipt: Receipt<T>,
response: F, response: F,
@ -778,11 +731,11 @@ impl Client {
} }
} }
pub fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool { pub async fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool {
read_credentials_from_keychain(cx).is_some() read_credentials_from_keychain(cx).await.is_some()
} }
#[async_recursion(?Send)] #[async_recursion]
pub async fn authenticate_and_connect( pub async fn authenticate_and_connect(
self: &Arc<Self>, self: &Arc<Self>,
try_keychain: bool, try_keychain: bool,
@ -840,7 +793,7 @@ impl Client {
self.set_status(Status::Reconnecting, cx); self.set_status(Status::Reconnecting, cx);
} }
let mut timeout = futures::FutureExt::fuse(cx.executor()?.timer(CONNECTION_TIMEOUT)); let mut timeout = futures::FutureExt::fuse(cx.executor().timer(CONNECTION_TIMEOUT));
futures::select_biased! { futures::select_biased! {
connection = self.establish_connection(&credentials, cx).fuse() => { connection = self.establish_connection(&credentials, cx).fuse() => {
match connection { match connection {
@ -891,7 +844,7 @@ impl Client {
conn: Connection, conn: Connection,
cx: &AsyncAppContext, cx: &AsyncAppContext,
) -> Result<()> { ) -> Result<()> {
let executor = cx.executor()?; let executor = cx.executor();
log::info!("add connection to peer"); log::info!("add connection to peer");
let (connection_id, handle_io, mut incoming) = self.peer.add_connection(conn, { let (connection_id, handle_io, mut incoming) = self.peer.add_connection(conn, {
let executor = executor.clone(); let executor = executor.clone();
@ -955,7 +908,7 @@ impl Client {
} }
} }
} }
})? })
.detach(); .detach();
cx.spawn({ cx.spawn({
@ -978,7 +931,8 @@ impl Client {
} }
} }
} }
})?; })
.detach();
Ok(()) Ok(())
} }
@ -1042,13 +996,8 @@ impl Client {
credentials: &Credentials, credentials: &Credentials,
cx: &AsyncAppContext, cx: &AsyncAppContext,
) -> Task<Result<Connection, EstablishConnectionError>> { ) -> Task<Result<Connection, EstablishConnectionError>> {
let executor = match cx.executor() {
Ok(executor) => executor,
Err(error) => return Task::ready(Err(error)),
};
let use_preview_server = cx let use_preview_server = cx
.try_read_global(|channel: &ReleaseChannel, _| channel != ReleaseChannel::Stable) .try_read_global(|channel: &ReleaseChannel, _| *channel != ReleaseChannel::Stable)
.unwrap_or(false); .unwrap_or(false);
let request = Request::builder() let request = Request::builder()
@ -1059,7 +1008,7 @@ impl Client {
.header("x-zed-protocol-version", rpc::PROTOCOL_VERSION); .header("x-zed-protocol-version", rpc::PROTOCOL_VERSION);
let http = self.http.clone(); let http = self.http.clone();
executor.spawn(async move { cx.executor().spawn(async move {
let mut rpc_url = Self::get_rpc_url(http, use_preview_server).await?; let mut rpc_url = Self::get_rpc_url(http, use_preview_server).await?;
let rpc_host = rpc_url let rpc_host = rpc_url
.host_str() .host_str()
@ -1130,15 +1079,15 @@ impl Client {
write!(&mut url, "&impersonate={}", impersonate_login).unwrap(); write!(&mut url, "&impersonate={}", impersonate_login).unwrap();
} }
platform.open_url(&url); cx.run_on_main(|cx| cx.open_url(&url))?.await;
// Receive the HTTP request from the user's browser. Retrieve the user id and encrypted // Receive the HTTP request from the user's browser. Retrieve the user id and encrypted
// access token from the query params. // access token from the query params.
// //
// TODO - Avoid ever starting more than one HTTP server. Maybe switch to using a // TODO - Avoid ever starting more than one HTTP server. Maybe switch to using a
// custom URL scheme instead of this local HTTP server. // custom URL scheme instead of this local HTTP server.
let (user_id, access_token) = executor let (user_id, access_token) = cx
.spawn(async move { .spawn(|_| async move {
for _ in 0..100 { for _ in 0..100 {
if let Some(req) = server.recv_timeout(Duration::from_secs(1))? { if let Some(req) = server.recv_timeout(Duration::from_secs(1))? {
let path = req.url(); let path = req.url();
@ -1181,14 +1130,13 @@ impl Client {
let access_token = private_key let access_token = private_key
.decrypt_string(&access_token) .decrypt_string(&access_token)
.context("failed to decrypt access token")?; .context("failed to decrypt access token")?;
platform.activate(true); cx.run_on_main(|cx| cx.activate(true))?.await;
Ok(Credentials { Ok(Credentials {
user_id: user_id.parse()?, user_id: user_id.parse()?,
access_token, access_token,
}) })
}) })
.unwrap_or_else(|error| Task::ready(Err(error)))
} }
async fn authenticate_as_admin( async fn authenticate_as_admin(
@ -1344,12 +1292,16 @@ impl Client {
return; return;
} }
Some(weak_subscriber @ _) => match weak_subscriber { Some(weak_subscriber @ _) => match weak_subscriber {
WeakSubscriber::Model(handle) => { WeakSubscriber::Entity {
subscriber = handle.upgrade(cx).map(Subscriber::Model); handle,
} window_handle,
WeakSubscriber::View(handle) => { } => {
subscriber = Some(Subscriber::View(handle.clone())); subscriber = handle.upgrade().map(|handle| Subscriber {
handle,
window_handle: window_handle.clone(),
});
} }
WeakSubscriber::Pending(_) => {} WeakSubscriber::Pending(_) => {}
}, },
_ => {} _ => {}
@ -1379,8 +1331,7 @@ impl Client {
sender_id, sender_id,
type_name type_name
); );
cx.foreground() cx.spawn_on_main(|_| async move {
.spawn(async move {
match future.await { match future.await {
Ok(()) => { Ok(()) => {
log::debug!( log::debug!(

View file

@ -1,5 +1,5 @@
use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL}; use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
use gpui2::{serde_json, AppContext, Executor, Task}; use gpui2::{serde_json, AppContext, AppMetadata, Executor, Task};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use parking_lot::Mutex; use parking_lot::Mutex;
use serde::Serialize; use serde::Serialize;
@ -15,15 +15,12 @@ pub struct Telemetry {
state: Mutex<TelemetryState>, state: Mutex<TelemetryState>,
} }
#[derive(Default)]
struct TelemetryState { struct TelemetryState {
metrics_id: Option<Arc<str>>, // Per logged-in user metrics_id: Option<Arc<str>>, // Per logged-in user
installation_id: Option<Arc<str>>, // Per app installation (different for dev, preview, and stable) installation_id: Option<Arc<str>>, // Per app installation (different for dev, preview, and stable)
session_id: Option<Arc<str>>, // Per app launch session_id: Option<Arc<str>>, // Per app launch
app_version: Option<Arc<str>>,
release_channel: Option<&'static str>, release_channel: Option<&'static str>,
os_name: &'static str, app_metadata: AppMetadata,
os_version: Option<Arc<str>>,
architecture: &'static str, architecture: &'static str,
clickhouse_events_queue: Vec<ClickhouseEventWrapper>, clickhouse_events_queue: Vec<ClickhouseEventWrapper>,
flush_clickhouse_events_task: Option<Task<()>>, flush_clickhouse_events_task: Option<Task<()>>,
@ -115,7 +112,6 @@ const DEBOUNCE_INTERVAL: Duration = Duration::from_secs(30);
impl Telemetry { impl Telemetry {
pub fn new(client: Arc<dyn HttpClient>, cx: &AppContext) -> Arc<Self> { pub fn new(client: Arc<dyn HttpClient>, cx: &AppContext) -> Arc<Self> {
let platform = cx.platform();
let release_channel = if cx.has_global::<ReleaseChannel>() { let release_channel = if cx.has_global::<ReleaseChannel>() {
Some(cx.global::<ReleaseChannel>().display_name()) Some(cx.global::<ReleaseChannel>().display_name())
} else { } else {
@ -124,12 +120,10 @@ impl Telemetry {
// TODO: Replace all hardware stuff with nested SystemSpecs json // TODO: Replace all hardware stuff with nested SystemSpecs json
let this = Arc::new(Self { let this = Arc::new(Self {
http_client: client, http_client: client,
executor: cx.background().clone(), executor: cx.executor().clone(),
state: Mutex::new(TelemetryState { state: Mutex::new(TelemetryState {
os_name: platform.os_name().into(), app_metadata: cx.app_metadata(),
os_version: platform.os_version().ok().map(|v| v.to_string().into()),
architecture: env::consts::ARCH, architecture: env::consts::ARCH,
app_version: platform.app_version().ok().map(|v| v.to_string().into()),
release_channel, release_channel,
installation_id: None, installation_id: None,
metrics_id: None, metrics_id: None,
@ -196,7 +190,7 @@ impl Telemetry {
core_count: system.cpus().len() as u32, core_count: system.cpus().len() as u32,
}; };
let telemetry_settings = cx.update(|cx| *settings::get::<TelemetrySettings>(cx)); let telemetry_settings = cx.update(|cx| *settings2::get::<TelemetrySettings>(cx));
this.report_clickhouse_event(memory_event, telemetry_settings); this.report_clickhouse_event(memory_event, telemetry_settings);
this.report_clickhouse_event(cpu_event, telemetry_settings); this.report_clickhouse_event(cpu_event, telemetry_settings);
@ -211,7 +205,7 @@ impl Telemetry {
is_staff: bool, is_staff: bool,
cx: &AppContext, cx: &AppContext,
) { ) {
if !settings::get::<TelemetrySettings>(cx).metrics { if !settings2::get::<TelemetrySettings>(cx).metrics {
return; return;
} }

View file

@ -9,9 +9,9 @@ use refineable::Refineable;
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{ use crate::{
current_platform, image_cache::ImageCache, Action, AssetSource, Context, DispatchPhase, current_platform, image_cache::ImageCache, Action, AppMetadata, AssetSource, Context,
DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId, DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap,
MainThread, MainThreadOnly, Platform, SemanticVersion, SharedString, SubscriberSet, LayoutId, MainThread, MainThreadOnly, Platform, SemanticVersion, SharedString, SubscriberSet,
SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext,
WindowHandle, WindowId, WindowHandle, WindowId,
}; };
@ -49,13 +49,25 @@ impl App {
http_client: Arc<dyn HttpClient>, http_client: Arc<dyn HttpClient>,
) -> Self { ) -> Self {
let executor = platform.executor(); let executor = platform.executor();
assert!(
executor.is_main_thread(),
"must construct App on main thread"
);
let text_system = Arc::new(TextSystem::new(platform.text_system()));
let entities = EntityMap::new(); let entities = EntityMap::new();
let unit_entity = entities.insert(entities.reserve(), ()); let unit_entity = entities.insert(entities.reserve(), ());
let app_metadata = AppMetadata {
os_name: platform.os_name(),
os_version: platform.os_version().unwrap_or_default(),
app_version: platform.app_version().unwrap_or_default(),
};
Self(Arc::new_cyclic(|this| { Self(Arc::new_cyclic(|this| {
Mutex::new(AppContext { Mutex::new(AppContext {
this: this.clone(), this: this.clone(),
text_system: Arc::new(TextSystem::new(platform.text_system())), text_system,
platform: MainThreadOnly::new(platform, executor.clone()), platform: MainThreadOnly::new(platform, executor.clone()),
app_metadata,
flushing_effects: false, flushing_effects: false,
pending_updates: 0, pending_updates: 0,
next_frame_callbacks: Default::default(), next_frame_callbacks: Default::default(),
@ -128,16 +140,8 @@ impl App {
self self
} }
pub fn app_version(&self) -> Result<SemanticVersion> { pub fn metadata(&self) -> AppMetadata {
self.0.lock().platform.borrow_on_main_thread().app_version() self.0.lock().app_metadata.clone()
}
pub fn os_name(&self) -> &'static str {
self.0.lock().platform.borrow_on_main_thread().os_name()
}
pub fn os_version(&self) -> Result<SemanticVersion> {
self.0.lock().platform.borrow_on_main_thread().os_version()
} }
pub fn executor(&self) -> Executor { pub fn executor(&self) -> Executor {
@ -158,6 +162,7 @@ type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<d
pub struct AppContext { pub struct AppContext {
this: Weak<Mutex<AppContext>>, this: Weak<Mutex<AppContext>>,
pub(crate) platform: MainThreadOnly<dyn Platform>, pub(crate) platform: MainThreadOnly<dyn Platform>,
app_metadata: AppMetadata,
text_system: Arc<TextSystem>, text_system: Arc<TextSystem>,
flushing_effects: bool, flushing_effects: bool,
pending_updates: usize, pending_updates: usize,
@ -184,6 +189,10 @@ pub struct AppContext {
} }
impl AppContext { impl AppContext {
pub fn app_metadata(&self) -> AppMetadata {
self.app_metadata.clone()
}
pub fn refresh(&mut self) { pub fn refresh(&mut self) {
self.push_effect(Effect::Refresh); self.push_effect(Effect::Refresh);
} }
@ -379,7 +388,10 @@ impl AppContext {
} }
pub fn to_async(&self) -> AsyncAppContext { pub fn to_async(&self) -> AsyncAppContext {
AsyncAppContext(unsafe { mem::transmute(self.this.clone()) }) AsyncAppContext {
app: unsafe { mem::transmute(self.this.clone()) },
executor: self.executor.clone(),
}
} }
pub fn executor(&self) -> &Executor { pub fn executor(&self) -> &Executor {
@ -639,7 +651,7 @@ impl MainThread<AppContext> {
self.platform.borrow_on_main_thread() self.platform.borrow_on_main_thread()
} }
pub fn activate(&mut self, ignoring_other_apps: bool) { pub fn activate(&self, ignoring_other_apps: bool) {
self.platform().activate(ignoring_other_apps); self.platform().activate(ignoring_other_apps);
} }
@ -655,6 +667,10 @@ impl MainThread<AppContext> {
self.platform().delete_credentials(url) self.platform().delete_credentials(url)
} }
pub fn open_url(&self, url: &str) {
self.platform().open_url(url);
}
pub fn open_window<S: 'static + Send + Sync>( pub fn open_window<S: 'static + Send + Sync>(
&mut self, &mut self,
options: crate::WindowOptions, options: crate::WindowOptions,

View file

@ -8,7 +8,10 @@ use parking_lot::Mutex;
use std::{future::Future, sync::Weak}; use std::{future::Future, sync::Weak};
#[derive(Clone)] #[derive(Clone)]
pub struct AsyncAppContext(pub(crate) Weak<Mutex<AppContext>>); pub struct AsyncAppContext {
pub(crate) app: Weak<Mutex<AppContext>>,
pub(crate) executor: Executor,
}
impl Context for AsyncAppContext { impl Context for AsyncAppContext {
type EntityContext<'a, 'w, T: 'static + Send + Sync> = ModelContext<'a, T>; type EntityContext<'a, 'w, T: 'static + Send + Sync> = ModelContext<'a, T>;
@ -19,7 +22,7 @@ impl Context for AsyncAppContext {
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T, build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
) -> Self::Result<Handle<T>> { ) -> Self::Result<Handle<T>> {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock(); // Need this to compile let mut lock = app.lock(); // Need this to compile
@ -32,7 +35,7 @@ impl Context for AsyncAppContext {
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R, update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
) -> Self::Result<R> { ) -> Self::Result<R> {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock(); // Need this to compile let mut lock = app.lock(); // Need this to compile
@ -43,7 +46,7 @@ impl Context for AsyncAppContext {
impl AsyncAppContext { impl AsyncAppContext {
pub fn refresh(&mut self) -> Result<()> { pub fn refresh(&mut self) -> Result<()> {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock(); // Need this to compile let mut lock = app.lock(); // Need this to compile
@ -51,13 +54,17 @@ impl AsyncAppContext {
Ok(()) Ok(())
} }
pub fn executor(&self) -> Result<Executor> { pub fn executor(&self) -> &Executor {
&self.executor
}
pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let lock = app.lock(); // Need this to compile let mut lock = app.lock();
Ok(lock.executor().clone()) Ok(f(&mut *lock))
} }
pub fn read_window<R>( pub fn read_window<R>(
@ -66,7 +73,7 @@ impl AsyncAppContext {
update: impl FnOnce(&WindowContext) -> R, update: impl FnOnce(&WindowContext) -> R,
) -> Result<R> { ) -> Result<R> {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let mut app_context = app.lock(); let mut app_context = app.lock();
@ -79,27 +86,32 @@ impl AsyncAppContext {
update: impl FnOnce(&mut WindowContext) -> R, update: impl FnOnce(&mut WindowContext) -> R,
) -> Result<R> { ) -> Result<R> {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let mut app_context = app.lock(); let mut app_context = app.lock();
app_context.update_window(handle.id, update) app_context.update_window(handle.id, update)
} }
pub fn spawn<Fut, R>( pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
&self,
f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static,
) -> Result<Task<R>>
where where
Fut: Future<Output = R> + Send + 'static, Fut: Future<Output = R> + Send + 'static,
R: Send + 'static, R: Send + 'static,
{ {
let app = self let this = self.clone();
.0 self.executor.spawn(async move { f(this).await })
.upgrade() }
.ok_or_else(|| anyhow!("app was released"))?;
let app_context = app.lock(); pub fn spawn_on_main<Fut, R>(
Ok(app_context.spawn(f)) &self,
f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static,
) -> Task<R>
where
Fut: Future<Output = R> + 'static,
R: Send + 'static,
{
let this = self.clone();
self.executor.spawn_on_main(|| f(this))
} }
pub fn run_on_main<R>( pub fn run_on_main<R>(
@ -110,7 +122,7 @@ impl AsyncAppContext {
R: Send + 'static, R: Send + 'static,
{ {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let mut app_context = app.lock(); let mut app_context = app.lock();
@ -119,7 +131,7 @@ impl AsyncAppContext {
pub fn has_global<G: 'static + Send + Sync>(&self) -> Result<bool> { pub fn has_global<G: 'static + Send + Sync>(&self) -> Result<bool> {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let lock = app.lock(); // Need this to compile let lock = app.lock(); // Need this to compile
@ -131,7 +143,7 @@ impl AsyncAppContext {
read: impl FnOnce(&G, &AppContext) -> R, read: impl FnOnce(&G, &AppContext) -> R,
) -> Result<R> { ) -> Result<R> {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let lock = app.lock(); // Need this to compile let lock = app.lock(); // Need this to compile
@ -142,7 +154,7 @@ impl AsyncAppContext {
&self, &self,
read: impl FnOnce(&G, &AppContext) -> R, read: impl FnOnce(&G, &AppContext) -> R,
) -> Option<R> { ) -> Option<R> {
let app = self.0.upgrade()?; let app = self.app.upgrade()?;
let lock = app.lock(); // Need this to compile let lock = app.lock(); // Need this to compile
Some(read(lock.try_global()?, &lock)) Some(read(lock.try_global()?, &lock))
} }
@ -152,7 +164,7 @@ impl AsyncAppContext {
update: impl FnOnce(&mut G, &mut AppContext) -> R, update: impl FnOnce(&mut G, &mut AppContext) -> R,
) -> Result<R> { ) -> Result<R> {
let app = self let app = self
.0 .app
.upgrade() .upgrade()
.ok_or_else(|| anyhow!("app was released"))?; .ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock(); // Need this to compile let mut lock = app.lock(); // Need this to compile

View file

@ -181,6 +181,13 @@ pub trait PlatformTextSystem: Send + Sync {
) -> Vec<usize>; ) -> Vec<usize>;
} }
#[derive(Clone, Debug)]
pub struct AppMetadata {
pub os_name: &'static str,
pub os_version: SemanticVersion,
pub app_version: SemanticVersion,
}
#[derive(PartialEq, Eq, Hash, Clone)] #[derive(PartialEq, Eq, Hash, Clone)]
pub enum AtlasKey { pub enum AtlasKey {
Glyph(RenderGlyphParams), Glyph(RenderGlyphParams),
@ -404,7 +411,7 @@ impl Default for CursorStyle {
} }
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct SemanticVersion { pub struct SemanticVersion {
major: usize, major: usize,
minor: usize, minor: usize,