windows: active window implementation (#9314)

Ezekiel Warren created

active window implementation - bonus vim mode works on windows now

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/platform.rs | 28 +++++++++++-----
crates/gpui/src/platform/windows/window.rs   | 36 ++++++++++++++++++++-
2 files changed, 53 insertions(+), 11 deletions(-)

Detailed changes

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

@@ -11,6 +11,7 @@ use std::{
     time::Duration,
 };
 
+use ::util::{ResultExt, SemanticVersion};
 use anyhow::{anyhow, Result};
 use async_task::Runnable;
 use copypasta::{ClipboardContext, ClipboardProvider};
@@ -19,7 +20,6 @@ use itertools::Itertools;
 use parking_lot::{Mutex, RwLock};
 use smallvec::SmallVec;
 use time::UtcOffset;
-use util::{ResultExt, SemanticVersion};
 use windows::{
     core::*,
     Wdk::System::SystemServices::*,
@@ -31,12 +31,7 @@ use windows::{
     },
 };
 
-use crate::{
-    Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor,
-    Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem,
-    PlatformWindow, Task, WindowAppearance, WindowParams, WindowsDispatcher, WindowsDisplay,
-    WindowsTextSystem, WindowsWindow,
-};
+use crate::*;
 
 pub(crate) struct WindowsPlatform {
     inner: Rc<WindowsPlatformInner>,
@@ -64,6 +59,19 @@ pub(crate) struct WindowsPlatformInner {
     pub(crate) settings: RefCell<WindowsPlatformSystemSettings>,
 }
 
+impl WindowsPlatformInner {
+    pub(crate) fn try_get_windows_inner_from_hwnd(
+        &self,
+        hwnd: HWND,
+    ) -> Option<Rc<WindowsWindowInner>> {
+        self.raw_window_handles
+            .read()
+            .iter()
+            .find(|entry| *entry == &hwnd)
+            .and_then(|hwnd| try_get_window_inner(*hwnd))
+    }
+}
+
 impl Drop for WindowsPlatformInner {
     fn drop(&mut self) {
         unsafe { CloseHandle(self.event) }.ok();
@@ -282,9 +290,11 @@ impl Platform for WindowsPlatform {
         }
     }
 
-    // todo(windows)
     fn active_window(&self) -> Option<AnyWindowHandle> {
-        None
+        let active_window_hwnd = unsafe { GetActiveWindow() };
+        self.inner
+            .try_get_windows_inner_from_hwnd(active_window_hwnd)
+            .map(|inner| inner.handle)
     }
 
     fn open_window(

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

@@ -47,7 +47,7 @@ pub(crate) struct WindowsWindowInner {
     renderer: RefCell<BladeRenderer>,
     callbacks: RefCell<Callbacks>,
     platform_inner: Rc<WindowsPlatformInner>,
-    handle: AnyWindowHandle,
+    pub(crate) handle: AnyWindowHandle,
     scale_factor: f32,
 }
 
@@ -178,6 +178,7 @@ impl WindowsWindowInner {
         match msg {
             WM_ACTIVATE => self.handle_activate_msg(msg, wparam, lparam),
             WM_CREATE => self.handle_create_msg(lparam),
+            WM_SETFOCUS => self.handle_set_focus_msg(msg, wparam, lparam),
             WM_MOVE => self.handle_move_msg(lparam),
             WM_SIZE => self.handle_size_msg(lparam),
             WM_NCCALCSIZE => self.handle_calc_client_size(msg, wparam, lparam),
@@ -968,6 +969,31 @@ impl WindowsWindowInner {
 
         unsafe { DefWindowProcW(self.hwnd, msg, wparam, lparam) }
     }
+
+    fn handle_set_focus_msg(&self, _msg: u32, wparam: WPARAM, _lparam: LPARAM) -> LRESULT {
+        // wparam is the window that just lost focus (may be null)
+        // SEE: https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-setfocus
+        let lost_focus_hwnd = HWND(wparam.0 as isize);
+        if let Some(lost_focus_window) = self
+            .platform_inner
+            .try_get_windows_inner_from_hwnd(lost_focus_hwnd)
+        {
+            lost_focus_window
+                .callbacks
+                .borrow_mut()
+                .active_status_change
+                .as_mut()
+                .map(|mut cb| cb(false));
+        }
+
+        self.callbacks
+            .borrow_mut()
+            .active_status_change
+            .as_mut()
+            .map(|mut cb| cb(true));
+
+        LRESULT(0)
+    }
 }
 
 #[derive(Default)]
@@ -1234,7 +1260,9 @@ impl PlatformWindow for WindowsWindow {
     }
 
     fn activate(&self) {
-        unsafe { ShowWindowAsync(self.inner.hwnd, SW_NORMAL) };
+        unsafe { SetActiveWindow(self.inner.hwnd) };
+        unsafe { SetFocus(self.inner.hwnd) };
+        unsafe { SetForegroundWindow(self.inner.hwnd) };
     }
 
     // todo(windows)
@@ -1497,6 +1525,10 @@ unsafe extern "system" fn wnd_proc(
 }
 
 pub(crate) fn try_get_window_inner(hwnd: HWND) -> Option<Rc<WindowsWindowInner>> {
+    if hwnd == HWND(0) {
+        return None;
+    }
+
     let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsWindowInner>;
     if !ptr.is_null() {
         let inner = unsafe { &*ptr };