style: cargo fmt with the workspace tab policy (hard_tabs)

Whitespace only; the fork has no own rustfmt.toml so the Oak root
policy applies.
This commit is contained in:
2026-08-11 23:06:13 +08:00
parent 507d7e6e8d
commit af5482d774
318 changed files with 132564 additions and 132170 deletions
+268 -268
View File
@@ -1,6 +1,6 @@
use gpui::{
PlatformDispatcher, Priority, PriorityQueueReceiver, PriorityQueueSender, RunnableVariant,
ThreadTaskTimings,
PlatformDispatcher, Priority, PriorityQueueReceiver, PriorityQueueSender, RunnableVariant,
ThreadTaskTimings,
};
use std::sync::Arc;
use std::sync::atomic::AtomicI32;
@@ -12,131 +12,131 @@ use web_time::Instant;
const MIN_BACKGROUND_THREADS: usize = 2;
fn shared_memory_supported() -> bool {
#[cfg(feature = "multithreaded")]
{
let global = js_sys::global();
let has_shared_array_buffer =
js_sys::Reflect::has(&global, &JsValue::from_str("SharedArrayBuffer")).unwrap_or(false);
let has_atomics =
js_sys::Reflect::has(&global, &JsValue::from_str("Atomics")).unwrap_or(false);
let memory = js_sys::WebAssembly::Memory::from(wasm_bindgen::memory());
let buffer = memory.buffer();
let is_shared_buffer = buffer.is_instance_of::<js_sys::SharedArrayBuffer>();
has_shared_array_buffer && has_atomics && is_shared_buffer
}
#[cfg(not(feature = "multithreaded"))]
{
false
}
#[cfg(feature = "multithreaded")]
{
let global = js_sys::global();
let has_shared_array_buffer =
js_sys::Reflect::has(&global, &JsValue::from_str("SharedArrayBuffer")).unwrap_or(false);
let has_atomics =
js_sys::Reflect::has(&global, &JsValue::from_str("Atomics")).unwrap_or(false);
let memory = js_sys::WebAssembly::Memory::from(wasm_bindgen::memory());
let buffer = memory.buffer();
let is_shared_buffer = buffer.is_instance_of::<js_sys::SharedArrayBuffer>();
has_shared_array_buffer && has_atomics && is_shared_buffer
}
#[cfg(not(feature = "multithreaded"))]
{
false
}
}
enum MainThreadItem {
Runnable(RunnableVariant),
Delayed {
runnable: RunnableVariant,
millis: i32,
},
// TODO-Wasm: Shouldn't these run on their own dedicated thread?
RealtimeFunction(Box<dyn FnOnce() + Send>),
Runnable(RunnableVariant),
Delayed {
runnable: RunnableVariant,
millis: i32,
},
// TODO-Wasm: Shouldn't these run on their own dedicated thread?
RealtimeFunction(Box<dyn FnOnce() + Send>),
}
struct MainThreadMailbox {
sender: PriorityQueueSender<MainThreadItem>,
receiver: parking_lot::Mutex<PriorityQueueReceiver<MainThreadItem>>,
signal: AtomicI32,
sender: PriorityQueueSender<MainThreadItem>,
receiver: parking_lot::Mutex<PriorityQueueReceiver<MainThreadItem>>,
signal: AtomicI32,
}
impl MainThreadMailbox {
fn new() -> Self {
let (sender, receiver) = PriorityQueueReceiver::new();
Self {
sender,
receiver: parking_lot::Mutex::new(receiver),
signal: AtomicI32::new(0),
}
}
fn new() -> Self {
let (sender, receiver) = PriorityQueueReceiver::new();
Self {
sender,
receiver: parking_lot::Mutex::new(receiver),
signal: AtomicI32::new(0),
}
}
fn post(&self, priority: Priority, item: MainThreadItem) {
if self.sender.spin_send(priority, item).is_err() {
log::error!("MainThreadMailbox::send failed: receiver disconnected");
}
fn post(&self, priority: Priority, item: MainThreadItem) {
if self.sender.spin_send(priority, item).is_err() {
log::error!("MainThreadMailbox::send failed: receiver disconnected");
}
// TODO-Wasm: Verify this lock-free protocol
let view = self.signal_view();
js_sys::Atomics::store(&view, 0, 1).ok();
js_sys::Atomics::notify(&view, 0).ok();
}
// TODO-Wasm: Verify this lock-free protocol
let view = self.signal_view();
js_sys::Atomics::store(&view, 0, 1).ok();
js_sys::Atomics::notify(&view, 0).ok();
}
fn drain(&self, window: &web_sys::Window) {
let mut receiver = self.receiver.lock();
loop {
// We need these `spin` variants because we can't acquire a lock on the main thread.
// TODO-WASM: Should we do something different?
match receiver.spin_try_pop() {
Ok(Some(item)) => execute_on_main_thread(window, item),
Ok(None) => break,
Err(_) => break,
}
}
}
fn drain(&self, window: &web_sys::Window) {
let mut receiver = self.receiver.lock();
loop {
// We need these `spin` variants because we can't acquire a lock on the main thread.
// TODO-WASM: Should we do something different?
match receiver.spin_try_pop() {
Ok(Some(item)) => execute_on_main_thread(window, item),
Ok(None) => break,
Err(_) => break,
}
}
}
fn signal_view(&self) -> js_sys::Int32Array {
let byte_offset = self.signal.as_ptr() as u32;
let memory = js_sys::WebAssembly::Memory::from(wasm_bindgen::memory());
js_sys::Int32Array::new_with_byte_offset_and_length(&memory.buffer(), byte_offset, 1)
}
fn signal_view(&self) -> js_sys::Int32Array {
let byte_offset = self.signal.as_ptr() as u32;
let memory = js_sys::WebAssembly::Memory::from(wasm_bindgen::memory());
js_sys::Int32Array::new_with_byte_offset_and_length(&memory.buffer(), byte_offset, 1)
}
fn run_waker_loop(self: &Arc<Self>, window: web_sys::Window) {
if !shared_memory_supported() {
log::warn!("SharedArrayBuffer not available; main thread mailbox waker loop disabled");
return;
}
fn run_waker_loop(self: &Arc<Self>, window: web_sys::Window) {
if !shared_memory_supported() {
log::warn!("SharedArrayBuffer not available; main thread mailbox waker loop disabled");
return;
}
let mailbox = Arc::clone(self);
wasm_bindgen_futures::spawn_local(async move {
let view = mailbox.signal_view();
loop {
js_sys::Atomics::store(&view, 0, 0).expect("Atomics.store failed");
let mailbox = Arc::clone(self);
wasm_bindgen_futures::spawn_local(async move {
let view = mailbox.signal_view();
loop {
js_sys::Atomics::store(&view, 0, 0).expect("Atomics.store failed");
let result = match js_sys::Atomics::wait_async(&view, 0, 0) {
Ok(result) => result,
Err(error) => {
log::error!("Atomics.waitAsync failed: {error:?}");
break;
}
};
let result = match js_sys::Atomics::wait_async(&view, 0, 0) {
Ok(result) => result,
Err(error) => {
log::error!("Atomics.waitAsync failed: {error:?}");
break;
}
};
let is_async = js_sys::Reflect::get(&result, &JsValue::from_str("async"))
.ok()
.and_then(|v| v.as_bool())
.unwrap_or(false);
let is_async = js_sys::Reflect::get(&result, &JsValue::from_str("async"))
.ok()
.and_then(|v| v.as_bool())
.unwrap_or(false);
if !is_async {
log::error!("Atomics.waitAsync returned synchronously; waker loop exiting");
break;
}
if !is_async {
log::error!("Atomics.waitAsync returned synchronously; waker loop exiting");
break;
}
let promise: js_sys::Promise =
js_sys::Reflect::get(&result, &JsValue::from_str("value"))
.expect("waitAsync result missing 'value'")
.unchecked_into();
let promise: js_sys::Promise =
js_sys::Reflect::get(&result, &JsValue::from_str("value"))
.expect("waitAsync result missing 'value'")
.unchecked_into();
let _ = wasm_bindgen_futures::JsFuture::from(promise).await;
let _ = wasm_bindgen_futures::JsFuture::from(promise).await;
mailbox.drain(&window);
}
});
}
mailbox.drain(&window);
}
});
}
}
pub struct WebDispatcher {
main_thread_id: std::thread::ThreadId,
browser_window: web_sys::Window,
background_sender: PriorityQueueSender<RunnableVariant>,
main_thread_mailbox: Arc<MainThreadMailbox>,
supports_threads: bool,
#[cfg(feature = "multithreaded")]
_background_threads: Vec<wasm_thread::JoinHandle<()>>,
main_thread_id: std::thread::ThreadId,
browser_window: web_sys::Window,
background_sender: PriorityQueueSender<RunnableVariant>,
main_thread_mailbox: Arc<MainThreadMailbox>,
supports_threads: bool,
#[cfg(feature = "multithreaded")]
_background_threads: Vec<wasm_thread::JoinHandle<()>>,
}
// Safety: `web_sys::Window` is only accessed from the main thread
@@ -145,196 +145,196 @@ unsafe impl Send for WebDispatcher {}
unsafe impl Sync for WebDispatcher {}
impl WebDispatcher {
pub fn new(browser_window: web_sys::Window, _allow_threads: bool) -> Self {
#[cfg(feature = "multithreaded")]
let (background_sender, background_receiver) = PriorityQueueReceiver::new();
#[cfg(not(feature = "multithreaded"))]
let (background_sender, _) = PriorityQueueReceiver::new();
pub fn new(browser_window: web_sys::Window, _allow_threads: bool) -> Self {
#[cfg(feature = "multithreaded")]
let (background_sender, background_receiver) = PriorityQueueReceiver::new();
#[cfg(not(feature = "multithreaded"))]
let (background_sender, _) = PriorityQueueReceiver::new();
let main_thread_mailbox = Arc::new(MainThreadMailbox::new());
let main_thread_mailbox = Arc::new(MainThreadMailbox::new());
#[cfg(feature = "multithreaded")]
let supports_threads = _allow_threads && shared_memory_supported();
#[cfg(not(feature = "multithreaded"))]
let supports_threads = false;
#[cfg(feature = "multithreaded")]
let supports_threads = _allow_threads && shared_memory_supported();
#[cfg(not(feature = "multithreaded"))]
let supports_threads = false;
if supports_threads {
main_thread_mailbox.run_waker_loop(browser_window.clone());
} else {
log::warn!(
"SharedArrayBuffer not available; falling back to single-threaded dispatcher"
);
}
if supports_threads {
main_thread_mailbox.run_waker_loop(browser_window.clone());
} else {
log::warn!(
"SharedArrayBuffer not available; falling back to single-threaded dispatcher"
);
}
#[cfg(feature = "multithreaded")]
let background_threads = if supports_threads {
let thread_count = browser_window
.navigator()
.hardware_concurrency()
.max(MIN_BACKGROUND_THREADS as f64) as usize;
#[cfg(feature = "multithreaded")]
let background_threads = if supports_threads {
let thread_count = browser_window
.navigator()
.hardware_concurrency()
.max(MIN_BACKGROUND_THREADS as f64) as usize;
// TODO-Wasm: Is it bad to have web workers blocking for a long time like this?
(0..thread_count)
.map(|i| {
let mut receiver = background_receiver.clone();
wasm_thread::Builder::new()
.name(format!("background-worker-{i}"))
.spawn(move || {
loop {
let runnable: RunnableVariant = match receiver.pop() {
Ok(runnable) => runnable,
Err(_) => {
log::info!(
"background-worker-{i}: channel disconnected, exiting"
);
break;
}
};
// TODO-Wasm: Is it bad to have web workers blocking for a long time like this?
(0..thread_count)
.map(|i| {
let mut receiver = background_receiver.clone();
wasm_thread::Builder::new()
.name(format!("background-worker-{i}"))
.spawn(move || {
loop {
let runnable: RunnableVariant = match receiver.pop() {
Ok(runnable) => runnable,
Err(_) => {
log::info!(
"background-worker-{i}: channel disconnected, exiting"
);
break;
}
};
runnable.run();
}
})
.expect("failed to spawn background worker thread")
})
.collect::<Vec<_>>()
} else {
Vec::new()
};
runnable.run();
}
})
.expect("failed to spawn background worker thread")
})
.collect::<Vec<_>>()
} else {
Vec::new()
};
Self {
main_thread_id: std::thread::current().id(),
browser_window,
background_sender,
main_thread_mailbox,
supports_threads,
#[cfg(feature = "multithreaded")]
_background_threads: background_threads,
}
}
Self {
main_thread_id: std::thread::current().id(),
browser_window,
background_sender,
main_thread_mailbox,
supports_threads,
#[cfg(feature = "multithreaded")]
_background_threads: background_threads,
}
}
fn on_main_thread(&self) -> bool {
std::thread::current().id() == self.main_thread_id
}
fn on_main_thread(&self) -> bool {
std::thread::current().id() == self.main_thread_id
}
}
impl PlatformDispatcher for WebDispatcher {
fn get_all_timings(&self) -> Vec<ThreadTaskTimings> {
// TODO-Wasm: should we panic here?
Vec::new()
}
fn get_all_timings(&self) -> Vec<ThreadTaskTimings> {
// TODO-Wasm: should we panic here?
Vec::new()
}
fn get_current_thread_timings(&self) -> ThreadTaskTimings {
ThreadTaskTimings {
thread_name: None,
thread_id: std::thread::current().id(),
timings: Vec::new(),
total_pushed: 0,
}
}
fn get_current_thread_timings(&self) -> ThreadTaskTimings {
ThreadTaskTimings {
thread_name: None,
thread_id: std::thread::current().id(),
timings: Vec::new(),
total_pushed: 0,
}
}
fn is_main_thread(&self) -> bool {
self.on_main_thread()
}
fn is_main_thread(&self) -> bool {
self.on_main_thread()
}
fn dispatch(&self, runnable: RunnableVariant, priority: Priority) {
if !self.supports_threads {
self.dispatch_on_main_thread(runnable, priority);
return;
}
fn dispatch(&self, runnable: RunnableVariant, priority: Priority) {
if !self.supports_threads {
self.dispatch_on_main_thread(runnable, priority);
return;
}
let result = if self.on_main_thread() {
self.background_sender.spin_send(priority, runnable)
} else {
self.background_sender.send(priority, runnable)
};
let result = if self.on_main_thread() {
self.background_sender.spin_send(priority, runnable)
} else {
self.background_sender.send(priority, runnable)
};
if let Err(error) = result {
log::error!("dispatch: failed to send to background queue: {error:?}");
}
}
if let Err(error) = result {
log::error!("dispatch: failed to send to background queue: {error:?}");
}
}
fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority) {
if self.on_main_thread() {
schedule_runnable(&self.browser_window, runnable, priority);
} else {
self.main_thread_mailbox
.post(priority, MainThreadItem::Runnable(runnable));
}
}
fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority) {
if self.on_main_thread() {
schedule_runnable(&self.browser_window, runnable, priority);
} else {
self.main_thread_mailbox
.post(priority, MainThreadItem::Runnable(runnable));
}
}
fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) {
let millis = duration.as_millis().min(i32::MAX as u128) as i32;
if self.on_main_thread() {
let callback = Closure::once_into_js(move || {
runnable.run();
});
self.browser_window
.set_timeout_with_callback_and_timeout_and_arguments_0(
callback.unchecked_ref(),
millis,
)
.ok();
} else {
self.main_thread_mailbox
.post(Priority::High, MainThreadItem::Delayed { runnable, millis });
}
}
fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) {
let millis = duration.as_millis().min(i32::MAX as u128) as i32;
if self.on_main_thread() {
let callback = Closure::once_into_js(move || {
runnable.run();
});
self.browser_window
.set_timeout_with_callback_and_timeout_and_arguments_0(
callback.unchecked_ref(),
millis,
)
.ok();
} else {
self.main_thread_mailbox
.post(Priority::High, MainThreadItem::Delayed { runnable, millis });
}
}
fn spawn_realtime(&self, function: Box<dyn FnOnce() + Send>) {
if self.on_main_thread() {
let callback = Closure::once_into_js(move || {
function();
});
self.browser_window
.queue_microtask(callback.unchecked_ref());
} else {
self.main_thread_mailbox
.post(Priority::High, MainThreadItem::RealtimeFunction(function));
}
}
fn spawn_realtime(&self, function: Box<dyn FnOnce() + Send>) {
if self.on_main_thread() {
let callback = Closure::once_into_js(move || {
function();
});
self.browser_window
.queue_microtask(callback.unchecked_ref());
} else {
self.main_thread_mailbox
.post(Priority::High, MainThreadItem::RealtimeFunction(function));
}
}
fn now(&self) -> Instant {
Instant::now()
}
fn now(&self) -> Instant {
Instant::now()
}
}
fn execute_on_main_thread(window: &web_sys::Window, item: MainThreadItem) {
match item {
MainThreadItem::Runnable(runnable) => {
runnable.run();
}
MainThreadItem::Delayed { runnable, millis } => {
let callback = Closure::once_into_js(move || {
runnable.run();
});
window
.set_timeout_with_callback_and_timeout_and_arguments_0(
callback.unchecked_ref(),
millis,
)
.ok();
}
MainThreadItem::RealtimeFunction(function) => {
function();
}
}
match item {
MainThreadItem::Runnable(runnable) => {
runnable.run();
}
MainThreadItem::Delayed { runnable, millis } => {
let callback = Closure::once_into_js(move || {
runnable.run();
});
window
.set_timeout_with_callback_and_timeout_and_arguments_0(
callback.unchecked_ref(),
millis,
)
.ok();
}
MainThreadItem::RealtimeFunction(function) => {
function();
}
}
}
fn schedule_runnable(window: &web_sys::Window, runnable: RunnableVariant, priority: Priority) {
let callback = Closure::once_into_js(move || {
runnable.run();
});
let callback: &js_sys::Function = callback.unchecked_ref();
let callback = Closure::once_into_js(move || {
runnable.run();
});
let callback: &js_sys::Function = callback.unchecked_ref();
match priority {
Priority::RealtimeAudio => {
window.queue_microtask(callback);
}
_ => {
// TODO-Wasm: this ought to enqueue so we can dequeue with proper priority
window
.set_timeout_with_callback_and_timeout_and_arguments_0(callback, 0)
.ok();
}
}
match priority {
Priority::RealtimeAudio => {
window.queue_microtask(callback);
}
_ => {
// TODO-Wasm: this ought to enqueue so we can dequeue with proper priority
window
.set_timeout_with_callback_and_timeout_and_arguments_0(callback, 0)
.ok();
}
}
}
+73 -73
View File
@@ -3,9 +3,9 @@ use gpui::{Bounds, DisplayId, Pixels, PlatformDisplay, Point, Size, px};
#[derive(Debug)]
pub struct WebDisplay {
id: DisplayId,
uuid: uuid::Uuid,
browser_window: web_sys::Window,
id: DisplayId,
uuid: uuid::Uuid,
browser_window: web_sys::Window,
}
// Safety: WASM is single-threaded — there is no concurrent access to `web_sys::Window`.
@@ -13,86 +13,86 @@ unsafe impl Send for WebDisplay {}
unsafe impl Sync for WebDisplay {}
impl WebDisplay {
pub fn new(browser_window: web_sys::Window) -> Self {
WebDisplay {
id: DisplayId::new(1),
uuid: uuid::Uuid::new_v4(),
browser_window,
}
}
pub fn new(browser_window: web_sys::Window) -> Self {
WebDisplay {
id: DisplayId::new(1),
uuid: uuid::Uuid::new_v4(),
browser_window,
}
}
fn screen_size(&self) -> Size<Pixels> {
let Some(screen) = self.browser_window.screen().ok() else {
return Size {
width: px(1920.),
height: px(1080.),
};
};
fn screen_size(&self) -> Size<Pixels> {
let Some(screen) = self.browser_window.screen().ok() else {
return Size {
width: px(1920.),
height: px(1080.),
};
};
let width = screen.width().unwrap_or(1920) as f32;
let height = screen.height().unwrap_or(1080) as f32;
let width = screen.width().unwrap_or(1920) as f32;
let height = screen.height().unwrap_or(1080) as f32;
Size {
width: px(width),
height: px(height),
}
}
Size {
width: px(width),
height: px(height),
}
}
fn viewport_size(&self) -> Size<Pixels> {
let width = self
.browser_window
.inner_width()
.ok()
.and_then(|v| v.as_f64())
.unwrap_or(1920.0) as f32;
let height = self
.browser_window
.inner_height()
.ok()
.and_then(|v| v.as_f64())
.unwrap_or(1080.0) as f32;
fn viewport_size(&self) -> Size<Pixels> {
let width = self
.browser_window
.inner_width()
.ok()
.and_then(|v| v.as_f64())
.unwrap_or(1920.0) as f32;
let height = self
.browser_window
.inner_height()
.ok()
.and_then(|v| v.as_f64())
.unwrap_or(1080.0) as f32;
Size {
width: px(width),
height: px(height),
}
}
Size {
width: px(width),
height: px(height),
}
}
}
impl PlatformDisplay for WebDisplay {
fn id(&self) -> DisplayId {
self.id
}
fn id(&self) -> DisplayId {
self.id
}
fn uuid(&self) -> Result<uuid::Uuid> {
Ok(self.uuid)
}
fn uuid(&self) -> Result<uuid::Uuid> {
Ok(self.uuid)
}
fn bounds(&self) -> Bounds<Pixels> {
let size = self.screen_size();
Bounds {
origin: Point::default(),
size,
}
}
fn bounds(&self) -> Bounds<Pixels> {
let size = self.screen_size();
Bounds {
origin: Point::default(),
size,
}
}
fn visible_bounds(&self) -> Bounds<Pixels> {
let size = self.viewport_size();
Bounds {
origin: Point::default(),
size,
}
}
fn visible_bounds(&self) -> Bounds<Pixels> {
let size = self.viewport_size();
Bounds {
origin: Point::default(),
size,
}
}
fn default_bounds(&self) -> Bounds<Pixels> {
let visible = self.visible_bounds();
let width = visible.size.width * 0.75;
let height = visible.size.height * 0.75;
let origin_x = (visible.size.width - width) / 2.0;
let origin_y = (visible.size.height - height) / 2.0;
Bounds {
origin: Point::new(origin_x, origin_y),
size: Size { width, height },
}
}
fn default_bounds(&self) -> Bounds<Pixels> {
let visible = self.visible_bounds();
let width = visible.size.width * 0.75;
let height = visible.size.height * 0.75;
let origin_x = (visible.size.width - width) / 2.0;
let origin_y = (visible.size.height - height) / 2.0;
Bounds {
origin: Point::new(origin_x, origin_y),
size: Size { width, height },
}
}
}
File diff suppressed because it is too large Load Diff
+56 -56
View File
@@ -8,30 +8,30 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(catch, js_name = "fetch")]
fn global_fetch(input: &web_sys::Request) -> Result<js_sys::Promise, JsValue>;
#[wasm_bindgen(catch, js_name = "fetch")]
fn global_fetch(input: &web_sys::Request) -> Result<js_sys::Promise, JsValue>;
}
pub struct FetchHttpClient;
impl Default for FetchHttpClient {
fn default() -> Self {
Self
}
fn default() -> Self {
Self
}
}
#[cfg(feature = "multithreaded")]
impl FetchHttpClient {
pub unsafe fn new() -> Self {
Self
}
pub unsafe fn new() -> Self {
Self
}
}
#[cfg(not(feature = "multithreaded"))]
impl FetchHttpClient {
pub fn new() -> Self {
Self
}
pub fn new() -> Self {
Self
}
}
/// Wraps a `!Send` future to satisfy the `Send` bound on `BoxFuture`.
@@ -40,60 +40,60 @@ struct AssertSend<F>(F);
unsafe impl<F> Send for AssertSend<F> {}
impl<F: Future> Future for AssertSend<F> {
type Output = F::Output;
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
let inner = unsafe { self.map_unchecked_mut(|this| &mut this.0) };
inner.poll(cx)
}
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
let inner = unsafe { self.map_unchecked_mut(|this| &mut this.0) };
inner.poll(cx)
}
}
impl HttpClient for FetchHttpClient {
fn get(
&self,
url: &str,
follow_redirects: bool,
) -> futures::future::BoxFuture<'static, anyhow::Result<HttpResponse>> {
let url = url.to_string();
Box::pin(AssertSend(async move {
let init = web_sys::RequestInit::new();
init.set_method("GET");
fn get(
&self,
url: &str,
follow_redirects: bool,
) -> futures::future::BoxFuture<'static, anyhow::Result<HttpResponse>> {
let url = url.to_string();
Box::pin(AssertSend(async move {
let init = web_sys::RequestInit::new();
init.set_method("GET");
if !follow_redirects {
init.set_redirect(web_sys::RequestRedirect::Manual);
}
if !follow_redirects {
init.set_redirect(web_sys::RequestRedirect::Manual);
}
let request = web_sys::Request::new_with_str_and_init(&url, &init)
.map_err(|error| anyhow!("failed to create fetch Request: {error:?}"))?;
let request = web_sys::Request::new_with_str_and_init(&url, &init)
.map_err(|error| anyhow!("failed to create fetch Request: {error:?}"))?;
let promise = global_fetch(&request)
.map_err(|error| anyhow!("fetch threw an error: {error:?}"))?;
let response_value = wasm_bindgen_futures::JsFuture::from(promise)
.await
.map_err(|error| anyhow!("fetch failed: {error:?}"))?;
let promise = global_fetch(&request)
.map_err(|error| anyhow!("fetch threw an error: {error:?}"))?;
let response_value = wasm_bindgen_futures::JsFuture::from(promise)
.await
.map_err(|error| anyhow!("fetch failed: {error:?}"))?;
let web_response: web_sys::Response = response_value
.dyn_into()
.map_err(|error| anyhow!("fetch result is not a Response: {error:?}"))?;
let web_response: web_sys::Response = response_value
.dyn_into()
.map_err(|error| anyhow!("fetch result is not a Response: {error:?}"))?;
let status_code = http::StatusCode::from_u16(web_response.status())
.map_err(|_| anyhow!("invalid status code"))?;
let status_code = http::StatusCode::from_u16(web_response.status())
.map_err(|_| anyhow!("invalid status code"))?;
let body_promise = web_response
.array_buffer()
.map_err(|error| anyhow!("failed to initiate response body read: {error:?}"))?;
let body_value = wasm_bindgen_futures::JsFuture::from(body_promise)
.await
.map_err(|error| anyhow!("failed to read response body: {error:?}"))?;
let array_buffer: js_sys::ArrayBuffer = body_value
.dyn_into()
.map_err(|error| anyhow!("response body is not an ArrayBuffer: {error:?}"))?;
let body = js_sys::Uint8Array::new(&array_buffer).to_vec();
let body_promise = web_response
.array_buffer()
.map_err(|error| anyhow!("failed to initiate response body read: {error:?}"))?;
let body_value = wasm_bindgen_futures::JsFuture::from(body_promise)
.await
.map_err(|error| anyhow!("failed to read response body: {error:?}"))?;
let array_buffer: js_sys::ArrayBuffer = body_value
.dyn_into()
.map_err(|error| anyhow!("response body is not an ArrayBuffer: {error:?}"))?;
let body = js_sys::Uint8Array::new(&array_buffer).to_vec();
Ok(HttpResponse {
status: status_code,
body,
})
}))
}
Ok(HttpResponse {
status: status_code,
body,
})
}))
}
}
+9 -9
View File
@@ -3,17 +3,17 @@ use gpui::PlatformKeyboardLayout;
pub struct WebKeyboardLayout;
impl WebKeyboardLayout {
pub fn new() -> Self {
WebKeyboardLayout
}
pub fn new() -> Self {
WebKeyboardLayout
}
}
impl PlatformKeyboardLayout for WebKeyboardLayout {
fn id(&self) -> &str {
"us"
}
fn id(&self) -> &str {
"us"
}
fn name(&self) -> &str {
"US"
}
fn name(&self) -> &str {
"US"
}
}
+24 -24
View File
@@ -3,35 +3,35 @@ use log::{Level, Log, Metadata, Record};
struct ConsoleLogger;
impl Log for ConsoleLogger {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
let message = format!(
"[{}] {}: {}",
record.level(),
record.target(),
record.args()
);
let js_string = wasm_bindgen::JsValue::from_str(&message);
let message = format!(
"[{}] {}: {}",
record.level(),
record.target(),
record.args()
);
let js_string = wasm_bindgen::JsValue::from_str(&message);
match record.level() {
Level::Error => web_sys::console::error_1(&js_string),
Level::Warn => web_sys::console::warn_1(&js_string),
Level::Info => web_sys::console::info_1(&js_string),
Level::Debug | Level::Trace => web_sys::console::log_1(&js_string),
}
}
match record.level() {
Level::Error => web_sys::console::error_1(&js_string),
Level::Warn => web_sys::console::warn_1(&js_string),
Level::Info => web_sys::console::info_1(&js_string),
Level::Debug | Level::Trace => web_sys::console::log_1(&js_string),
}
}
fn flush(&self) {}
fn flush(&self) {}
}
pub fn init_logging() {
log::set_logger(&ConsoleLogger).ok();
log::set_max_level(log::LevelFilter::Info);
log::set_logger(&ConsoleLogger).ok();
log::set_max_level(log::LevelFilter::Info);
}
+339 -339
View File
@@ -5,431 +5,431 @@ use crate::window::WebWindow;
use anyhow::Result;
use futures::channel::oneshot;
use gpui::{
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DummyKeyboardMapper,
ForegroundExecutor, Keymap, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem, PlatformWindow, Task,
ThermalState, WindowAppearance, WindowParams,
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DummyKeyboardMapper,
ForegroundExecutor, Keymap, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem, PlatformWindow, Task,
ThermalState, WindowAppearance, WindowParams,
};
use gpui_wgpu::WgpuContext;
use std::{
borrow::Cow,
cell::{Cell, RefCell},
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
borrow::Cow,
cell::{Cell, RefCell},
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
};
use wasm_bindgen::prelude::*;
static BUNDLED_FONTS: &[&[u8]] = &[
include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-Regular.ttf"),
include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-Italic.ttf"),
include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-SemiBold.ttf"),
include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-SemiBoldItalic.ttf"),
include_bytes!("../../../assets/fonts/lilex/Lilex-Regular.ttf"),
include_bytes!("../../../assets/fonts/lilex/Lilex-Bold.ttf"),
include_bytes!("../../../assets/fonts/lilex/Lilex-Italic.ttf"),
include_bytes!("../../../assets/fonts/lilex/Lilex-BoldItalic.ttf"),
include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-Regular.ttf"),
include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-Italic.ttf"),
include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-SemiBold.ttf"),
include_bytes!("../../../assets/fonts/ibm-plex-sans/IBMPlexSans-SemiBoldItalic.ttf"),
include_bytes!("../../../assets/fonts/lilex/Lilex-Regular.ttf"),
include_bytes!("../../../assets/fonts/lilex/Lilex-Bold.ttf"),
include_bytes!("../../../assets/fonts/lilex/Lilex-Italic.ttf"),
include_bytes!("../../../assets/fonts/lilex/Lilex-BoldItalic.ttf"),
];
pub struct WebPlatform {
browser_window: web_sys::Window,
background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor,
text_system: Arc<dyn PlatformTextSystem>,
active_window: RefCell<Option<AnyWindowHandle>>,
active_display: Rc<dyn PlatformDisplay>,
callbacks: RefCell<WebPlatformCallbacks>,
wgpu_context: Rc<RefCell<Option<WgpuContext>>>,
cursor_visible: Rc<Cell<bool>>,
last_cursor_css: Rc<Cell<&'static str>>,
_cursor_restore_listeners: Vec<EventListenerHandle>,
browser_window: web_sys::Window,
background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor,
text_system: Arc<dyn PlatformTextSystem>,
active_window: RefCell<Option<AnyWindowHandle>>,
active_display: Rc<dyn PlatformDisplay>,
callbacks: RefCell<WebPlatformCallbacks>,
wgpu_context: Rc<RefCell<Option<WgpuContext>>>,
cursor_visible: Rc<Cell<bool>>,
last_cursor_css: Rc<Cell<&'static str>>,
_cursor_restore_listeners: Vec<EventListenerHandle>,
}
#[derive(Default)]
struct WebPlatformCallbacks {
open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
quit: Option<Box<dyn FnMut()>>,
reopen: Option<Box<dyn FnMut()>>,
app_menu_action: Option<Box<dyn FnMut(&dyn Action)>>,
will_open_app_menu: Option<Box<dyn FnMut()>>,
validate_app_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
keyboard_layout_change: Option<Box<dyn FnMut()>>,
thermal_state_change: Option<Box<dyn FnMut()>>,
open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
quit: Option<Box<dyn FnMut()>>,
reopen: Option<Box<dyn FnMut()>>,
app_menu_action: Option<Box<dyn FnMut(&dyn Action)>>,
will_open_app_menu: Option<Box<dyn FnMut()>>,
validate_app_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
keyboard_layout_change: Option<Box<dyn FnMut()>>,
thermal_state_change: Option<Box<dyn FnMut()>>,
}
impl WebPlatform {
pub fn new(allow_multi_threading: bool) -> Self {
let browser_window =
web_sys::window().expect("must be running in a browser window context");
let dispatcher = Arc::new(WebDispatcher::new(
browser_window.clone(),
allow_multi_threading,
));
let background_executor = BackgroundExecutor::new(dispatcher.clone());
let foreground_executor = ForegroundExecutor::new(dispatcher);
let text_system = Arc::new(gpui_wgpu::CosmicTextSystem::new_without_system_fonts(
"IBM Plex Sans",
));
let fonts = BUNDLED_FONTS
.iter()
.map(|bytes| Cow::Borrowed(*bytes))
.collect();
if let Err(error) = text_system.add_fonts(fonts) {
log::error!("failed to load bundled fonts: {error:#}");
}
let text_system: Arc<dyn PlatformTextSystem> = text_system;
let active_display: Rc<dyn PlatformDisplay> =
Rc::new(WebDisplay::new(browser_window.clone()));
pub fn new(allow_multi_threading: bool) -> Self {
let browser_window =
web_sys::window().expect("must be running in a browser window context");
let dispatcher = Arc::new(WebDispatcher::new(
browser_window.clone(),
allow_multi_threading,
));
let background_executor = BackgroundExecutor::new(dispatcher.clone());
let foreground_executor = ForegroundExecutor::new(dispatcher);
let text_system = Arc::new(gpui_wgpu::CosmicTextSystem::new_without_system_fonts(
"IBM Plex Sans",
));
let fonts = BUNDLED_FONTS
.iter()
.map(|bytes| Cow::Borrowed(*bytes))
.collect();
if let Err(error) = text_system.add_fonts(fonts) {
log::error!("failed to load bundled fonts: {error:#}");
}
let text_system: Arc<dyn PlatformTextSystem> = text_system;
let active_display: Rc<dyn PlatformDisplay> =
Rc::new(WebDisplay::new(browser_window.clone()));
let cursor_visible = Rc::new(Cell::new(true));
let last_cursor_css = Rc::new(Cell::new("default"));
let cursor_restore_listeners = cursor_restore_listeners(
&browser_window,
cursor_visible.clone(),
last_cursor_css.clone(),
);
let cursor_visible = Rc::new(Cell::new(true));
let last_cursor_css = Rc::new(Cell::new("default"));
let cursor_restore_listeners = cursor_restore_listeners(
&browser_window,
cursor_visible.clone(),
last_cursor_css.clone(),
);
Self {
browser_window,
background_executor,
foreground_executor,
text_system,
active_window: RefCell::new(None),
active_display,
callbacks: RefCell::new(WebPlatformCallbacks::default()),
wgpu_context: Rc::new(RefCell::new(None)),
cursor_visible,
last_cursor_css,
_cursor_restore_listeners: cursor_restore_listeners,
}
}
Self {
browser_window,
background_executor,
foreground_executor,
text_system,
active_window: RefCell::new(None),
active_display,
callbacks: RefCell::new(WebPlatformCallbacks::default()),
wgpu_context: Rc::new(RefCell::new(None)),
cursor_visible,
last_cursor_css,
_cursor_restore_listeners: cursor_restore_listeners,
}
}
}
impl Platform for WebPlatform {
fn background_executor(&self) -> BackgroundExecutor {
self.background_executor.clone()
}
fn background_executor(&self) -> BackgroundExecutor {
self.background_executor.clone()
}
fn foreground_executor(&self) -> ForegroundExecutor {
self.foreground_executor.clone()
}
fn foreground_executor(&self) -> ForegroundExecutor {
self.foreground_executor.clone()
}
fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
self.text_system.clone()
}
fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
self.text_system.clone()
}
fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
let wgpu_context = self.wgpu_context.clone();
wasm_bindgen_futures::spawn_local(async move {
match WgpuContext::new_web().await {
Ok(context) => {
log::info!("WebGPU context initialized successfully");
*wgpu_context.borrow_mut() = Some(context);
on_finish_launching();
}
Err(err) => {
log::error!("Failed to initialize WebGPU context: {err:#}");
on_finish_launching();
}
}
});
}
fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
let wgpu_context = self.wgpu_context.clone();
wasm_bindgen_futures::spawn_local(async move {
match WgpuContext::new_web().await {
Ok(context) => {
log::info!("WebGPU context initialized successfully");
*wgpu_context.borrow_mut() = Some(context);
on_finish_launching();
}
Err(err) => {
log::error!("Failed to initialize WebGPU context: {err:#}");
on_finish_launching();
}
}
});
}
fn quit(&self) {
log::warn!("WebPlatform::quit called, but quitting is not supported in the browser .");
}
fn quit(&self) {
log::warn!("WebPlatform::quit called, but quitting is not supported in the browser .");
}
fn restart(&self, _binary_path: Option<PathBuf>) {}
fn restart(&self, _binary_path: Option<PathBuf>) {}
fn activate(&self, _ignoring_other_apps: bool) {}
fn activate(&self, _ignoring_other_apps: bool) {}
fn hide(&self) {}
fn hide(&self) {}
fn hide_other_apps(&self) {}
fn hide_other_apps(&self) {}
fn unhide_other_apps(&self) {}
fn unhide_other_apps(&self) {}
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
vec![self.active_display.clone()]
}
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
vec![self.active_display.clone()]
}
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
Some(self.active_display.clone())
}
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
Some(self.active_display.clone())
}
fn active_window(&self) -> Option<AnyWindowHandle> {
*self.active_window.borrow()
}
fn active_window(&self) -> Option<AnyWindowHandle> {
*self.active_window.borrow()
}
fn open_window(
&self,
handle: AnyWindowHandle,
params: WindowParams,
) -> anyhow::Result<Box<dyn PlatformWindow>> {
let context_ref = self.wgpu_context.borrow();
let context = context_ref.as_ref().ok_or_else(|| {
anyhow::anyhow!("WebGPU context not initialized. Was Platform::run() called?")
})?;
fn open_window(
&self,
handle: AnyWindowHandle,
params: WindowParams,
) -> anyhow::Result<Box<dyn PlatformWindow>> {
let context_ref = self.wgpu_context.borrow();
let context = context_ref.as_ref().ok_or_else(|| {
anyhow::anyhow!("WebGPU context not initialized. Was Platform::run() called?")
})?;
let window = WebWindow::new(handle, params, context, self.browser_window.clone())?;
*self.active_window.borrow_mut() = Some(handle);
Ok(Box::new(window))
}
let window = WebWindow::new(handle, params, context, self.browser_window.clone())?;
*self.active_window.borrow_mut() = Some(handle);
Ok(Box::new(window))
}
fn window_appearance(&self) -> WindowAppearance {
let Ok(Some(media_query)) = self
.browser_window
.match_media("(prefers-color-scheme: dark)")
else {
return WindowAppearance::Light;
};
if media_query.matches() {
WindowAppearance::Dark
} else {
WindowAppearance::Light
}
}
fn window_appearance(&self) -> WindowAppearance {
let Ok(Some(media_query)) = self
.browser_window
.match_media("(prefers-color-scheme: dark)")
else {
return WindowAppearance::Light;
};
if media_query.matches() {
WindowAppearance::Dark
} else {
WindowAppearance::Light
}
}
fn open_url(&self, url: &str) {
if let Err(error) = self.browser_window.open_with_url(url) {
log::warn!("Failed to open URL '{url}': {error:?}");
}
}
fn open_url(&self, url: &str) {
if let Err(error) = self.browser_window.open_with_url(url) {
log::warn!("Failed to open URL '{url}': {error:?}");
}
}
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
self.callbacks.borrow_mut().open_urls = Some(callback);
}
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
self.callbacks.borrow_mut().open_urls = Some(callback);
}
fn register_url_scheme(&self, _url: &str) -> Task<Result<()>> {
Task::ready(Ok(()))
}
fn register_url_scheme(&self, _url: &str) -> Task<Result<()>> {
Task::ready(Ok(()))
}
fn prompt_for_paths(
&self,
_options: PathPromptOptions,
) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>> {
let (tx, rx) = oneshot::channel();
tx.send(Err(anyhow::anyhow!(
"prompt_for_paths is not supported on the web"
)))
.ok();
rx
}
fn prompt_for_paths(
&self,
_options: PathPromptOptions,
) -> oneshot::Receiver<Result<Option<Vec<PathBuf>>>> {
let (tx, rx) = oneshot::channel();
tx.send(Err(anyhow::anyhow!(
"prompt_for_paths is not supported on the web"
)))
.ok();
rx
}
fn prompt_for_new_path(
&self,
_directory: &Path,
_suggested_name: Option<&str>,
) -> oneshot::Receiver<Result<Option<PathBuf>>> {
let (sender, receiver) = oneshot::channel();
sender
.send(Err(anyhow::anyhow!(
"prompt_for_new_path is not supported on the web"
)))
.ok();
receiver
}
fn prompt_for_new_path(
&self,
_directory: &Path,
_suggested_name: Option<&str>,
) -> oneshot::Receiver<Result<Option<PathBuf>>> {
let (sender, receiver) = oneshot::channel();
sender
.send(Err(anyhow::anyhow!(
"prompt_for_new_path is not supported on the web"
)))
.ok();
receiver
}
fn can_select_mixed_files_and_dirs(&self) -> bool {
false
}
fn can_select_mixed_files_and_dirs(&self) -> bool {
false
}
fn reveal_path(&self, _path: &Path) {}
fn reveal_path(&self, _path: &Path) {}
fn open_with_system(&self, _path: &Path) {}
fn open_with_system(&self, _path: &Path) {}
fn on_quit(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().quit = Some(callback);
}
fn on_quit(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().quit = Some(callback);
}
fn on_reopen(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().reopen = Some(callback);
}
fn on_reopen(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().reopen = Some(callback);
}
fn set_menus(&self, _menus: Vec<Menu>, _keymap: &Keymap) {}
fn set_menus(&self, _menus: Vec<Menu>, _keymap: &Keymap) {}
fn set_dock_menu(&self, _menu: Vec<MenuItem>, _keymap: &Keymap) {}
fn set_dock_menu(&self, _menu: Vec<MenuItem>, _keymap: &Keymap) {}
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
self.callbacks.borrow_mut().app_menu_action = Some(callback);
}
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
self.callbacks.borrow_mut().app_menu_action = Some(callback);
}
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().will_open_app_menu = Some(callback);
}
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().will_open_app_menu = Some(callback);
}
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
self.callbacks.borrow_mut().validate_app_menu_command = Some(callback);
}
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
self.callbacks.borrow_mut().validate_app_menu_command = Some(callback);
}
fn thermal_state(&self) -> ThermalState {
ThermalState::Nominal
}
fn thermal_state(&self) -> ThermalState {
ThermalState::Nominal
}
fn on_thermal_state_change(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().thermal_state_change = Some(callback);
}
fn on_thermal_state_change(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().thermal_state_change = Some(callback);
}
fn compositor_name(&self) -> &'static str {
"Web"
}
fn compositor_name(&self) -> &'static str {
"Web"
}
fn app_path(&self) -> Result<PathBuf> {
Err(anyhow::anyhow!("app_path is not available on the web"))
}
fn app_path(&self) -> Result<PathBuf> {
Err(anyhow::anyhow!("app_path is not available on the web"))
}
fn path_for_auxiliary_executable(&self, _name: &str) -> Result<PathBuf> {
Err(anyhow::anyhow!(
"path_for_auxiliary_executable is not available on the web"
))
}
fn path_for_auxiliary_executable(&self, _name: &str) -> Result<PathBuf> {
Err(anyhow::anyhow!(
"path_for_auxiliary_executable is not available on the web"
))
}
fn set_cursor_style(&self, style: CursorStyle) {
let css_cursor = match style {
CursorStyle::Arrow => "default",
CursorStyle::IBeam => "text",
CursorStyle::Crosshair => "crosshair",
CursorStyle::ClosedHand => "grabbing",
CursorStyle::OpenHand => "grab",
CursorStyle::PointingHand => "pointer",
CursorStyle::ResizeLeft | CursorStyle::ResizeRight | CursorStyle::ResizeLeftRight => {
"ew-resize"
}
CursorStyle::ResizeUp | CursorStyle::ResizeDown | CursorStyle::ResizeUpDown => {
"ns-resize"
}
CursorStyle::ResizeUpLeftDownRight => "nesw-resize",
CursorStyle::ResizeUpRightDownLeft => "nwse-resize",
CursorStyle::ResizeColumn => "col-resize",
CursorStyle::ResizeRow => "row-resize",
CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
CursorStyle::OperationNotAllowed => "not-allowed",
CursorStyle::DragLink => "alias",
CursorStyle::DragCopy => "copy",
CursorStyle::ContextualMenu => "context-menu",
};
fn set_cursor_style(&self, style: CursorStyle) {
let css_cursor = match style {
CursorStyle::Arrow => "default",
CursorStyle::IBeam => "text",
CursorStyle::Crosshair => "crosshair",
CursorStyle::ClosedHand => "grabbing",
CursorStyle::OpenHand => "grab",
CursorStyle::PointingHand => "pointer",
CursorStyle::ResizeLeft | CursorStyle::ResizeRight | CursorStyle::ResizeLeftRight => {
"ew-resize"
}
CursorStyle::ResizeUp | CursorStyle::ResizeDown | CursorStyle::ResizeUpDown => {
"ns-resize"
}
CursorStyle::ResizeUpLeftDownRight => "nesw-resize",
CursorStyle::ResizeUpRightDownLeft => "nwse-resize",
CursorStyle::ResizeColumn => "col-resize",
CursorStyle::ResizeRow => "row-resize",
CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
CursorStyle::OperationNotAllowed => "not-allowed",
CursorStyle::DragLink => "alias",
CursorStyle::DragCopy => "copy",
CursorStyle::ContextualMenu => "context-menu",
};
self.last_cursor_css.set(css_cursor);
if self.cursor_visible.get() {
set_body_cursor(&self.browser_window, css_cursor);
}
}
self.last_cursor_css.set(css_cursor);
if self.cursor_visible.get() {
set_body_cursor(&self.browser_window, css_cursor);
}
}
fn hide_cursor_until_mouse_moves(&self) {
if !self.cursor_visible.replace(false) {
return;
}
set_body_cursor(&self.browser_window, "none");
}
fn hide_cursor_until_mouse_moves(&self) {
if !self.cursor_visible.replace(false) {
return;
}
set_body_cursor(&self.browser_window, "none");
}
fn is_cursor_visible(&self) -> bool {
self.cursor_visible.get()
}
fn is_cursor_visible(&self) -> bool {
self.cursor_visible.get()
}
fn should_auto_hide_scrollbars(&self) -> bool {
true
}
fn should_auto_hide_scrollbars(&self) -> bool {
true
}
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
None
}
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
None
}
fn write_to_clipboard(&self, _item: ClipboardItem) {}
fn write_to_clipboard(&self, _item: ClipboardItem) {}
fn write_credentials(&self, _url: &str, _username: &str, _password: &[u8]) -> Task<Result<()>> {
Task::ready(Err(anyhow::anyhow!(
"credential storage is not available on the web"
)))
}
fn write_credentials(&self, _url: &str, _username: &str, _password: &[u8]) -> Task<Result<()>> {
Task::ready(Err(anyhow::anyhow!(
"credential storage is not available on the web"
)))
}
fn read_credentials(&self, _url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
Task::ready(Ok(None))
}
fn read_credentials(&self, _url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
Task::ready(Ok(None))
}
fn delete_credentials(&self, _url: &str) -> Task<Result<()>> {
Task::ready(Err(anyhow::anyhow!(
"credential storage is not available on the web"
)))
}
fn delete_credentials(&self, _url: &str) -> Task<Result<()>> {
Task::ready(Err(anyhow::anyhow!(
"credential storage is not available on the web"
)))
}
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
Box::new(WebKeyboardLayout)
}
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
Box::new(WebKeyboardLayout)
}
fn keyboard_mapper(&self) -> Rc<dyn PlatformKeyboardMapper> {
Rc::new(DummyKeyboardMapper)
}
fn keyboard_mapper(&self) -> Rc<dyn PlatformKeyboardMapper> {
Rc::new(DummyKeyboardMapper)
}
fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().keyboard_layout_change = Some(callback);
}
fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>) {
self.callbacks.borrow_mut().keyboard_layout_change = Some(callback);
}
}
struct EventListenerHandle {
target: web_sys::EventTarget,
event_name: &'static str,
closure: Closure<dyn FnMut(JsValue)>,
target: web_sys::EventTarget,
event_name: &'static str,
closure: Closure<dyn FnMut(JsValue)>,
}
impl Drop for EventListenerHandle {
fn drop(&mut self) {
self.target
.remove_event_listener_with_callback(
self.event_name,
self.closure.as_ref().unchecked_ref(),
)
.ok();
}
fn drop(&mut self) {
self.target
.remove_event_listener_with_callback(
self.event_name,
self.closure.as_ref().unchecked_ref(),
)
.ok();
}
}
fn cursor_restore_listeners(
browser_window: &web_sys::Window,
cursor_visible: Rc<Cell<bool>>,
last_cursor_css: Rc<Cell<&'static str>>,
browser_window: &web_sys::Window,
cursor_visible: Rc<Cell<bool>>,
last_cursor_css: Rc<Cell<&'static str>>,
) -> Vec<EventListenerHandle> {
let mut handles = Vec::new();
let Some(document) = browser_window.document() else {
return handles;
};
let mut handles = Vec::new();
let Some(document) = browser_window.document() else {
return handles;
};
let make_restore_handler = |browser_window: web_sys::Window| {
let cursor_visible = cursor_visible.clone();
let last_cursor_css = last_cursor_css.clone();
Closure::<dyn FnMut(JsValue)>::new(move |_event: JsValue| {
if !cursor_visible.replace(true) {
set_body_cursor(&browser_window, last_cursor_css.get());
}
})
};
let make_restore_handler = |browser_window: web_sys::Window| {
let cursor_visible = cursor_visible.clone();
let last_cursor_css = last_cursor_css.clone();
Closure::<dyn FnMut(JsValue)>::new(move |_event: JsValue| {
if !cursor_visible.replace(true) {
set_body_cursor(&browser_window, last_cursor_css.get());
}
})
};
let mut add_listener = |target: &web_sys::EventTarget, event_name: &'static str| {
let closure = make_restore_handler(browser_window.clone());
target
.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())
.ok();
handles.push(EventListenerHandle {
target: target.clone(),
event_name,
closure,
});
};
let mut add_listener = |target: &web_sys::EventTarget, event_name: &'static str| {
let closure = make_restore_handler(browser_window.clone());
target
.add_event_listener_with_callback(event_name, closure.as_ref().unchecked_ref())
.ok();
handles.push(EventListenerHandle {
target: target.clone(),
event_name,
closure,
});
};
let document_target: &web_sys::EventTarget = document.as_ref();
let window_target: &web_sys::EventTarget = browser_window.as_ref();
let document_target: &web_sys::EventTarget = document.as_ref();
let window_target: &web_sys::EventTarget = browser_window.as_ref();
add_listener(document_target, "mousemove");
add_listener(document_target, "mouseenter");
add_listener(window_target, "blur");
add_listener(document_target, "visibilitychange");
add_listener(document_target, "mousemove");
add_listener(document_target, "mouseenter");
add_listener(window_target, "blur");
add_listener(document_target, "visibilitychange");
handles
handles
}
fn set_body_cursor(browser_window: &web_sys::Window, css_cursor: &str) {
if let Some(document) = browser_window.document()
&& let Some(body) = document.body()
&& let Err(error) = body.style().set_property("cursor", css_cursor)
{
log::warn!("Failed to set cursor style: {error:?}");
}
if let Some(document) = browser_window.document()
&& let Some(body) = document.body()
&& let Err(error) = body.style().set_property("cursor", css_cursor)
{
log::warn!("Failed to set cursor style: {error:?}");
}
}
File diff suppressed because it is too large Load Diff