@@ -2,7 +2,7 @@ use std::{
cell::RefCell,
ffi::OsStr,
path::{Path, PathBuf},
- rc::Rc,
+ rc::{Rc, Weak},
sync::Arc,
};
@@ -19,7 +19,7 @@ use windows::{
Foundation::*,
Graphics::Gdi::*,
Security::Credentials::*,
- System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*, Threading::*},
+ System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*},
UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},
},
core::*,
@@ -28,21 +28,27 @@ use windows::{
use crate::*;
pub(crate) struct WindowsPlatform {
- state: RefCell<WindowsPlatformState>,
+ inner: Rc<WindowsPlatformInner>,
raw_window_handles: Arc<RwLock<SmallVec<[SafeHwnd; 4]>>>,
// The below members will never change throughout the entire lifecycle of the app.
icon: HICON,
- main_receiver: flume::Receiver<Runnable>,
background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor,
text_system: Arc<DirectWriteTextSystem>,
windows_version: WindowsVersion,
drop_target_helper: IDropTargetHelper,
- validation_number: usize,
- main_thread_id_win32: u32,
+ handle: HWND,
disable_direct_composition: bool,
}
+struct WindowsPlatformInner {
+ state: RefCell<WindowsPlatformState>,
+ raw_window_handles: std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
+ // The below members will never change throughout the entire lifecycle of the app.
+ validation_number: usize,
+ main_receiver: flume::Receiver<Runnable>,
+}
+
pub(crate) struct WindowsPlatformState {
callbacks: PlatformCallbacks,
menus: Vec<OwnedMenu>,
@@ -83,11 +89,36 @@ impl WindowsPlatform {
OleInitialize(None).context("unable to initialize Windows OLE")?;
}
let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
- let main_thread_id_win32 = unsafe { GetCurrentThreadId() };
let validation_number = rand::random::<usize>();
+ let raw_window_handles = Arc::new(RwLock::new(SmallVec::new()));
+ register_platform_window_class();
+ let mut context = PlatformWindowCreateContext {
+ inner: None,
+ raw_window_handles: Arc::downgrade(&raw_window_handles),
+ validation_number,
+ main_receiver: Some(main_receiver),
+ };
+ let result = unsafe {
+ CreateWindowExW(
+ WINDOW_EX_STYLE(0),
+ PLATFORM_WINDOW_CLASS_NAME,
+ None,
+ WINDOW_STYLE(0),
+ 0,
+ 0,
+ 0,
+ 0,
+ Some(HWND_MESSAGE),
+ None,
+ None,
+ Some(&context as *const _ as *const _),
+ )
+ };
+ let inner = context.inner.take().unwrap()?;
+ let handle = result?;
let dispatcher = Arc::new(WindowsDispatcher::new(
main_sender,
- main_thread_id_win32,
+ handle,
validation_number,
));
let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION)
@@ -105,23 +136,19 @@ impl WindowsPlatform {
.context("Error creating drop target helper.")?
};
let icon = load_icon().unwrap_or_default();
- let state = RefCell::new(WindowsPlatformState::new());
- let raw_window_handles = Arc::new(RwLock::new(SmallVec::new()));
let windows_version = WindowsVersion::new().context("Error retrieve windows version")?;
Ok(Self {
- state,
+ inner,
+ handle,
raw_window_handles,
icon,
- main_receiver,
background_executor,
foreground_executor,
text_system,
disable_direct_composition,
windows_version,
drop_target_helper,
- validation_number,
- main_thread_id_win32,
})
}
@@ -143,119 +170,20 @@ impl WindowsPlatform {
});
}
- fn close_one_window(&self, target_window: HWND) -> bool {
- let mut lock = self.raw_window_handles.write();
- let index = lock
- .iter()
- .position(|handle| handle.as_raw() == target_window)
- .unwrap();
- lock.remove(index);
-
- lock.is_empty()
- }
-
- #[inline]
- fn run_foreground_task(&self) {
- for runnable in self.main_receiver.drain() {
- runnable.run();
- }
- }
-
fn generate_creation_info(&self) -> WindowCreationInfo {
WindowCreationInfo {
icon: self.icon,
executor: self.foreground_executor.clone(),
- current_cursor: self.state.borrow().current_cursor,
+ current_cursor: self.inner.state.borrow().current_cursor,
windows_version: self.windows_version,
drop_target_helper: self.drop_target_helper.clone(),
- validation_number: self.validation_number,
- main_receiver: self.main_receiver.clone(),
- main_thread_id_win32: self.main_thread_id_win32,
+ validation_number: self.inner.validation_number,
+ main_receiver: self.inner.main_receiver.clone(),
+ platform_window_handle: self.handle,
disable_direct_composition: self.disable_direct_composition,
}
}
- fn handle_dock_action_event(&self, action_idx: usize) {
- let mut lock = self.state.borrow_mut();
- if let Some(mut callback) = lock.callbacks.app_menu_action.take() {
- let Some(action) = lock
- .jump_list
- .dock_menus
- .get(action_idx)
- .map(|dock_menu| dock_menu.action.boxed_clone())
- else {
- lock.callbacks.app_menu_action = Some(callback);
- log::error!("Dock menu for index {action_idx} not found");
- return;
- };
- drop(lock);
- callback(&*action);
- self.state.borrow_mut().callbacks.app_menu_action = Some(callback);
- }
- }
-
- fn handle_input_lang_change(&self) {
- let mut lock = self.state.borrow_mut();
- if let Some(mut callback) = lock.callbacks.keyboard_layout_change.take() {
- drop(lock);
- callback();
- self.state
- .borrow_mut()
- .callbacks
- .keyboard_layout_change
- .get_or_insert(callback);
- }
- }
-
- // Returns if the app should quit.
- fn handle_events(&self) {
- let mut msg = MSG::default();
- unsafe {
- while GetMessageW(&mut msg, None, 0, 0).as_bool() {
- match msg.message {
- WM_QUIT => return,
- WM_INPUTLANGCHANGE
- | WM_GPUI_CLOSE_ONE_WINDOW
- | WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD
- | WM_GPUI_DOCK_MENU_ACTION => {
- if self.handle_gpui_events(msg.message, msg.wParam, msg.lParam, &msg) {
- return;
- }
- }
- _ => {
- DispatchMessageW(&msg);
- }
- }
- }
- }
- }
-
- // Returns true if the app should quit.
- fn handle_gpui_events(
- &self,
- message: u32,
- wparam: WPARAM,
- lparam: LPARAM,
- msg: *const MSG,
- ) -> bool {
- if wparam.0 != self.validation_number {
- unsafe { DispatchMessageW(msg) };
- return false;
- }
- match message {
- WM_GPUI_CLOSE_ONE_WINDOW => {
- if self.close_one_window(HWND(lparam.0 as _)) {
- return true;
- }
- }
- WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD => self.run_foreground_task(),
- WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _),
- WM_INPUTLANGCHANGE => self.handle_input_lang_change(),
- _ => unreachable!(),
- }
- false
- }
-
fn set_dock_menus(&self, menus: Vec<MenuItem>) {
let mut actions = Vec::new();
menus.into_iter().for_each(|menu| {
@@ -263,7 +191,7 @@ impl WindowsPlatform {
actions.push(dock_menu);
}
});
- let mut lock = self.state.borrow_mut();
+ let mut lock = self.inner.state.borrow_mut();
lock.jump_list.dock_menus = actions;
update_jump_list(&lock.jump_list).log_err();
}
@@ -279,7 +207,7 @@ impl WindowsPlatform {
actions.push(dock_menu);
}
});
- let mut lock = self.state.borrow_mut();
+ let mut lock = self.inner.state.borrow_mut();
lock.jump_list.dock_menus = actions;
lock.jump_list.recent_workspaces = entries;
update_jump_list(&lock.jump_list)
@@ -346,15 +274,25 @@ impl Platform for WindowsPlatform {
}
fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>) {
- self.state.borrow_mut().callbacks.keyboard_layout_change = Some(callback);
+ self.inner
+ .state
+ .borrow_mut()
+ .callbacks
+ .keyboard_layout_change = Some(callback);
}
fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
on_finish_launching();
self.begin_vsync_thread();
- self.handle_events();
- if let Some(ref mut callback) = self.state.borrow_mut().callbacks.quit {
+ let mut msg = MSG::default();
+ unsafe {
+ while GetMessageW(&mut msg, None, 0, 0).as_bool() {
+ DispatchMessageW(&msg);
+ }
+ }
+
+ if let Some(ref mut callback) = self.inner.state.borrow_mut().callbacks.quit {
callback();
}
}
@@ -469,7 +407,7 @@ impl Platform for WindowsPlatform {
}
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
- self.state.borrow_mut().callbacks.open_urls = Some(callback);
+ self.inner.state.borrow_mut().callbacks.open_urls = Some(callback);
}
fn prompt_for_paths(
@@ -539,19 +477,19 @@ impl Platform for WindowsPlatform {
}
fn on_quit(&self, callback: Box<dyn FnMut()>) {
- self.state.borrow_mut().callbacks.quit = Some(callback);
+ self.inner.state.borrow_mut().callbacks.quit = Some(callback);
}
fn on_reopen(&self, callback: Box<dyn FnMut()>) {
- self.state.borrow_mut().callbacks.reopen = Some(callback);
+ self.inner.state.borrow_mut().callbacks.reopen = Some(callback);
}
fn set_menus(&self, menus: Vec<Menu>, _keymap: &Keymap) {
- self.state.borrow_mut().menus = menus.into_iter().map(|menu| menu.owned()).collect();
+ self.inner.state.borrow_mut().menus = menus.into_iter().map(|menu| menu.owned()).collect();
}
fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
- Some(self.state.borrow().menus.clone())
+ Some(self.inner.state.borrow().menus.clone())
}
fn set_dock_menu(&self, menus: Vec<MenuItem>, _keymap: &Keymap) {
@@ -559,15 +497,19 @@ impl Platform for WindowsPlatform {
}
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
- self.state.borrow_mut().callbacks.app_menu_action = Some(callback);
+ self.inner.state.borrow_mut().callbacks.app_menu_action = Some(callback);
}
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
- self.state.borrow_mut().callbacks.will_open_app_menu = Some(callback);
+ self.inner.state.borrow_mut().callbacks.will_open_app_menu = Some(callback);
}
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
- self.state.borrow_mut().callbacks.validate_app_menu_command = Some(callback);
+ self.inner
+ .state
+ .borrow_mut()
+ .callbacks
+ .validate_app_menu_command = Some(callback);
}
fn app_path(&self) -> Result<PathBuf> {
@@ -581,7 +523,7 @@ impl Platform for WindowsPlatform {
fn set_cursor_style(&self, style: CursorStyle) {
let hcursor = load_cursor(style);
- let mut lock = self.state.borrow_mut();
+ let mut lock = self.inner.state.borrow_mut();
if lock.current_cursor.map(|c| c.0) != hcursor.map(|c| c.0) {
self.post_message(
WM_GPUI_CURSOR_STYLE_CHANGED,
@@ -684,10 +626,10 @@ impl Platform for WindowsPlatform {
fn perform_dock_menu_action(&self, action: usize) {
unsafe {
- PostThreadMessageW(
- self.main_thread_id_win32,
+ PostMessageW(
+ Some(self.handle),
WM_GPUI_DOCK_MENU_ACTION,
- WPARAM(self.validation_number),
+ WPARAM(self.inner.validation_number),
LPARAM(action as isize),
)
.log_err();
@@ -703,9 +645,118 @@ impl Platform for WindowsPlatform {
}
}
+impl WindowsPlatformInner {
+ fn new(context: &mut PlatformWindowCreateContext) -> Result<Rc<Self>> {
+ let state = RefCell::new(WindowsPlatformState::new());
+ Ok(Rc::new(Self {
+ state,
+ raw_window_handles: context.raw_window_handles.clone(),
+ validation_number: context.validation_number,
+ main_receiver: context.main_receiver.take().unwrap(),
+ }))
+ }
+
+ fn handle_msg(
+ self: &Rc<Self>,
+ handle: HWND,
+ msg: u32,
+ wparam: WPARAM,
+ lparam: LPARAM,
+ ) -> LRESULT {
+ let handled = match msg {
+ WM_GPUI_CLOSE_ONE_WINDOW
+ | WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD
+ | WM_GPUI_DOCK_MENU_ACTION
+ | WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_gpui_events(msg, wparam, lparam),
+ _ => None,
+ };
+ if let Some(result) = handled {
+ LRESULT(result)
+ } else {
+ unsafe { DefWindowProcW(handle, msg, wparam, lparam) }
+ }
+ }
+
+ fn handle_gpui_events(&self, message: u32, wparam: WPARAM, lparam: LPARAM) -> Option<isize> {
+ if wparam.0 != self.validation_number {
+ log::error!("Wrong validation number while processing message: {message}");
+ return None;
+ }
+ match message {
+ WM_GPUI_CLOSE_ONE_WINDOW => {
+ if self.close_one_window(HWND(lparam.0 as _)) {
+ unsafe { PostQuitMessage(0) };
+ }
+ Some(0)
+ }
+ WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD => self.run_foreground_task(),
+ WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _),
+ WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_keyboard_layout_change(),
+ _ => unreachable!(),
+ }
+ }
+
+ fn close_one_window(&self, target_window: HWND) -> bool {
+ let Some(all_windows) = self.raw_window_handles.upgrade() else {
+ log::error!("Failed to upgrade raw window handles");
+ return false;
+ };
+ let mut lock = all_windows.write();
+ let index = lock
+ .iter()
+ .position(|handle| handle.as_raw() == target_window)
+ .unwrap();
+ lock.remove(index);
+
+ lock.is_empty()
+ }
+
+ #[inline]
+ fn run_foreground_task(&self) -> Option<isize> {
+ for runnable in self.main_receiver.drain() {
+ runnable.run();
+ }
+ Some(0)
+ }
+
+ fn handle_dock_action_event(&self, action_idx: usize) -> Option<isize> {
+ let mut lock = self.state.borrow_mut();
+ let mut callback = lock.callbacks.app_menu_action.take()?;
+ let Some(action) = lock
+ .jump_list
+ .dock_menus
+ .get(action_idx)
+ .map(|dock_menu| dock_menu.action.boxed_clone())
+ else {
+ lock.callbacks.app_menu_action = Some(callback);
+ log::error!("Dock menu for index {action_idx} not found");
+ return Some(1);
+ };
+ drop(lock);
+ callback(&*action);
+ self.state.borrow_mut().callbacks.app_menu_action = Some(callback);
+ Some(0)
+ }
+
+ fn handle_keyboard_layout_change(&self) -> Option<isize> {
+ let mut callback = self
+ .state
+ .borrow_mut()
+ .callbacks
+ .keyboard_layout_change
+ .take()?;
+ callback();
+ self.state.borrow_mut().callbacks.keyboard_layout_change = Some(callback);
+ Some(0)
+ }
+}
+
impl Drop for WindowsPlatform {
fn drop(&mut self) {
unsafe {
+ DestroyWindow(self.handle)
+ .context("Destroying platform window")
+ .log_err();
OleUninitialize();
}
}
@@ -719,10 +770,17 @@ pub(crate) struct WindowCreationInfo {
pub(crate) drop_target_helper: IDropTargetHelper,
pub(crate) validation_number: usize,
pub(crate) main_receiver: flume::Receiver<Runnable>,
- pub(crate) main_thread_id_win32: u32,
+ pub(crate) platform_window_handle: HWND,
pub(crate) disable_direct_composition: bool,
}
+struct PlatformWindowCreateContext {
+ inner: Option<Result<Rc<WindowsPlatformInner>>>,
+ raw_window_handles: std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
+ validation_number: usize,
+ main_receiver: Option<flume::Receiver<Runnable>>,
+}
+
fn open_target(target: impl AsRef<OsStr>) -> Result<()> {
let target = target.as_ref();
let ret = unsafe {
@@ -893,6 +951,61 @@ fn should_auto_hide_scrollbars() -> Result<bool> {
Ok(ui_settings.AutoHideScrollBars()?)
}
+const PLATFORM_WINDOW_CLASS_NAME: PCWSTR = w!("Zed::PlatformWindow");
+
+fn register_platform_window_class() {
+ let wc = WNDCLASSW {
+ lpfnWndProc: Some(window_procedure),
+ lpszClassName: PCWSTR(PLATFORM_WINDOW_CLASS_NAME.as_ptr()),
+ ..Default::default()
+ };
+ unsafe { RegisterClassW(&wc) };
+}
+
+unsafe extern "system" fn window_procedure(
+ hwnd: HWND,
+ msg: u32,
+ wparam: WPARAM,
+ lparam: LPARAM,
+) -> LRESULT {
+ if msg == WM_NCCREATE {
+ let params = lparam.0 as *const CREATESTRUCTW;
+ let params = unsafe { &*params };
+ let creation_context = params.lpCreateParams as *mut PlatformWindowCreateContext;
+ let creation_context = unsafe { &mut *creation_context };
+ return match WindowsPlatformInner::new(creation_context) {
+ Ok(inner) => {
+ let weak = Box::new(Rc::downgrade(&inner));
+ unsafe { set_window_long(hwnd, GWLP_USERDATA, Box::into_raw(weak) as isize) };
+ creation_context.inner = Some(Ok(inner));
+ unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
+ }
+ Err(error) => {
+ creation_context.inner = Some(Err(error));
+ LRESULT(0)
+ }
+ };
+ }
+
+ let ptr = unsafe { get_window_long(hwnd, GWLP_USERDATA) } as *mut Weak<WindowsPlatformInner>;
+ if ptr.is_null() {
+ return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) };
+ }
+ let inner = unsafe { &*ptr };
+ let result = if let Some(inner) = inner.upgrade() {
+ inner.handle_msg(hwnd, msg, wparam, lparam)
+ } else {
+ unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
+ };
+
+ if msg == WM_NCDESTROY {
+ unsafe { set_window_long(hwnd, GWLP_USERDATA, 0) };
+ unsafe { drop(Box::from_raw(ptr)) };
+ }
+
+ result
+}
+
#[cfg(test)]
mod tests {
use crate::{ClipboardItem, read_from_clipboard, write_to_clipboard};
@@ -73,7 +73,7 @@ pub(crate) struct WindowsWindowInner {
pub(crate) windows_version: WindowsVersion,
pub(crate) validation_number: usize,
pub(crate) main_receiver: flume::Receiver<Runnable>,
- pub(crate) main_thread_id_win32: u32,
+ pub(crate) platform_window_handle: HWND,
}
impl WindowsWindowState {
@@ -228,7 +228,7 @@ impl WindowsWindowInner {
windows_version: context.windows_version,
validation_number: context.validation_number,
main_receiver: context.main_receiver.clone(),
- main_thread_id_win32: context.main_thread_id_win32,
+ platform_window_handle: context.platform_window_handle,
}))
}
@@ -342,7 +342,7 @@ struct WindowCreateContext {
drop_target_helper: IDropTargetHelper,
validation_number: usize,
main_receiver: flume::Receiver<Runnable>,
- main_thread_id_win32: u32,
+ platform_window_handle: HWND,
appearance: WindowAppearance,
disable_direct_composition: bool,
}
@@ -361,7 +361,7 @@ impl WindowsWindow {
drop_target_helper,
validation_number,
main_receiver,
- main_thread_id_win32,
+ platform_window_handle,
disable_direct_composition,
} = creation_info;
register_window_class(icon);
@@ -419,7 +419,7 @@ impl WindowsWindow {
drop_target_helper,
validation_number,
main_receiver,
- main_thread_id_win32,
+ platform_window_handle,
appearance,
disable_direct_composition,
};