Compare commits
6 commits
main
...
display-li
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a0c10abe80 | ||
![]() |
32d490b16c | ||
![]() |
f99f8d232a | ||
![]() |
2ee290727a | ||
![]() |
12d9ebe8fd | ||
![]() |
ca9c247722 |
6 changed files with 138 additions and 158 deletions
|
@ -9,6 +9,7 @@ use derive_more::{Deref, DerefMut};
|
||||||
pub use entity_map::*;
|
pub use entity_map::*;
|
||||||
pub use model_context::*;
|
pub use model_context::*;
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use smol::future::FutureExt;
|
use smol::future::FutureExt;
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub use test_context::*;
|
pub use test_context::*;
|
||||||
|
@ -18,13 +19,18 @@ use crate::{
|
||||||
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
|
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
|
||||||
AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
|
AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
|
||||||
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap,
|
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap,
|
||||||
Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render,
|
Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay,
|
||||||
SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
|
PlatformDisplayLink, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer,
|
||||||
TextSystem, View, ViewContext, Window, WindowContext, WindowHandle, WindowId,
|
Task, TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window, WindowContext,
|
||||||
|
WindowHandle, WindowId,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use collections::{FxHashMap, FxHashSet, VecDeque};
|
use collections::{hash_map, FxHashMap, FxHashSet, VecDeque};
|
||||||
use futures::{channel::oneshot, future::LocalBoxFuture, Future};
|
use futures::{
|
||||||
|
channel::{mpsc, oneshot},
|
||||||
|
future::LocalBoxFuture,
|
||||||
|
Future, StreamExt,
|
||||||
|
};
|
||||||
|
|
||||||
use slotmap::SlotMap;
|
use slotmap::SlotMap;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -39,7 +45,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use util::{
|
use util::{
|
||||||
http::{self, HttpClient},
|
http::{self, HttpClient},
|
||||||
ResultExt,
|
measure, ResultExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The duration for which futures returned from [AppContext::on_app_context] or [ModelContext::on_app_quit] can run before the application fully quits.
|
/// The duration for which futures returned from [AppContext::on_app_context] or [ModelContext::on_app_quit] can run before the application fully quits.
|
||||||
|
@ -213,7 +219,8 @@ pub struct AppContext {
|
||||||
pub(crate) actions: Rc<ActionRegistry>,
|
pub(crate) actions: Rc<ActionRegistry>,
|
||||||
pub(crate) active_drag: Option<AnyDrag>,
|
pub(crate) active_drag: Option<AnyDrag>,
|
||||||
pub(crate) next_frame_callbacks: FxHashMap<DisplayId, Vec<FrameCallback>>,
|
pub(crate) next_frame_callbacks: FxHashMap<DisplayId, Vec<FrameCallback>>,
|
||||||
pub(crate) frame_consumers: FxHashMap<DisplayId, Task<()>>,
|
display_links: FxHashMap<DisplayId, Box<dyn PlatformDisplayLink>>,
|
||||||
|
display_updates: mpsc::UnboundedSender<DisplayId>,
|
||||||
pub(crate) background_executor: BackgroundExecutor,
|
pub(crate) background_executor: BackgroundExecutor,
|
||||||
pub(crate) foreground_executor: ForegroundExecutor,
|
pub(crate) foreground_executor: ForegroundExecutor,
|
||||||
pub(crate) svg_renderer: SvgRenderer,
|
pub(crate) svg_renderer: SvgRenderer,
|
||||||
|
@ -264,6 +271,7 @@ impl AppContext {
|
||||||
app_version: platform.app_version().ok(),
|
app_version: platform.app_version().ok(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (display_updates_tx, mut display_updates_rx) = mpsc::unbounded();
|
||||||
let app = Rc::new_cyclic(|this| AppCell {
|
let app = Rc::new_cyclic(|this| AppCell {
|
||||||
app: RefCell::new(AppContext {
|
app: RefCell::new(AppContext {
|
||||||
this: this.clone(),
|
this: this.clone(),
|
||||||
|
@ -275,9 +283,10 @@ impl AppContext {
|
||||||
pending_updates: 0,
|
pending_updates: 0,
|
||||||
active_drag: None,
|
active_drag: None,
|
||||||
next_frame_callbacks: FxHashMap::default(),
|
next_frame_callbacks: FxHashMap::default(),
|
||||||
frame_consumers: FxHashMap::default(),
|
display_links: FxHashMap::default(),
|
||||||
|
display_updates: display_updates_tx,
|
||||||
background_executor: executor,
|
background_executor: executor,
|
||||||
foreground_executor,
|
foreground_executor: foreground_executor.clone(),
|
||||||
svg_renderer: SvgRenderer::new(asset_source.clone()),
|
svg_renderer: SvgRenderer::new(asset_source.clone()),
|
||||||
asset_source,
|
asset_source,
|
||||||
image_cache: ImageCache::new(http_client),
|
image_cache: ImageCache::new(http_client),
|
||||||
|
@ -311,6 +320,23 @@ impl AppContext {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
foreground_executor
|
||||||
|
.spawn({
|
||||||
|
let cx = app.borrow().to_async();
|
||||||
|
async move {
|
||||||
|
while let Some(display_id) = display_updates_rx.next().await {
|
||||||
|
if cx
|
||||||
|
.update(|cx| cx.refresh_display(display_id))
|
||||||
|
.log_err()
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,6 +509,8 @@ impl AppContext {
|
||||||
let root_view = build_root_view(&mut WindowContext::new(cx, &mut window));
|
let root_view = build_root_view(&mut WindowContext::new(cx, &mut window));
|
||||||
window.root_view.replace(root_view.into());
|
window.root_view.replace(root_view.into());
|
||||||
cx.windows.get_mut(id).unwrap().replace(window);
|
cx.windows.get_mut(id).unwrap().replace(window);
|
||||||
|
// Schedule a draw right after launching the window.
|
||||||
|
cx.refresh();
|
||||||
handle
|
handle
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -652,10 +680,29 @@ impl AppContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let mut active_display_ids = FxHashSet::default();
|
||||||
for window in self.windows.values() {
|
for window in self.windows.values() {
|
||||||
if let Some(window) = window.as_ref() {
|
if let Some(window) = window.as_ref() {
|
||||||
if window.dirty {
|
active_display_ids.insert(window.display_id);
|
||||||
window.platform_window.invalidate();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.display_links
|
||||||
|
.retain(|display_id, _| active_display_ids.contains(display_id));
|
||||||
|
for display_id in active_display_ids {
|
||||||
|
if let hash_map::Entry::Vacant(entry) = self.display_links.entry(display_id) {
|
||||||
|
let tx = self.display_updates.clone();
|
||||||
|
if let Some(display_link) = self
|
||||||
|
.platform
|
||||||
|
.start_display_link(
|
||||||
|
display_id,
|
||||||
|
Box::new(move || {
|
||||||
|
tx.unbounded_send(display_id).log_err();
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.log_err()
|
||||||
|
{
|
||||||
|
entry.insert(display_link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -765,6 +812,32 @@ impl AppContext {
|
||||||
callback(self);
|
callback(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn refresh_display(&mut self, display_id: DisplayId) {
|
||||||
|
// TODO: run this as part of an effect cycle, so that if there are any side
|
||||||
|
// effects, they all get executed so that we can finally draw the windows at
|
||||||
|
// the end.
|
||||||
|
if let Some(callbacks) = self.next_frame_callbacks.remove(&display_id) {
|
||||||
|
for callback in callbacks {
|
||||||
|
callback(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dirty_handles = SmallVec::<[AnyWindowHandle; 4]>::new();
|
||||||
|
for window in self.windows.values() {
|
||||||
|
if let Some(window) = window {
|
||||||
|
if window.dirty && window.display_id == display_id {
|
||||||
|
dirty_handles.push(window.handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for dirty_handle in dirty_handles {
|
||||||
|
dirty_handle
|
||||||
|
.update(self, |_, cx| measure("frame duration", || cx.draw()))
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an `AsyncAppContext`, which can be cloned and has a static lifetime
|
/// Creates an `AsyncAppContext`, which can be cloned and has a static lifetime
|
||||||
/// so it can be held across `await` points.
|
/// so it can be held across `await` points.
|
||||||
pub fn to_async(&self) -> AsyncAppContext {
|
pub fn to_async(&self) -> AsyncAppContext {
|
||||||
|
|
|
@ -67,13 +67,11 @@ pub(crate) trait Platform: 'static {
|
||||||
options: WindowOptions,
|
options: WindowOptions,
|
||||||
) -> Box<dyn PlatformWindow>;
|
) -> Box<dyn PlatformWindow>;
|
||||||
|
|
||||||
fn set_display_link_output_callback(
|
fn start_display_link(
|
||||||
&self,
|
&self,
|
||||||
display_id: DisplayId,
|
display_id: DisplayId,
|
||||||
callback: Box<dyn FnMut() + Send>,
|
callback: Box<dyn FnMut() + Send>,
|
||||||
);
|
) -> Result<Box<dyn PlatformDisplayLink>>;
|
||||||
fn start_display_link(&self, display_id: DisplayId);
|
|
||||||
fn stop_display_link(&self, display_id: DisplayId);
|
|
||||||
|
|
||||||
fn open_url(&self, url: &str);
|
fn open_url(&self, url: &str);
|
||||||
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
|
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
|
||||||
|
@ -186,6 +184,8 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) trait PlatformDisplayLink {}
|
||||||
|
|
||||||
/// This type is public so that our test macro can generate and use it, but it should not
|
/// This type is public so that our test macro can generate and use it, but it should not
|
||||||
/// be considered part of our public API.
|
/// be considered part of our public API.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
@ -1,76 +1,50 @@
|
||||||
|
use crate::{DisplayId, PlatformDisplayLink};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
mem,
|
mem,
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::DisplayId;
|
|
||||||
use collections::HashMap;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
pub(crate) struct MacDisplayLinker {
|
|
||||||
links: HashMap<DisplayId, MacDisplayLink>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MacDisplayLink {
|
|
||||||
system_link: sys::DisplayLink,
|
|
||||||
_output_callback: Arc<OutputCallback>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MacDisplayLinker {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
MacDisplayLinker {
|
|
||||||
links: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OutputCallback = Mutex<Box<dyn FnMut() + Send>>;
|
type OutputCallback = Mutex<Box<dyn FnMut() + Send>>;
|
||||||
|
|
||||||
impl MacDisplayLinker {
|
pub(crate) struct MacDisplayLink {
|
||||||
pub fn set_output_callback(
|
system_link: sys::DisplayLink,
|
||||||
&mut self,
|
_callback: Arc<OutputCallback>,
|
||||||
display_id: DisplayId,
|
}
|
||||||
output_callback: Box<dyn FnMut() + Send>,
|
|
||||||
) {
|
|
||||||
if let Some(mut system_link) = unsafe { sys::DisplayLink::on_display(display_id.0) } {
|
|
||||||
let callback = Arc::new(Mutex::new(output_callback));
|
|
||||||
let weak_callback_ptr: *const OutputCallback = Arc::downgrade(&callback).into_raw();
|
|
||||||
unsafe { system_link.set_output_callback(trampoline, weak_callback_ptr as *mut c_void) }
|
|
||||||
|
|
||||||
self.links.insert(
|
impl MacDisplayLink {
|
||||||
display_id,
|
pub fn new(display_id: DisplayId, callback: Box<dyn FnMut() + Send>) -> Result<Self> {
|
||||||
MacDisplayLink {
|
let mut system_link = unsafe {
|
||||||
_output_callback: callback,
|
sys::DisplayLink::on_display(display_id.0)
|
||||||
system_link,
|
.ok_or_else(|| anyhow!("could not create DisplayLink"))
|
||||||
},
|
}?;
|
||||||
);
|
|
||||||
} else {
|
let callback = Arc::new(Mutex::new(callback));
|
||||||
log::warn!("DisplayLink could not be obtained for {:?}", display_id);
|
let weak_callback_ptr: *const OutputCallback = Arc::downgrade(&callback).into_raw();
|
||||||
|
unsafe {
|
||||||
|
system_link.set_output_callback(trampoline, weak_callback_ptr as *mut c_void);
|
||||||
|
system_link.start();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(&mut self, display_id: DisplayId) {
|
Ok(Self {
|
||||||
if let Some(link) = self.links.get_mut(&display_id) {
|
system_link,
|
||||||
unsafe {
|
_callback: callback,
|
||||||
link.system_link.start();
|
})
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::warn!("No DisplayLink callback registered for {:?}", display_id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self, display_id: DisplayId) {
|
impl Drop for MacDisplayLink {
|
||||||
if let Some(link) = self.links.get_mut(&display_id) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
link.system_link.stop();
|
self.system_link.stop();
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::warn!("No DisplayLink callback registered for {:?}", display_id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PlatformDisplayLink for MacDisplayLink {}
|
||||||
|
|
||||||
unsafe extern "C" fn trampoline(
|
unsafe extern "C" fn trampoline(
|
||||||
_display_link_out: *mut sys::CVDisplayLink,
|
_display_link_out: *mut sys::CVDisplayLink,
|
||||||
current_time: *const sys::CVTimeStamp,
|
current_time: *const sys::CVTimeStamp,
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use super::{events::key_to_native, BoolExt};
|
use super::{events::key_to_native, BoolExt};
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
|
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
|
||||||
ForegroundExecutor, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem,
|
ForegroundExecutor, Keymap, MacDispatcher, MacDisplay, MacDisplayLink, MacTextSystem,
|
||||||
MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformInput,
|
MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformDisplayLink,
|
||||||
PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, WindowOptions,
|
PlatformInput, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task,
|
||||||
|
WindowOptions,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use block::ConcreteBlock;
|
use block::ConcreteBlock;
|
||||||
|
@ -145,7 +146,6 @@ pub(crate) struct MacPlatformState {
|
||||||
background_executor: BackgroundExecutor,
|
background_executor: BackgroundExecutor,
|
||||||
foreground_executor: ForegroundExecutor,
|
foreground_executor: ForegroundExecutor,
|
||||||
text_system: Arc<MacTextSystem>,
|
text_system: Arc<MacTextSystem>,
|
||||||
display_linker: MacDisplayLinker,
|
|
||||||
pasteboard: id,
|
pasteboard: id,
|
||||||
text_hash_pasteboard_type: id,
|
text_hash_pasteboard_type: id,
|
||||||
metadata_pasteboard_type: id,
|
metadata_pasteboard_type: id,
|
||||||
|
@ -175,7 +175,6 @@ impl MacPlatform {
|
||||||
background_executor: BackgroundExecutor::new(dispatcher.clone()),
|
background_executor: BackgroundExecutor::new(dispatcher.clone()),
|
||||||
foreground_executor: ForegroundExecutor::new(dispatcher),
|
foreground_executor: ForegroundExecutor::new(dispatcher),
|
||||||
text_system: Arc::new(MacTextSystem::new()),
|
text_system: Arc::new(MacTextSystem::new()),
|
||||||
display_linker: MacDisplayLinker::new(),
|
|
||||||
pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
|
pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
|
||||||
text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
|
text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
|
||||||
metadata_pasteboard_type: unsafe { ns_string("zed-metadata") },
|
metadata_pasteboard_type: unsafe { ns_string("zed-metadata") },
|
||||||
|
@ -497,23 +496,12 @@ impl Platform for MacPlatform {
|
||||||
Box::new(MacWindow::open(handle, options, self.foreground_executor()))
|
Box::new(MacWindow::open(handle, options, self.foreground_executor()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_display_link_output_callback(
|
fn start_display_link(
|
||||||
&self,
|
&self,
|
||||||
display_id: DisplayId,
|
display_id: DisplayId,
|
||||||
callback: Box<dyn FnMut() + Send>,
|
callback: Box<dyn FnMut() + Send>,
|
||||||
) {
|
) -> Result<Box<dyn PlatformDisplayLink>> {
|
||||||
self.0
|
Ok(Box::new(MacDisplayLink::new(display_id, callback)?))
|
||||||
.lock()
|
|
||||||
.display_linker
|
|
||||||
.set_output_callback(display_id, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_display_link(&self, display_id: DisplayId) {
|
|
||||||
self.0.lock().display_linker.start(display_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stop_display_link(&self, display_id: DisplayId) {
|
|
||||||
self.0.lock().display_linker.stop(display_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_url(&self, url: &str) {
|
fn open_url(&self, url: &str) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor,
|
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor,
|
||||||
Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestWindow,
|
Keymap, Platform, PlatformDisplay, PlatformDisplayLink, PlatformTextSystem, Task, TestDisplay,
|
||||||
WindowOptions,
|
TestWindow, WindowOptions,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
|
@ -176,18 +176,14 @@ impl Platform for TestPlatform {
|
||||||
Box::new(window)
|
Box::new(window)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_display_link_output_callback(
|
fn start_display_link(
|
||||||
&self,
|
&self,
|
||||||
_display_id: DisplayId,
|
_display_id: DisplayId,
|
||||||
mut callback: Box<dyn FnMut() + Send>,
|
_callback: Box<dyn FnMut() + Send>,
|
||||||
) {
|
) -> Result<Box<dyn PlatformDisplayLink>> {
|
||||||
callback()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_display_link(&self, _display_id: DisplayId) {}
|
|
||||||
|
|
||||||
fn stop_display_link(&self, _display_id: DisplayId) {}
|
|
||||||
|
|
||||||
fn open_url(&self, _url: &str) {
|
fn open_url(&self, _url: &str) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,7 @@ use crate::{
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use collections::FxHashSet;
|
use collections::FxHashSet;
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use futures::{
|
use futures::channel::oneshot;
|
||||||
channel::{mpsc, oneshot},
|
|
||||||
StreamExt,
|
|
||||||
};
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use slotmap::SlotMap;
|
use slotmap::SlotMap;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -23,7 +20,6 @@ use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
borrow::{Borrow, BorrowMut},
|
borrow::{Borrow, BorrowMut},
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::hash_map::Entry,
|
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
future::Future,
|
future::Future,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
|
@ -36,7 +32,7 @@ use std::{
|
||||||
},
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use util::{measure, ResultExt};
|
use util::ResultExt;
|
||||||
|
|
||||||
mod element_cx;
|
mod element_cx;
|
||||||
pub use element_cx::*;
|
pub use element_cx::*;
|
||||||
|
@ -249,7 +245,7 @@ pub struct Window {
|
||||||
pub(crate) handle: AnyWindowHandle,
|
pub(crate) handle: AnyWindowHandle,
|
||||||
pub(crate) removed: bool,
|
pub(crate) removed: bool,
|
||||||
pub(crate) platform_window: Box<dyn PlatformWindow>,
|
pub(crate) platform_window: Box<dyn PlatformWindow>,
|
||||||
display_id: DisplayId,
|
pub(crate) display_id: DisplayId,
|
||||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||||
pub(crate) rem_size: Pixels,
|
pub(crate) rem_size: Pixels,
|
||||||
pub(crate) viewport_size: Size<Pixels>,
|
pub(crate) viewport_size: Size<Pixels>,
|
||||||
|
@ -338,19 +334,14 @@ impl Window {
|
||||||
let scale_factor = platform_window.scale_factor();
|
let scale_factor = platform_window.scale_factor();
|
||||||
let bounds = platform_window.bounds();
|
let bounds = platform_window.bounds();
|
||||||
|
|
||||||
platform_window.on_request_frame(Box::new({
|
|
||||||
let mut cx = cx.to_async();
|
|
||||||
move || {
|
|
||||||
measure("frame duration", || {
|
|
||||||
handle.update(&mut cx, |_, cx| cx.draw()).log_err();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
platform_window.on_resize(Box::new({
|
platform_window.on_resize(Box::new({
|
||||||
let mut cx = cx.to_async();
|
let mut cx = cx.to_async();
|
||||||
move |_, _| {
|
move |_, _| {
|
||||||
handle
|
handle
|
||||||
.update(&mut cx, |_, cx| cx.window_bounds_changed())
|
.update(&mut cx, |_, cx| {
|
||||||
|
cx.window_bounds_changed();
|
||||||
|
cx.draw()
|
||||||
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -642,48 +633,6 @@ impl<'a> WindowContext<'a> {
|
||||||
let handle = self.window.handle;
|
let handle = self.window.handle;
|
||||||
let display_id = self.window.display_id;
|
let display_id = self.window.display_id;
|
||||||
|
|
||||||
let mut frame_consumers = std::mem::take(&mut self.app.frame_consumers);
|
|
||||||
if let Entry::Vacant(e) = frame_consumers.entry(display_id) {
|
|
||||||
let (tx, mut rx) = mpsc::unbounded::<()>();
|
|
||||||
self.platform.set_display_link_output_callback(
|
|
||||||
display_id,
|
|
||||||
Box::new(move || _ = tx.unbounded_send(())),
|
|
||||||
);
|
|
||||||
|
|
||||||
let consumer_task = self.app.spawn(|cx| async move {
|
|
||||||
while rx.next().await.is_some() {
|
|
||||||
cx.update(|cx| {
|
|
||||||
for callback in cx
|
|
||||||
.next_frame_callbacks
|
|
||||||
.get_mut(&display_id)
|
|
||||||
.unwrap()
|
|
||||||
.drain(..)
|
|
||||||
.collect::<SmallVec<[_; 32]>>()
|
|
||||||
{
|
|
||||||
callback(cx);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
// Flush effects, then stop the display link if no new next_frame_callbacks have been added.
|
|
||||||
|
|
||||||
cx.update(|cx| {
|
|
||||||
if cx.next_frame_callbacks.is_empty() {
|
|
||||||
cx.platform.stop_display_link(display_id);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.insert(consumer_task);
|
|
||||||
}
|
|
||||||
debug_assert!(self.app.frame_consumers.is_empty());
|
|
||||||
self.app.frame_consumers = frame_consumers;
|
|
||||||
|
|
||||||
if self.next_frame_callbacks.is_empty() {
|
|
||||||
self.platform.start_display_link(display_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.next_frame_callbacks
|
self.next_frame_callbacks
|
||||||
.entry(display_id)
|
.entry(display_id)
|
||||||
.or_default()
|
.or_default()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue