windows: Use a message-only window for WindowsPlatform (#37313)
Previously, we were using `PostThreadMessage` to pass messages to `WindowsPlatform`. This PR switches to an approach similar to `winit` which using a hidden window as the message window (I guess that’s why winit uses a hidden window?). The difference is that this PR creates it as a message-only window. Thanks to @reflectronic for the original PR #37255, this implementation just fits better with the current code style. Release Notes: - N/A --------- Co-authored-by: reflectronic <john-tur@outlook.com>
This commit is contained in:
@@ -14,34 +14,37 @@ use windows::{
|
||||
},
|
||||
Win32::{
|
||||
Foundation::{LPARAM, WPARAM},
|
||||
UI::WindowsAndMessaging::PostThreadMessageW,
|
||||
UI::WindowsAndMessaging::PostMessageW,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{PlatformDispatcher, TaskLabel, WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD};
|
||||
use crate::{
|
||||
HWND, PlatformDispatcher, SafeHwnd, TaskLabel, WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD,
|
||||
};
|
||||
|
||||
pub(crate) struct WindowsDispatcher {
|
||||
main_sender: Sender<Runnable>,
|
||||
parker: Mutex<Parker>,
|
||||
main_thread_id: ThreadId,
|
||||
main_thread_id_win32: u32,
|
||||
platform_window_handle: SafeHwnd,
|
||||
validation_number: usize,
|
||||
}
|
||||
|
||||
impl WindowsDispatcher {
|
||||
pub(crate) fn new(
|
||||
main_sender: Sender<Runnable>,
|
||||
main_thread_id_win32: u32,
|
||||
platform_window_handle: HWND,
|
||||
validation_number: usize,
|
||||
) -> Self {
|
||||
let parker = Mutex::new(Parker::new());
|
||||
let main_thread_id = current().id();
|
||||
let platform_window_handle = platform_window_handle.into();
|
||||
|
||||
WindowsDispatcher {
|
||||
main_sender,
|
||||
parker,
|
||||
main_thread_id,
|
||||
main_thread_id_win32,
|
||||
platform_window_handle,
|
||||
validation_number,
|
||||
}
|
||||
}
|
||||
@@ -84,8 +87,8 @@ impl PlatformDispatcher for WindowsDispatcher {
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable) {
|
||||
match self.main_sender.send(runnable) {
|
||||
Ok(_) => unsafe {
|
||||
PostThreadMessageW(
|
||||
self.main_thread_id_win32,
|
||||
PostMessageW(
|
||||
Some(self.platform_window_handle.as_raw()),
|
||||
WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD,
|
||||
WPARAM(self.validation_number),
|
||||
LPARAM(0),
|
||||
|
||||
@@ -24,6 +24,7 @@ pub(crate) const WM_GPUI_CLOSE_ONE_WINDOW: u32 = WM_USER + 2;
|
||||
pub(crate) const WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD: u32 = WM_USER + 3;
|
||||
pub(crate) const WM_GPUI_DOCK_MENU_ACTION: u32 = WM_USER + 4;
|
||||
pub(crate) const WM_GPUI_FORCE_UPDATE_WINDOW: u32 = WM_USER + 5;
|
||||
pub(crate) const WM_GPUI_KEYBOARD_LAYOUT_CHANGED: u32 = WM_USER + 6;
|
||||
|
||||
const SIZE_MOVE_LOOP_TIMER_ID: usize = 1;
|
||||
const AUTO_HIDE_TASKBAR_THICKNESS_PX: i32 = 1;
|
||||
@@ -99,7 +100,7 @@ impl WindowsWindowInner {
|
||||
WM_IME_COMPOSITION => self.handle_ime_composition(handle, lparam),
|
||||
WM_SETCURSOR => self.handle_set_cursor(handle, lparam),
|
||||
WM_SETTINGCHANGE => self.handle_system_settings_changed(handle, wparam, lparam),
|
||||
WM_INPUTLANGCHANGE => self.handle_input_language_changed(lparam),
|
||||
WM_INPUTLANGCHANGE => self.handle_input_language_changed(),
|
||||
WM_SHOWWINDOW => self.handle_window_visibility_changed(handle, wparam),
|
||||
WM_GPUI_CURSOR_STYLE_CHANGED => self.handle_cursor_changed(lparam),
|
||||
WM_GPUI_FORCE_UPDATE_WINDOW => self.draw_window(handle, true),
|
||||
@@ -264,8 +265,8 @@ impl WindowsWindowInner {
|
||||
callback();
|
||||
}
|
||||
unsafe {
|
||||
PostThreadMessageW(
|
||||
self.main_thread_id_win32,
|
||||
PostMessageW(
|
||||
Some(self.platform_window_handle),
|
||||
WM_GPUI_CLOSE_ONE_WINDOW,
|
||||
WPARAM(self.validation_number),
|
||||
LPARAM(handle.0 as isize),
|
||||
@@ -1146,11 +1147,15 @@ impl WindowsWindowInner {
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn handle_input_language_changed(&self, lparam: LPARAM) -> Option<isize> {
|
||||
let thread = self.main_thread_id_win32;
|
||||
let validation = self.validation_number;
|
||||
fn handle_input_language_changed(&self) -> Option<isize> {
|
||||
unsafe {
|
||||
PostThreadMessageW(thread, WM_INPUTLANGCHANGE, WPARAM(validation), lparam).log_err();
|
||||
PostMessageW(
|
||||
Some(self.platform_window_handle),
|
||||
WM_GPUI_KEYBOARD_LAYOUT_CHANGED,
|
||||
WPARAM(self.validation_number),
|
||||
LPARAM(0),
|
||||
)
|
||||
.log_err();
|
||||
}
|
||||
Some(0)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user