mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-10 20:41:59 +00:00
Windows gpui platform (#8490)
First implementation of gpui platform for Windows. Release Notes: - N/A --------- Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
This commit is contained in:
parent
a88df2c103
commit
69e0474ebb
11 changed files with 1610 additions and 33 deletions
78
Cargo.lock
generated
78
Cargo.lock
generated
|
@ -4249,6 +4249,7 @@ dependencies = [
|
|||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"windows 0.53.0",
|
||||
"xcb",
|
||||
"xkbcommon",
|
||||
]
|
||||
|
@ -11732,6 +11733,35 @@ dependencies = [
|
|||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-targets 0.52.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-targets 0.52.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
|
@ -11756,7 +11786,7 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
"windows-targets 0.52.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -11791,17 +11821,17 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
"windows_aarch64_gnullvm 0.52.3",
|
||||
"windows_aarch64_msvc 0.52.3",
|
||||
"windows_i686_gnu 0.52.3",
|
||||
"windows_i686_msvc 0.52.3",
|
||||
"windows_x86_64_gnu 0.52.3",
|
||||
"windows_x86_64_gnullvm 0.52.3",
|
||||
"windows_x86_64_msvc 0.52.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -11818,9 +11848,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
|
@ -11836,9 +11866,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
|
@ -11854,9 +11884,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
|
@ -11872,9 +11902,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
|
@ -11890,9 +11920,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
|
@ -11908,9 +11938,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
|
@ -11926,9 +11956,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
|
|
|
@ -92,8 +92,16 @@ media.workspace = true
|
|||
metal = "0.25"
|
||||
objc = "0.2"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
[target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies]
|
||||
flume = "0.11"
|
||||
#TODO: use these on all platforms
|
||||
blade-graphics.workspace = true
|
||||
blade-macros.workspace = true
|
||||
blade-rwh.workspace = true
|
||||
bytemuck = "1"
|
||||
cosmic-text = "0.10.0"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
open = "5.0.1"
|
||||
ashpd = "0.7.0"
|
||||
xcb = { version = "1.3", features = ["as-raw-xcb-connection", "randr", "xkb"] }
|
||||
|
@ -104,12 +112,15 @@ xkbcommon = { version = "0.7", features = ["wayland", "x11"] }
|
|||
as-raw-xcb-connection = "1"
|
||||
calloop = "0.12.4"
|
||||
calloop-wayland-source = "0.2.0"
|
||||
#TODO: use these on all platforms
|
||||
blade-graphics.workspace = true
|
||||
blade-macros.workspace = true
|
||||
blade-rwh.workspace = true
|
||||
bytemuck = "1"
|
||||
cosmic-text = "0.10.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.53.0"
|
||||
features = [
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_Security",
|
||||
"Win32_System_Threading",
|
||||
]
|
||||
|
||||
[[example]]
|
||||
name = "hello_world"
|
||||
|
|
|
@ -12,12 +12,15 @@ mod linux;
|
|||
#[cfg(target_os = "macos")]
|
||||
mod mac;
|
||||
|
||||
#[cfg(any(target_os = "linux", feature = "macos-blade"))]
|
||||
#[cfg(any(target_os = "linux", target_os = "windows", feature = "macos-blade"))]
|
||||
mod blade;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
mod test;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
|
||||
use crate::{
|
||||
Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels, Font,
|
||||
FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout,
|
||||
|
@ -54,6 +57,8 @@ pub(crate) use mac::*;
|
|||
pub(crate) use test::*;
|
||||
use time::UtcOffset;
|
||||
pub use util::SemanticVersion;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(crate) use windows::*;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(crate) fn current_platform() -> Rc<dyn Platform> {
|
||||
|
@ -66,7 +71,7 @@ pub(crate) fn current_platform() -> Rc<dyn Platform> {
|
|||
// todo("windows")
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(crate) fn current_platform() -> Rc<dyn Platform> {
|
||||
unimplemented!()
|
||||
Rc::new(WindowsPlatform::new())
|
||||
}
|
||||
|
||||
pub(crate) trait Platform: 'static {
|
||||
|
|
13
crates/gpui/src/platform/windows.rs
Normal file
13
crates/gpui/src/platform/windows.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
mod dispatcher;
|
||||
mod display;
|
||||
mod platform;
|
||||
mod text_system;
|
||||
mod util;
|
||||
mod window;
|
||||
|
||||
pub(crate) use dispatcher::*;
|
||||
pub(crate) use display::*;
|
||||
pub(crate) use platform::*;
|
||||
pub(crate) use text_system::*;
|
||||
pub(crate) use util::*;
|
||||
pub(crate) use window::*;
|
159
crates/gpui/src/platform/windows/dispatcher.rs
Normal file
159
crates/gpui/src/platform/windows/dispatcher.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
use std::{
|
||||
cmp::Ordering,
|
||||
thread::{current, JoinHandle, ThreadId},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use async_task::Runnable;
|
||||
use collections::BinaryHeap;
|
||||
use flume::{RecvTimeoutError, Sender};
|
||||
use parking::Parker;
|
||||
use parking_lot::Mutex;
|
||||
use windows::Win32::{Foundation::HANDLE, System::Threading::SetEvent};
|
||||
|
||||
use crate::{PlatformDispatcher, TaskLabel};
|
||||
|
||||
pub(crate) struct WindowsDispatcher {
|
||||
background_sender: Sender<(Runnable, Option<TaskLabel>)>,
|
||||
main_sender: Sender<Runnable>,
|
||||
timer_sender: Sender<(Runnable, Duration)>,
|
||||
background_threads: Vec<JoinHandle<()>>,
|
||||
timer_thread: JoinHandle<()>,
|
||||
parker: Mutex<Parker>,
|
||||
main_thread_id: ThreadId,
|
||||
event: HANDLE,
|
||||
}
|
||||
|
||||
impl WindowsDispatcher {
|
||||
pub(crate) fn new(main_sender: Sender<Runnable>, event: HANDLE) -> Self {
|
||||
let parker = Mutex::new(Parker::new());
|
||||
let (background_sender, background_receiver) =
|
||||
flume::unbounded::<(Runnable, Option<TaskLabel>)>();
|
||||
let background_threads = (0..std::thread::available_parallelism()
|
||||
.map(|i| i.get())
|
||||
.unwrap_or(1))
|
||||
.map(|_| {
|
||||
let receiver = background_receiver.clone();
|
||||
std::thread::spawn(move || {
|
||||
for (runnable, label) in receiver {
|
||||
if let Some(label) = label {
|
||||
log::debug!("TaskLabel: {label:?}");
|
||||
}
|
||||
runnable.run();
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let (timer_sender, timer_receiver) = flume::unbounded::<(Runnable, Duration)>();
|
||||
let timer_thread = std::thread::spawn(move || {
|
||||
let mut runnables = BinaryHeap::<RunnableAfter>::new();
|
||||
let mut timeout_dur = None;
|
||||
loop {
|
||||
let recv = if let Some(dur) = timeout_dur {
|
||||
match timer_receiver.recv_timeout(dur) {
|
||||
Ok(recv) => Some(recv),
|
||||
Err(RecvTimeoutError::Timeout) => None,
|
||||
Err(RecvTimeoutError::Disconnected) => break,
|
||||
}
|
||||
} else if let Ok(recv) = timer_receiver.recv() {
|
||||
Some(recv)
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
let now = Instant::now();
|
||||
if let Some((runnable, dur)) = recv {
|
||||
runnables.push(RunnableAfter {
|
||||
runnable,
|
||||
instant: now + dur,
|
||||
});
|
||||
while let Ok((runnable, dur)) = timer_receiver.try_recv() {
|
||||
runnables.push(RunnableAfter {
|
||||
runnable,
|
||||
instant: now + dur,
|
||||
})
|
||||
}
|
||||
}
|
||||
while runnables.peek().is_some_and(|entry| entry.instant <= now) {
|
||||
runnables.pop().unwrap().runnable.run();
|
||||
}
|
||||
timeout_dur = runnables.peek().map(|entry| entry.instant - now);
|
||||
}
|
||||
});
|
||||
let main_thread_id = current().id();
|
||||
Self {
|
||||
background_sender,
|
||||
main_sender,
|
||||
timer_sender,
|
||||
background_threads,
|
||||
timer_thread,
|
||||
parker,
|
||||
main_thread_id,
|
||||
event,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformDispatcher for WindowsDispatcher {
|
||||
fn is_main_thread(&self) -> bool {
|
||||
current().id() == self.main_thread_id
|
||||
}
|
||||
|
||||
fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>) {
|
||||
self.background_sender
|
||||
.send((runnable, label))
|
||||
.inspect_err(|e| log::error!("Dispatch failed: {e}"))
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable) {
|
||||
self.main_sender
|
||||
.send(runnable)
|
||||
.inspect_err(|e| log::error!("Dispatch failed: {e}"))
|
||||
.ok();
|
||||
unsafe { SetEvent(self.event) }.ok();
|
||||
}
|
||||
|
||||
fn dispatch_after(&self, duration: std::time::Duration, runnable: Runnable) {
|
||||
self.timer_sender
|
||||
.send((runnable, duration))
|
||||
.inspect_err(|e| log::error!("Dispatch failed: {e}"))
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn tick(&self, _background_only: bool) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn park(&self) {
|
||||
self.parker.lock().park();
|
||||
}
|
||||
|
||||
fn unparker(&self) -> parking::Unparker {
|
||||
self.parker.lock().unparker()
|
||||
}
|
||||
}
|
||||
|
||||
struct RunnableAfter {
|
||||
runnable: Runnable,
|
||||
instant: Instant,
|
||||
}
|
||||
|
||||
impl PartialEq for RunnableAfter {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.instant == other.instant
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for RunnableAfter {}
|
||||
|
||||
impl Ord for RunnableAfter {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.instant.cmp(&other.instant).reverse()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for RunnableAfter {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
36
crates/gpui/src/platform/windows/display.rs
Normal file
36
crates/gpui/src/platform/windows/display.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point, Size};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct WindowsDisplay;
|
||||
|
||||
impl WindowsDisplay {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformDisplay for WindowsDisplay {
|
||||
// todo!("windows")
|
||||
fn id(&self) -> DisplayId {
|
||||
DisplayId(1)
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn uuid(&self) -> Result<Uuid> {
|
||||
Err(anyhow!("not implemented yet."))
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn bounds(&self) -> Bounds<GlobalPixels> {
|
||||
Bounds::new(
|
||||
Point::new(0.0.into(), 0.0.into()),
|
||||
Size {
|
||||
width: 1920.0.into(),
|
||||
height: 1280.0.into(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
317
crates/gpui/src/platform/windows/platform.rs
Normal file
317
crates/gpui/src/platform/windows/platform.rs
Normal file
|
@ -0,0 +1,317 @@
|
|||
// todo!("windows"): remove
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashSet,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_task::Runnable;
|
||||
use futures::channel::oneshot::Receiver;
|
||||
use parking_lot::Mutex;
|
||||
use time::UtcOffset;
|
||||
use util::SemanticVersion;
|
||||
use windows::Win32::{
|
||||
Foundation::{CloseHandle, HANDLE, HWND},
|
||||
System::Threading::{CreateEventW, INFINITE},
|
||||
UI::WindowsAndMessaging::{
|
||||
DispatchMessageW, MsgWaitForMultipleObjects, PeekMessageW, PostQuitMessage,
|
||||
TranslateMessage, MSG, PM_REMOVE, QS_ALLINPUT, WM_QUIT,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor,
|
||||
Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem,
|
||||
PlatformWindow, Task, WindowAppearance, WindowOptions, WindowsDispatcher, WindowsDisplay,
|
||||
WindowsTextSystem, WindowsWindow,
|
||||
};
|
||||
|
||||
pub(crate) struct WindowsPlatform {
|
||||
inner: Rc<WindowsPlatformInner>,
|
||||
}
|
||||
|
||||
pub(crate) struct WindowsPlatformInner {
|
||||
background_executor: BackgroundExecutor,
|
||||
pub(crate) foreground_executor: ForegroundExecutor,
|
||||
main_receiver: flume::Receiver<Runnable>,
|
||||
text_system: Arc<WindowsTextSystem>,
|
||||
callbacks: Mutex<Callbacks>,
|
||||
pub(crate) window_handles: RefCell<HashSet<AnyWindowHandle>>,
|
||||
pub(crate) event: HANDLE,
|
||||
}
|
||||
|
||||
impl Drop for WindowsPlatformInner {
|
||||
fn drop(&mut self) {
|
||||
unsafe { CloseHandle(self.event) }.ok();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Callbacks {
|
||||
open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
|
||||
become_active: Option<Box<dyn FnMut()>>,
|
||||
resign_active: Option<Box<dyn FnMut()>>,
|
||||
quit: Option<Box<dyn FnMut()>>,
|
||||
reopen: Option<Box<dyn FnMut()>>,
|
||||
event: Option<Box<dyn FnMut(PlatformInput) -> bool>>,
|
||||
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>>,
|
||||
}
|
||||
|
||||
impl WindowsPlatform {
|
||||
pub(crate) fn new() -> Self {
|
||||
let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
|
||||
let event = unsafe { CreateEventW(None, false, false, None) }.unwrap();
|
||||
let dispatcher = Arc::new(WindowsDispatcher::new(main_sender, event));
|
||||
let background_executor = BackgroundExecutor::new(dispatcher.clone());
|
||||
let foreground_executor = ForegroundExecutor::new(dispatcher);
|
||||
let text_system = Arc::new(WindowsTextSystem::new());
|
||||
let callbacks = Mutex::new(Callbacks::default());
|
||||
let window_handles = RefCell::new(HashSet::new());
|
||||
let inner = Rc::new(WindowsPlatformInner {
|
||||
background_executor,
|
||||
foreground_executor,
|
||||
main_receiver,
|
||||
text_system,
|
||||
callbacks,
|
||||
window_handles,
|
||||
event,
|
||||
});
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl Platform for WindowsPlatform {
|
||||
fn background_executor(&self) -> BackgroundExecutor {
|
||||
self.inner.background_executor.clone()
|
||||
}
|
||||
|
||||
fn foreground_executor(&self) -> ForegroundExecutor {
|
||||
self.inner.foreground_executor.clone()
|
||||
}
|
||||
|
||||
fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
|
||||
self.inner.text_system.clone()
|
||||
}
|
||||
|
||||
fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
|
||||
on_finish_launching();
|
||||
'a: loop {
|
||||
unsafe {
|
||||
MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT)
|
||||
};
|
||||
let mut msg = MSG::default();
|
||||
while unsafe { PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE) }.as_bool() {
|
||||
if msg.message == WM_QUIT {
|
||||
break 'a;
|
||||
}
|
||||
unsafe { TranslateMessage(&msg) };
|
||||
unsafe { DispatchMessageW(&msg) };
|
||||
}
|
||||
while let Ok(runnable) = self.inner.main_receiver.try_recv() {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
let mut callbacks = self.inner.callbacks.lock();
|
||||
if let Some(callback) = callbacks.quit.as_mut() {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
fn quit(&self) {
|
||||
self.foreground_executor()
|
||||
.spawn(async { unsafe { PostQuitMessage(0) } })
|
||||
.detach();
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn restart(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn activate(&self, ignoring_other_apps: bool) {}
|
||||
|
||||
// todo!("windows")
|
||||
fn hide(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn hide_other_apps(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn unhide_other_apps(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
|
||||
vec![Rc::new(WindowsDisplay::new())]
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn display(&self, id: crate::DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
|
||||
Some(Rc::new(WindowsDisplay::new()))
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowOptions,
|
||||
) -> Box<dyn PlatformWindow> {
|
||||
Box::new(WindowsWindow::new(self.inner.clone(), handle, options))
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn window_appearance(&self) -> WindowAppearance {
|
||||
WindowAppearance::Dark
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn open_url(&self, url: &str) {
|
||||
// todo!("windows")
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
|
||||
self.inner.callbacks.lock().open_urls = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn prompt_for_paths(&self, options: PathPromptOptions) -> Receiver<Option<Vec<PathBuf>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn prompt_for_new_path(&self, directory: &Path) -> Receiver<Option<PathBuf>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn reveal_path(&self, path: &Path) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_become_active(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.callbacks.lock().become_active = Some(callback);
|
||||
}
|
||||
|
||||
fn on_resign_active(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.callbacks.lock().resign_active = Some(callback);
|
||||
}
|
||||
|
||||
fn on_quit(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.callbacks.lock().quit = Some(callback);
|
||||
}
|
||||
|
||||
fn on_reopen(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.callbacks.lock().reopen = Some(callback);
|
||||
}
|
||||
|
||||
fn on_event(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
|
||||
self.inner.callbacks.lock().event = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap) {}
|
||||
|
||||
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
|
||||
self.inner.callbacks.lock().app_menu_action = Some(callback);
|
||||
}
|
||||
|
||||
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.callbacks.lock().will_open_app_menu = Some(callback);
|
||||
}
|
||||
|
||||
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
|
||||
self.inner.callbacks.lock().validate_app_menu_command = Some(callback);
|
||||
}
|
||||
|
||||
fn os_name(&self) -> &'static str {
|
||||
"Windows"
|
||||
}
|
||||
|
||||
fn os_version(&self) -> Result<SemanticVersion> {
|
||||
Ok(SemanticVersion {
|
||||
major: 1,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn app_version(&self) -> Result<SemanticVersion> {
|
||||
Ok(SemanticVersion {
|
||||
major: 1,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
})
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn app_path(&self) -> Result<PathBuf> {
|
||||
Err(anyhow!("not yet implemented"))
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn local_timezone(&self) -> UtcOffset {
|
||||
UtcOffset::from_hms(9, 0, 0).unwrap()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn double_click_interval(&self) -> Duration {
|
||||
Duration::from_millis(100)
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
|
||||
Err(anyhow!("not yet implemented"))
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn set_cursor_style(&self, style: CursorStyle) {}
|
||||
|
||||
// todo!("windows")
|
||||
fn should_auto_hide_scrollbars(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>> {
|
||||
Task::Ready(Some(Err(anyhow!("not implemented yet."))))
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
|
||||
Task::Ready(Some(Err(anyhow!("not implemented yet."))))
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
|
||||
Task::Ready(Some(Err(anyhow!("not implemented yet."))))
|
||||
}
|
||||
}
|
440
crates/gpui/src/platform/windows/text_system.rs
Normal file
440
crates/gpui/src/platform/windows/text_system.rs
Normal file
|
@ -0,0 +1,440 @@
|
|||
use crate::{
|
||||
point, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontRun, FontStyle,
|
||||
FontWeight, GlyphId, LineLayout, Pixels, PlatformTextSystem, Point, RenderGlyphParams,
|
||||
ShapedGlyph, SharedString, Size,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Ok, Result};
|
||||
use collections::HashMap;
|
||||
use cosmic_text::{
|
||||
fontdb::Query, Attrs, AttrsList, BufferLine, CacheKey, Family, Font as CosmicTextFont,
|
||||
FontSystem, SwashCache,
|
||||
};
|
||||
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
||||
use pathfinder_geometry::{
|
||||
rect::{RectF, RectI},
|
||||
vector::{Vector2F, Vector2I},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
pub(crate) struct WindowsTextSystem(RwLock<WindowsTextSystemState>);
|
||||
|
||||
struct WindowsTextSystemState {
|
||||
swash_cache: SwashCache,
|
||||
font_system: FontSystem,
|
||||
fonts: Vec<Arc<CosmicTextFont>>,
|
||||
font_selections: HashMap<Font, FontId>,
|
||||
font_ids_by_family_name: HashMap<SharedString, SmallVec<[FontId; 4]>>,
|
||||
postscript_names_by_font_id: HashMap<FontId, String>,
|
||||
}
|
||||
|
||||
impl WindowsTextSystem {
|
||||
pub(crate) fn new() -> Self {
|
||||
let mut font_system = FontSystem::new();
|
||||
|
||||
// todo!("windows") make font loading non-blocking
|
||||
font_system.db_mut().load_system_fonts();
|
||||
|
||||
Self(RwLock::new(WindowsTextSystemState {
|
||||
font_system,
|
||||
swash_cache: SwashCache::new(),
|
||||
fonts: Vec::new(),
|
||||
font_selections: HashMap::default(),
|
||||
// font_ids_by_postscript_name: HashMap::default(),
|
||||
font_ids_by_family_name: HashMap::default(),
|
||||
postscript_names_by_font_id: HashMap::default(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WindowsTextSystem {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl PlatformTextSystem for WindowsTextSystem {
|
||||
fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
|
||||
self.0.write().add_fonts(fonts)
|
||||
}
|
||||
|
||||
// todo!("windows") ensure that this integrates with platform font loading
|
||||
// do we need to do more than call load_system_fonts()?
|
||||
fn all_font_names(&self) -> Vec<String> {
|
||||
self.0
|
||||
.read()
|
||||
.font_system
|
||||
.db()
|
||||
.faces()
|
||||
.map(|face| face.post_script_name.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn all_font_families(&self) -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn font_id(&self, font: &Font) -> Result<FontId> {
|
||||
// todo!("windows"): Do we need to use CosmicText's Font APIs? Can we consolidate this to use font_kit?
|
||||
let lock = self.0.upgradable_read();
|
||||
if let Some(font_id) = lock.font_selections.get(font) {
|
||||
Ok(*font_id)
|
||||
} else {
|
||||
let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
|
||||
let candidates = if let Some(font_ids) = lock.font_ids_by_family_name.get(&font.family)
|
||||
{
|
||||
font_ids.as_slice()
|
||||
} else {
|
||||
let font_ids = lock.load_family(&font.family, font.features)?;
|
||||
lock.font_ids_by_family_name
|
||||
.insert(font.family.clone(), font_ids);
|
||||
lock.font_ids_by_family_name[&font.family].as_ref()
|
||||
};
|
||||
|
||||
let id = lock
|
||||
.font_system
|
||||
.db()
|
||||
.query(&Query {
|
||||
families: &[Family::Name(&font.family)],
|
||||
weight: font.weight.into(),
|
||||
style: font.style.into(),
|
||||
stretch: Default::default(),
|
||||
})
|
||||
.context("no font")?;
|
||||
|
||||
let font_id = if let Some(font_id) = lock.fonts.iter().position(|font| font.id() == id)
|
||||
{
|
||||
FontId(font_id)
|
||||
} else {
|
||||
// Font isn't in fonts so add it there, this is because we query all the fonts in the db
|
||||
// and maybe we haven't loaded it yet
|
||||
let font_id = FontId(lock.fonts.len());
|
||||
let font = lock.font_system.get_font(id).unwrap();
|
||||
lock.fonts.push(font);
|
||||
font_id
|
||||
};
|
||||
|
||||
lock.font_selections.insert(font.clone(), font_id);
|
||||
Ok(font_id)
|
||||
}
|
||||
}
|
||||
|
||||
fn font_metrics(&self, font_id: FontId) -> FontMetrics {
|
||||
let metrics = self.0.read().fonts[font_id.0].as_swash().metrics(&[]);
|
||||
|
||||
FontMetrics {
|
||||
units_per_em: metrics.units_per_em as u32,
|
||||
ascent: metrics.ascent,
|
||||
descent: -metrics.descent, // todo!("windows") confirm this is correct
|
||||
line_gap: metrics.leading,
|
||||
underline_position: metrics.underline_offset,
|
||||
underline_thickness: metrics.stroke_size,
|
||||
cap_height: metrics.cap_height,
|
||||
x_height: metrics.x_height,
|
||||
// todo!("windows"): Compute this correctly
|
||||
bounding_box: Bounds {
|
||||
origin: point(0.0, 0.0),
|
||||
size: size(metrics.max_width, metrics.ascent + metrics.descent),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
|
||||
let lock = self.0.read();
|
||||
let metrics = lock.fonts[font_id.0].as_swash().metrics(&[]);
|
||||
let glyph_metrics = lock.fonts[font_id.0].as_swash().glyph_metrics(&[]);
|
||||
let glyph_id = glyph_id.0 as u16;
|
||||
// todo!("windows"): Compute this correctly
|
||||
// see https://github.com/servo/font-kit/blob/master/src/loaders/freetype.rs#L614-L620
|
||||
Ok(Bounds {
|
||||
origin: point(0.0, 0.0),
|
||||
size: size(
|
||||
glyph_metrics.advance_width(glyph_id),
|
||||
glyph_metrics.advance_height(glyph_id),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
|
||||
self.0.read().advance(font_id, glyph_id)
|
||||
}
|
||||
|
||||
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
|
||||
self.0.read().glyph_for_char(font_id, ch)
|
||||
}
|
||||
|
||||
fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
|
||||
self.0.write().raster_bounds(params)
|
||||
}
|
||||
|
||||
fn rasterize_glyph(
|
||||
&self,
|
||||
params: &RenderGlyphParams,
|
||||
raster_bounds: Bounds<DevicePixels>,
|
||||
) -> Result<(Size<DevicePixels>, Vec<u8>)> {
|
||||
self.0.write().rasterize_glyph(params, raster_bounds)
|
||||
}
|
||||
|
||||
fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout {
|
||||
self.0.write().layout_line(text, font_size, runs)
|
||||
}
|
||||
|
||||
// todo!("windows") Confirm that this has been superseded by the LineWrapper
|
||||
fn wrap_line(
|
||||
&self,
|
||||
text: &str,
|
||||
font_id: FontId,
|
||||
font_size: Pixels,
|
||||
width: Pixels,
|
||||
) -> Vec<usize> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowsTextSystemState {
|
||||
#[profiling::function]
|
||||
fn add_fonts(&mut self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
|
||||
let db = self.font_system.db_mut();
|
||||
for bytes in fonts {
|
||||
match bytes {
|
||||
Cow::Borrowed(embedded_font) => {
|
||||
db.load_font_data(embedded_font.to_vec());
|
||||
}
|
||||
Cow::Owned(bytes) => {
|
||||
db.load_font_data(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[profiling::function]
|
||||
fn load_family(
|
||||
&mut self,
|
||||
name: &SharedString,
|
||||
_features: FontFeatures,
|
||||
) -> Result<SmallVec<[FontId; 4]>> {
|
||||
let mut font_ids = SmallVec::new();
|
||||
let family = self
|
||||
.font_system
|
||||
.get_font_matches(Attrs::new().family(cosmic_text::Family::Name(name)));
|
||||
for font in family.as_ref() {
|
||||
let font = self.font_system.get_font(*font).unwrap();
|
||||
if font.as_swash().charmap().map('m') == 0 {
|
||||
self.font_system.db_mut().remove_face(font.id());
|
||||
continue;
|
||||
};
|
||||
|
||||
let font_id = FontId(self.fonts.len());
|
||||
font_ids.push(font_id);
|
||||
self.fonts.push(font);
|
||||
}
|
||||
Ok(font_ids)
|
||||
}
|
||||
|
||||
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
|
||||
let width = self.fonts[font_id.0]
|
||||
.as_swash()
|
||||
.glyph_metrics(&[])
|
||||
.advance_width(glyph_id.0 as u16);
|
||||
let height = self.fonts[font_id.0]
|
||||
.as_swash()
|
||||
.glyph_metrics(&[])
|
||||
.advance_height(glyph_id.0 as u16);
|
||||
Ok(Size { width, height })
|
||||
}
|
||||
|
||||
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
|
||||
let glyph_id = self.fonts[font_id.0].as_swash().charmap().map(ch);
|
||||
if glyph_id == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(GlyphId(glyph_id.into()))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_emoji(&self, font_id: FontId) -> bool {
|
||||
// todo!("windows"): implement this correctly
|
||||
self.postscript_names_by_font_id
|
||||
.get(&font_id)
|
||||
.map_or(false, |postscript_name| {
|
||||
postscript_name == "AppleColorEmoji"
|
||||
})
|
||||
}
|
||||
|
||||
// todo!("windows") both raster functions have problems because I am not sure this is the correct mapping from cosmic text to gpui system
|
||||
fn raster_bounds(&mut self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
|
||||
let font = &self.fonts[params.font_id.0];
|
||||
let font_system = &mut self.font_system;
|
||||
let image = self
|
||||
.swash_cache
|
||||
.get_image(
|
||||
font_system,
|
||||
CacheKey::new(
|
||||
font.id(),
|
||||
params.glyph_id.0 as u16,
|
||||
(params.font_size * params.scale_factor).into(),
|
||||
(0.0, 0.0),
|
||||
)
|
||||
.0,
|
||||
)
|
||||
.clone()
|
||||
.unwrap();
|
||||
Ok(Bounds {
|
||||
origin: point(image.placement.left.into(), (-image.placement.top).into()),
|
||||
size: size(image.placement.width.into(), image.placement.height.into()),
|
||||
})
|
||||
}
|
||||
|
||||
#[profiling::function]
|
||||
fn rasterize_glyph(
|
||||
&mut self,
|
||||
params: &RenderGlyphParams,
|
||||
glyph_bounds: Bounds<DevicePixels>,
|
||||
) -> Result<(Size<DevicePixels>, Vec<u8>)> {
|
||||
if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 {
|
||||
Err(anyhow!("glyph bounds are empty"))
|
||||
} else {
|
||||
// todo!("windows") handle subpixel variants
|
||||
let bitmap_size = glyph_bounds.size;
|
||||
let font = &self.fonts[params.font_id.0];
|
||||
let font_system = &mut self.font_system;
|
||||
let image = self
|
||||
.swash_cache
|
||||
.get_image(
|
||||
font_system,
|
||||
CacheKey::new(
|
||||
font.id(),
|
||||
params.glyph_id.0 as u16,
|
||||
(params.font_size * params.scale_factor).into(),
|
||||
(0.0, 0.0),
|
||||
)
|
||||
.0,
|
||||
)
|
||||
.clone()
|
||||
.unwrap();
|
||||
|
||||
Ok((bitmap_size, image.data))
|
||||
}
|
||||
}
|
||||
|
||||
// todo!("windows") This is all a quick first pass, maybe we should be using cosmic_text::Buffer
|
||||
#[profiling::function]
|
||||
fn layout_line(&mut self, text: &str, font_size: Pixels, font_runs: &[FontRun]) -> LineLayout {
|
||||
let mut attrs_list = AttrsList::new(Attrs::new());
|
||||
let mut offs = 0;
|
||||
for run in font_runs {
|
||||
// todo!("windows") We need to check we are doing utf properly
|
||||
let font = &self.fonts[run.font_id.0];
|
||||
let font = self.font_system.db().face(font.id()).unwrap();
|
||||
attrs_list.add_span(
|
||||
offs..offs + run.len,
|
||||
Attrs::new()
|
||||
.family(Family::Name(&font.families.first().unwrap().0))
|
||||
.stretch(font.stretch)
|
||||
.style(font.style)
|
||||
.weight(font.weight),
|
||||
);
|
||||
offs += run.len;
|
||||
}
|
||||
let mut line = BufferLine::new(text, attrs_list, cosmic_text::Shaping::Advanced);
|
||||
let layout = line.layout(
|
||||
&mut self.font_system,
|
||||
font_size.0,
|
||||
f32::MAX, // todo!("windows") we don't have a width cause this should technically not be wrapped I believe
|
||||
cosmic_text::Wrap::None,
|
||||
);
|
||||
let mut runs = Vec::new();
|
||||
// todo!("windows") what I think can happen is layout returns possibly multiple lines which means we should be probably working with it higher up in the text rendering
|
||||
let layout = layout.first().unwrap();
|
||||
for glyph in &layout.glyphs {
|
||||
let font_id = glyph.font_id;
|
||||
let font_id = FontId(
|
||||
self.fonts
|
||||
.iter()
|
||||
.position(|font| font.id() == font_id)
|
||||
.unwrap(),
|
||||
);
|
||||
let mut glyphs = SmallVec::new();
|
||||
// todo!("windows") this is definitely wrong, each glyph in glyphs from cosmic-text is a cluster with one glyph, ShapedRun takes a run of glyphs with the same font and direction
|
||||
glyphs.push(ShapedGlyph {
|
||||
id: GlyphId(glyph.glyph_id as u32),
|
||||
position: point((glyph.x).into(), glyph.y.into()),
|
||||
index: glyph.start,
|
||||
is_emoji: self.is_emoji(font_id),
|
||||
});
|
||||
runs.push(crate::ShapedRun { font_id, glyphs });
|
||||
}
|
||||
LineLayout {
|
||||
font_size,
|
||||
width: layout.w.into(),
|
||||
ascent: layout.max_ascent.into(),
|
||||
descent: layout.max_descent.into(),
|
||||
runs,
|
||||
len: text.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RectF> for Bounds<f32> {
|
||||
fn from(rect: RectF) -> Self {
|
||||
Bounds {
|
||||
origin: point(rect.origin_x(), rect.origin_y()),
|
||||
size: size(rect.width(), rect.height()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RectI> for Bounds<DevicePixels> {
|
||||
fn from(rect: RectI) -> Self {
|
||||
Bounds {
|
||||
origin: point(DevicePixels(rect.origin_x()), DevicePixels(rect.origin_y())),
|
||||
size: size(DevicePixels(rect.width()), DevicePixels(rect.height())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vector2I> for Size<DevicePixels> {
|
||||
fn from(value: Vector2I) -> Self {
|
||||
size(value.x().into(), value.y().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RectI> for Bounds<i32> {
|
||||
fn from(rect: RectI) -> Self {
|
||||
Bounds {
|
||||
origin: point(rect.origin_x(), rect.origin_y()),
|
||||
size: size(rect.width(), rect.height()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point<u32>> for Vector2I {
|
||||
fn from(size: Point<u32>) -> Self {
|
||||
Vector2I::new(size.x as i32, size.y as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vector2F> for Size<f32> {
|
||||
fn from(vec: Vector2F) -> Self {
|
||||
size(vec.x(), vec.y())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FontWeight> for cosmic_text::Weight {
|
||||
fn from(value: FontWeight) -> Self {
|
||||
cosmic_text::Weight(value.0 as u16)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FontStyle> for cosmic_text::Style {
|
||||
fn from(style: FontStyle) -> Self {
|
||||
match style {
|
||||
FontStyle::Normal => cosmic_text::Style::Normal,
|
||||
FontStyle::Italic => cosmic_text::Style::Italic,
|
||||
FontStyle::Oblique => cosmic_text::Style::Oblique,
|
||||
}
|
||||
}
|
||||
}
|
26
crates/gpui/src/platform/windows/util.rs
Normal file
26
crates/gpui/src/platform/windows/util.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use windows::Win32::Foundation::{LPARAM, WPARAM};
|
||||
|
||||
pub(crate) trait HiLoWord {
|
||||
fn hiword(&self) -> u16;
|
||||
fn loword(&self) -> u16;
|
||||
}
|
||||
|
||||
impl HiLoWord for WPARAM {
|
||||
fn hiword(&self) -> u16 {
|
||||
((self.0 >> 16) & 0xFFFF) as u16
|
||||
}
|
||||
|
||||
fn loword(&self) -> u16 {
|
||||
(self.0 & 0xFFFF) as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl HiLoWord for LPARAM {
|
||||
fn hiword(&self) -> u16 {
|
||||
((self.0 >> 16) & 0xFFFF) as u16
|
||||
}
|
||||
|
||||
fn loword(&self) -> u16 {
|
||||
(self.0 & 0xFFFF) as u16
|
||||
}
|
||||
}
|
535
crates/gpui/src/platform/windows/window.rs
Normal file
535
crates/gpui/src/platform/windows/window.rs
Normal file
|
@ -0,0 +1,535 @@
|
|||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
// todo!("windows"): remove
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
ffi::c_void,
|
||||
num::NonZeroIsize,
|
||||
rc::{Rc, Weak},
|
||||
sync::{Arc, Once},
|
||||
};
|
||||
|
||||
use blade_graphics as gpu;
|
||||
use futures::channel::oneshot::Receiver;
|
||||
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
|
||||
use windows::{
|
||||
core::{w, HSTRING, PCWSTR},
|
||||
Win32::{
|
||||
Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM},
|
||||
UI::WindowsAndMessaging::{
|
||||
CreateWindowExW, DefWindowProcW, GetWindowLongPtrW, LoadCursorW, PostQuitMessage,
|
||||
RegisterClassW, SetWindowLongPtrW, SetWindowTextW, ShowWindow, CREATESTRUCTW,
|
||||
CW_USEDEFAULT, GWLP_USERDATA, HMENU, IDC_ARROW, SW_MAXIMIZE, SW_SHOW, WINDOW_EX_STYLE,
|
||||
WINDOW_LONG_PTR_INDEX, WM_CLOSE, WM_DESTROY, WM_MOVE, WM_NCCREATE, WM_NCDESTROY,
|
||||
WM_PAINT, WM_SIZE, WNDCLASSW, WS_OVERLAPPEDWINDOW, WS_VISIBLE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
platform::blade::BladeRenderer, AnyWindowHandle, Bounds, GlobalPixels, HiLoWord, Modifiers,
|
||||
Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow,
|
||||
Point, PromptLevel, Scene, Size, WindowAppearance, WindowBounds, WindowOptions, WindowsDisplay,
|
||||
WindowsPlatformInner,
|
||||
};
|
||||
|
||||
struct WindowsWindowInner {
|
||||
hwnd: HWND,
|
||||
origin: Cell<Point<GlobalPixels>>,
|
||||
size: Cell<Size<GlobalPixels>>,
|
||||
mouse_position: Cell<Point<Pixels>>,
|
||||
input_handler: Cell<Option<PlatformInputHandler>>,
|
||||
renderer: RefCell<BladeRenderer>,
|
||||
callbacks: RefCell<Callbacks>,
|
||||
platform_inner: Rc<WindowsPlatformInner>,
|
||||
handle: AnyWindowHandle,
|
||||
}
|
||||
|
||||
impl WindowsWindowInner {
|
||||
fn new(
|
||||
hwnd: HWND,
|
||||
cs: &CREATESTRUCTW,
|
||||
platform_inner: Rc<WindowsPlatformInner>,
|
||||
handle: AnyWindowHandle,
|
||||
) -> Self {
|
||||
let origin = Cell::new(Point::new((cs.x as f64).into(), (cs.y as f64).into()));
|
||||
let size = Cell::new(Size {
|
||||
width: (cs.cx as f64).into(),
|
||||
height: (cs.cy as f64).into(),
|
||||
});
|
||||
let mouse_position = Cell::new(Point::default());
|
||||
let input_handler = Cell::new(None);
|
||||
struct RawWindow {
|
||||
hwnd: *mut c_void,
|
||||
}
|
||||
unsafe impl blade_rwh::HasRawWindowHandle for RawWindow {
|
||||
fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
|
||||
let mut handle = blade_rwh::Win32WindowHandle::empty();
|
||||
handle.hwnd = self.hwnd;
|
||||
handle.into()
|
||||
}
|
||||
}
|
||||
unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
|
||||
fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
|
||||
blade_rwh::WindowsDisplayHandle::empty().into()
|
||||
}
|
||||
}
|
||||
let raw = RawWindow { hwnd: hwnd.0 as _ };
|
||||
let gpu = Arc::new(
|
||||
unsafe {
|
||||
gpu::Context::init_windowed(
|
||||
&raw,
|
||||
gpu::ContextDesc {
|
||||
validation: false,
|
||||
capture: false,
|
||||
},
|
||||
)
|
||||
}
|
||||
.unwrap(),
|
||||
);
|
||||
let extent = gpu::Extent {
|
||||
width: 1,
|
||||
height: 1,
|
||||
depth: 1,
|
||||
};
|
||||
let renderer = RefCell::new(BladeRenderer::new(gpu, extent));
|
||||
let callbacks = RefCell::new(Callbacks::default());
|
||||
Self {
|
||||
hwnd,
|
||||
origin,
|
||||
size,
|
||||
mouse_position,
|
||||
input_handler,
|
||||
renderer,
|
||||
callbacks,
|
||||
platform_inner,
|
||||
handle,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_msg(&self, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
|
||||
log::debug!("msg: {msg}, wparam: {}, lparam: {}", wparam.0, lparam.0);
|
||||
match msg {
|
||||
WM_MOVE => {
|
||||
let x = lparam.loword() as f64;
|
||||
let y = lparam.hiword() as f64;
|
||||
self.origin.set(Point::new(x.into(), y.into()));
|
||||
let mut callbacks = self.callbacks.borrow_mut();
|
||||
if let Some(callback) = callbacks.moved.as_mut() {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
WM_SIZE => {
|
||||
// todo!("windows"): handle maximized or minimized
|
||||
let width = lparam.loword().max(1) as f64;
|
||||
let height = lparam.hiword().max(1) as f64;
|
||||
self.renderer
|
||||
.borrow_mut()
|
||||
.update_drawable_size(Size { width, height });
|
||||
let width = width.into();
|
||||
let height = height.into();
|
||||
self.size.set(Size { width, height });
|
||||
let mut callbacks = self.callbacks.borrow_mut();
|
||||
if let Some(callback) = callbacks.resize.as_mut() {
|
||||
callback(
|
||||
Size {
|
||||
width: Pixels(width.0),
|
||||
height: Pixels(height.0),
|
||||
},
|
||||
1.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
WM_PAINT => {
|
||||
let mut callbacks = self.callbacks.borrow_mut();
|
||||
if let Some(callback) = callbacks.request_frame.as_mut() {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
WM_CLOSE => {
|
||||
let mut callbacks: std::cell::RefMut<'_, Callbacks> = self.callbacks.borrow_mut();
|
||||
if let Some(callback) = callbacks.should_close.as_mut() {
|
||||
if callback() {
|
||||
return LRESULT(0);
|
||||
}
|
||||
}
|
||||
drop(callbacks);
|
||||
return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) };
|
||||
}
|
||||
WM_DESTROY => {
|
||||
let mut callbacks: std::cell::RefMut<'_, Callbacks> = self.callbacks.borrow_mut();
|
||||
if let Some(callback) = callbacks.close.take() {
|
||||
callback()
|
||||
}
|
||||
let mut window_handles = self.platform_inner.window_handles.borrow_mut();
|
||||
window_handles.remove(&self.handle);
|
||||
if window_handles.is_empty() {
|
||||
self.platform_inner
|
||||
.foreground_executor
|
||||
.spawn(async {
|
||||
unsafe { PostQuitMessage(0) };
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
return LRESULT(1);
|
||||
}
|
||||
_ => return unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) },
|
||||
}
|
||||
LRESULT(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Callbacks {
|
||||
request_frame: Option<Box<dyn FnMut()>>,
|
||||
input: Option<Box<dyn FnMut(crate::PlatformInput) -> bool>>,
|
||||
active_status_change: Option<Box<dyn FnMut(bool)>>,
|
||||
resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
|
||||
fullscreen: Option<Box<dyn FnMut(bool)>>,
|
||||
moved: Option<Box<dyn FnMut()>>,
|
||||
should_close: Option<Box<dyn FnMut() -> bool>>,
|
||||
close: Option<Box<dyn FnOnce()>>,
|
||||
appearance_changed: Option<Box<dyn FnMut()>>,
|
||||
}
|
||||
|
||||
pub(crate) struct WindowsWindow {
|
||||
inner: Rc<WindowsWindowInner>,
|
||||
}
|
||||
|
||||
struct WindowCreateContext {
|
||||
inner: Option<Rc<WindowsWindowInner>>,
|
||||
platform_inner: Rc<WindowsPlatformInner>,
|
||||
handle: AnyWindowHandle,
|
||||
}
|
||||
|
||||
impl WindowsWindow {
|
||||
pub(crate) fn new(
|
||||
platform_inner: Rc<WindowsPlatformInner>,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowOptions,
|
||||
) -> Self {
|
||||
let dwexstyle = WINDOW_EX_STYLE::default();
|
||||
let classname = register_wnd_class();
|
||||
let windowname = HSTRING::from(
|
||||
options
|
||||
.titlebar
|
||||
.as_ref()
|
||||
.and_then(|titlebar| titlebar.title.as_ref())
|
||||
.map(|title| title.as_ref())
|
||||
.unwrap_or(""),
|
||||
);
|
||||
let dwstyle = WS_OVERLAPPEDWINDOW & !WS_VISIBLE;
|
||||
let mut x = CW_USEDEFAULT;
|
||||
let mut y = CW_USEDEFAULT;
|
||||
let mut nwidth = CW_USEDEFAULT;
|
||||
let mut nheight = CW_USEDEFAULT;
|
||||
match options.bounds {
|
||||
WindowBounds::Fullscreen => {}
|
||||
WindowBounds::Maximized => {}
|
||||
WindowBounds::Fixed(bounds) => {
|
||||
x = bounds.origin.x.0 as i32;
|
||||
y = bounds.origin.y.0 as i32;
|
||||
nwidth = bounds.size.width.0 as i32;
|
||||
nheight = bounds.size.height.0 as i32;
|
||||
}
|
||||
};
|
||||
let hwndparent = HWND::default();
|
||||
let hmenu = HMENU::default();
|
||||
let hinstance = HINSTANCE::default();
|
||||
let mut context = WindowCreateContext {
|
||||
inner: None,
|
||||
platform_inner: platform_inner.clone(),
|
||||
handle,
|
||||
};
|
||||
let lpparam = Some(&context as *const _ as *const _);
|
||||
unsafe {
|
||||
CreateWindowExW(
|
||||
dwexstyle,
|
||||
classname,
|
||||
&windowname,
|
||||
dwstyle,
|
||||
x,
|
||||
y,
|
||||
nwidth,
|
||||
nheight,
|
||||
hwndparent,
|
||||
hmenu,
|
||||
hinstance,
|
||||
lpparam,
|
||||
)
|
||||
};
|
||||
let wnd = Self {
|
||||
inner: context.inner.unwrap(),
|
||||
};
|
||||
platform_inner.window_handles.borrow_mut().insert(handle);
|
||||
match options.bounds {
|
||||
WindowBounds::Fullscreen => wnd.toggle_full_screen(),
|
||||
WindowBounds::Maximized => wnd.maximize(),
|
||||
WindowBounds::Fixed(_) => {}
|
||||
}
|
||||
unsafe { ShowWindow(wnd.inner.hwnd, SW_SHOW) };
|
||||
wnd
|
||||
}
|
||||
|
||||
fn maximize(&self) {
|
||||
unsafe { ShowWindow(self.inner.hwnd, SW_MAXIMIZE) };
|
||||
}
|
||||
}
|
||||
|
||||
impl HasWindowHandle for WindowsWindow {
|
||||
fn window_handle(
|
||||
&self,
|
||||
) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
|
||||
let raw = raw_window_handle::Win32WindowHandle::new(unsafe {
|
||||
NonZeroIsize::new_unchecked(self.inner.hwnd.0)
|
||||
})
|
||||
.into();
|
||||
Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(raw) })
|
||||
}
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
impl HasDisplayHandle for WindowsWindow {
|
||||
fn display_handle(
|
||||
&self,
|
||||
) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformWindow for WindowsWindow {
|
||||
fn bounds(&self) -> WindowBounds {
|
||||
WindowBounds::Fixed(Bounds {
|
||||
origin: self.inner.origin.get(),
|
||||
size: self.inner.size.get(),
|
||||
})
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn content_size(&self) -> Size<Pixels> {
|
||||
let size = self.inner.size.get();
|
||||
Size {
|
||||
width: size.width.0.into(),
|
||||
height: size.height.0.into(),
|
||||
}
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn scale_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn titlebar_height(&self) -> Pixels {
|
||||
20.0.into()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn appearance(&self) -> WindowAppearance {
|
||||
WindowAppearance::Dark
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn display(&self) -> Rc<dyn PlatformDisplay> {
|
||||
Rc::new(WindowsDisplay::new())
|
||||
}
|
||||
|
||||
fn mouse_position(&self) -> Point<Pixels> {
|
||||
self.inner.mouse_position.get()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn modifiers(&self) -> Modifiers {
|
||||
Modifiers::none()
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
|
||||
self.inner.input_handler.set(Some(input_handler));
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
|
||||
self.inner.input_handler.take()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn prompt(
|
||||
&self,
|
||||
level: PromptLevel,
|
||||
msg: &str,
|
||||
detail: Option<&str>,
|
||||
answers: &[&str],
|
||||
) -> Receiver<usize> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn activate(&self) {}
|
||||
|
||||
// todo!("windows")
|
||||
fn set_title(&mut self, title: &str) {
|
||||
unsafe { SetWindowTextW(self.inner.hwnd, &HSTRING::from(title)) }
|
||||
.inspect_err(|e| log::error!("Set title failed: {e}"))
|
||||
.ok();
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn set_edited(&mut self, edited: bool) {}
|
||||
|
||||
// todo!("windows")
|
||||
fn show_character_palette(&self) {}
|
||||
|
||||
// todo!("windows")
|
||||
fn minimize(&self) {}
|
||||
|
||||
// todo!("windows")
|
||||
fn zoom(&self) {}
|
||||
|
||||
// todo!("windows")
|
||||
fn toggle_full_screen(&self) {}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.callbacks.borrow_mut().request_frame = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
|
||||
self.inner.callbacks.borrow_mut().input = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
|
||||
self.inner.callbacks.borrow_mut().active_status_change = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
|
||||
self.inner.callbacks.borrow_mut().resize = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
|
||||
self.inner.callbacks.borrow_mut().fullscreen = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_moved(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.callbacks.borrow_mut().moved = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
|
||||
self.inner.callbacks.borrow_mut().should_close = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_close(&self, callback: Box<dyn FnOnce()>) {
|
||||
self.inner.callbacks.borrow_mut().close = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
|
||||
self.inner.callbacks.borrow_mut().appearance_changed = Some(callback);
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn draw(&self, scene: &Scene) {
|
||||
self.inner.renderer.borrow_mut().draw(scene)
|
||||
}
|
||||
|
||||
// todo!("windows")
|
||||
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
|
||||
self.inner.renderer.borrow().sprite_atlas().clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn register_wnd_class() -> PCWSTR {
|
||||
const CLASS_NAME: PCWSTR = w!("Zed::Window");
|
||||
|
||||
static ONCE: Once = Once::new();
|
||||
ONCE.call_once(|| {
|
||||
let wc = WNDCLASSW {
|
||||
lpfnWndProc: Some(wnd_proc),
|
||||
hCursor: unsafe { LoadCursorW(None, IDC_ARROW).ok().unwrap() },
|
||||
lpszClassName: PCWSTR(CLASS_NAME.as_ptr()),
|
||||
..Default::default()
|
||||
};
|
||||
unsafe { RegisterClassW(&wc) };
|
||||
});
|
||||
|
||||
CLASS_NAME
|
||||
}
|
||||
|
||||
unsafe extern "system" fn wnd_proc(
|
||||
hwnd: HWND,
|
||||
msg: u32,
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> LRESULT {
|
||||
if msg == WM_NCCREATE {
|
||||
let cs = lparam.0 as *const CREATESTRUCTW;
|
||||
let cs = unsafe { &*cs };
|
||||
let ctx = cs.lpCreateParams as *mut WindowCreateContext;
|
||||
let ctx = unsafe { &mut *ctx };
|
||||
let inner = Rc::new(WindowsWindowInner::new(
|
||||
hwnd,
|
||||
cs,
|
||||
ctx.platform_inner.clone(),
|
||||
ctx.handle,
|
||||
));
|
||||
let weak = Box::new(Rc::downgrade(&inner));
|
||||
unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) };
|
||||
ctx.inner = Some(inner);
|
||||
return LRESULT(1);
|
||||
}
|
||||
let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowInner>;
|
||||
if ptr.is_null() {
|
||||
return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) };
|
||||
}
|
||||
let inner = unsafe { &*ptr };
|
||||
let r = if let Some(inner) = inner.upgrade() {
|
||||
inner.handle_msg(msg, wparam, lparam)
|
||||
} else {
|
||||
unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
|
||||
};
|
||||
if msg == WM_NCDESTROY {
|
||||
unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) };
|
||||
unsafe { std::mem::drop(Box::from_raw(ptr)) };
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
unsafe fn get_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX) -> isize {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
unsafe {
|
||||
GetWindowLongPtrW(hwnd, nindex)
|
||||
}
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
unsafe {
|
||||
GetWindowLongW(hwnd, nindex) as isize
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_window_long(hwnd: HWND, nindex: WINDOW_LONG_PTR_INDEX, dwnewlong: isize) -> isize {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
unsafe {
|
||||
SetWindowLongPtrW(hwnd, nindex, dwnewlong)
|
||||
}
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
unsafe {
|
||||
SetWindowLongW(hwnd, nindex, dwnewlong as i32) as isize
|
||||
}
|
||||
}
|
|
@ -43,4 +43,9 @@ fn main() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo!("windows"): This is to avoid stack overflow. Remove it when solved.
|
||||
if std::env::var("CARGO_CFG_TARGET_ENV").ok() == Some("msvc".to_string()) {
|
||||
println!("cargo:rustc-link-arg=/stack:{}", 8 * 1024 * 1024);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue