Windows gpui platform (#8490)

į™―åąąéĒĻéœē and Marshall Bowers created

First implementation of gpui platform for Windows.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>

Change summary

Cargo.lock                                      |  78 +
crates/gpui/Cargo.toml                          |  25 
crates/gpui/src/platform.rs                     |   9 
crates/gpui/src/platform/windows.rs             |  13 
crates/gpui/src/platform/windows/dispatcher.rs  | 159 +++++
crates/gpui/src/platform/windows/display.rs     |  36 +
crates/gpui/src/platform/windows/platform.rs    | 317 +++++++++++
crates/gpui/src/platform/windows/text_system.rs | 440 +++++++++++++++
crates/gpui/src/platform/windows/util.rs        |  26 
crates/gpui/src/platform/windows/window.rs      | 535 +++++++++++++++++++
crates/zed/build.rs                             |   5 
11 files changed, 1,610 insertions(+), 33 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -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"

crates/gpui/Cargo.toml 🔗

@@ -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"

crates/gpui/src/platform.rs 🔗

@@ -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 {

crates/gpui/src/platform/windows.rs 🔗

@@ -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::*;

crates/gpui/src/platform/windows/dispatcher.rs 🔗

@@ -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))
+    }
+}

crates/gpui/src/platform/windows/display.rs 🔗

@@ -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(),
+            },
+        )
+    }
+}

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -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."))))
+    }
+}

crates/gpui/src/platform/windows/text_system.rs 🔗

@@ -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,
+        }
+    }
+}

crates/gpui/src/platform/windows/util.rs 🔗

@@ -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
+    }
+}

crates/gpui/src/platform/windows/window.rs 🔗

@@ -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
+    }
+}

crates/zed/build.rs 🔗

@@ -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);
+    }
 }