windows: display icon (#9571)

张小白 created

Now `Zed` can display icons. The image below shows the icon of the
`zed.exe` file and the icon in the right-click properties.

![Screenshot 2024-03-20
181054](https://github.com/zed-industries/zed/assets/14981363/8f1ccc7f-aab0-46cf-8c32-a3545ba710a3)

I used the `crates\zed\resources\app-icon@2x.png` file to generate the
`.ico` file. Due to some blank space around the logo in the original
file, the logo appears slightly smaller on Windows compared to other
software.

![Screenshot 2024-03-20
181155](https://github.com/zed-industries/zed/assets/14981363/874c5ed3-6796-428c-9a91-f91231bb6510)

The current `.ico` file contains logo files of multiple sizes: 16x16,
24x24, 32x32, 48x48, 64x64, 96x96, 128x128, 256x256, 512x512.

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/platform.rs | 21 ++++++++++++++++++++-
crates/gpui/src/platform/windows/window.rs   |  5 +++--
crates/zed/build.rs                          |  7 +++++--
crates/zed/resources/windows/app-icon.ico    |  0 
4 files changed, 28 insertions(+), 5 deletions(-)

Detailed changes

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

@@ -14,7 +14,7 @@ use std::{
 };
 
 use ::util::{ResultExt, SemanticVersion};
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, Context, Result};
 use async_task::Runnable;
 use copypasta::{ClipboardContext, ClipboardProvider};
 use futures::channel::oneshot::{self, Receiver};
@@ -62,6 +62,7 @@ pub(crate) struct WindowsPlatformInner {
     pub raw_window_handles: RwLock<SmallVec<[HWND; 4]>>,
     pub(crate) dispatch_event: OwnedHandle,
     pub(crate) settings: RefCell<WindowsPlatformSystemSettings>,
+    pub icon: HICON,
 }
 
 impl WindowsPlatformInner {
@@ -156,6 +157,7 @@ impl WindowsPlatform {
         let callbacks = Mutex::new(Callbacks::default());
         let raw_window_handles = RwLock::new(SmallVec::new());
         let settings = RefCell::new(WindowsPlatformSystemSettings::new());
+        let icon = load_icon().unwrap_or_default();
         let inner = Rc::new(WindowsPlatformInner {
             background_executor,
             foreground_executor,
@@ -165,6 +167,7 @@ impl WindowsPlatform {
             raw_window_handles,
             dispatch_event,
             settings,
+            icon,
         });
         Self { inner }
     }
@@ -881,3 +884,19 @@ fn fallback_vsync_fn() -> impl Fn(HANDLE) -> bool + Send {
         (unsafe { WaitForSingleObject(timer_stop_event, interval) }) == WAIT_TIMEOUT
     }
 }
+
+fn load_icon() -> Result<HICON> {
+    let module = unsafe { GetModuleHandleW(None).context("unable to get module handle")? };
+    let handle = unsafe {
+        LoadImageW(
+            module,
+            IDI_APPLICATION,
+            IMAGE_ICON,
+            0,
+            0,
+            LR_DEFAULTSIZE | LR_SHARED,
+        )
+        .context("unable to load icon file")?
+    };
+    Ok(HICON(handle.0))
+}

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

@@ -1097,7 +1097,7 @@ impl WindowsWindow {
         handle: AnyWindowHandle,
         options: WindowParams,
     ) -> Self {
-        let classname = register_wnd_class();
+        let classname = register_wnd_class(platform_inner.icon);
         let hide_title_bar = options
             .titlebar
             .as_ref()
@@ -1556,13 +1556,14 @@ impl IDropTarget_Impl for WindowsDragDropHandler {
     }
 }
 
-fn register_wnd_class() -> PCWSTR {
+fn register_wnd_class(icon_handle: HICON) -> PCWSTR {
     const CLASS_NAME: PCWSTR = w!("Zed::Window");
 
     static ONCE: Once = Once::new();
     ONCE.call_once(|| {
         let wc = WNDCLASSW {
             lpfnWndProc: Some(wnd_proc),
+            hIcon: icon_handle,
             hCursor: unsafe { LoadCursorW(None, IDC_ARROW).ok().unwrap() },
             lpszClassName: PCWSTR(CLASS_NAME.as_ptr()),
             style: CS_HREDRAW | CS_VREDRAW,

crates/zed/build.rs 🔗

@@ -50,15 +50,18 @@ fn main() {
         println!("cargo:rustc-link-arg=/stack:{}", 8 * 1024 * 1024);
 
         let manifest = std::path::Path::new("resources/windows/manifest.xml");
+        let icon = std::path::Path::new("resources/windows/app-icon.ico");
         println!("cargo:rerun-if-changed={}", manifest.display());
-        println!("cargo:rustc-link-arg-bins=/MANIFEST:EMBED");
+        println!("cargo:rerun-if-changed={}", icon.display());
 
+        println!("cargo:rustc-link-arg-bins=/MANIFEST:EMBED");
         println!(
             "cargo:rustc-link-arg-bins=/MANIFESTINPUT:{}",
             manifest.canonicalize().unwrap().display()
         );
 
-        let res = winresource::WindowsResource::new();
+        let mut res = winresource::WindowsResource::new();
+        res.set_icon(icon.to_str().unwrap());
         if let Err(e) = res.compile() {
             eprintln!("{}", e);
             std::process::exit(1);