From de8ecba9a4bbc4f0ca2b510fe7e09141eb0f7ad6 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Sun, 7 Dec 2025 04:58:15 +0200 Subject: [PATCH] fix(cef): fix start_window_dragging on Windows --- crates/tauri-runtime-cef/src/cef_impl.rs | 31 ++++++-- .../src/cef_impl/drag_window.rs | 71 +++++++++++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 crates/tauri-runtime-cef/src/cef_impl/drag_window.rs diff --git a/crates/tauri-runtime-cef/src/cef_impl.rs b/crates/tauri-runtime-cef/src/cef_impl.rs index 13446daa7b87..37d230f3e463 100644 --- a/crates/tauri-runtime-cef/src/cef_impl.rs +++ b/crates/tauri-runtime-cef/src/cef_impl.rs @@ -30,7 +30,9 @@ use crate::{ }; mod cookie; +mod drag_window; mod request_handler; + use cookie::{CollectAllCookiesVisitor, CollectUrlCookiesVisitor}; #[cfg(target_os = "linux")] @@ -823,6 +825,11 @@ wrap_window_delegate! { impl WindowDelegate { fn on_window_created(&self, window: Option<&mut Window>) { if let Some(window) = window { + + // Setup necessary handling for `start_window_dragging` to work on Windows + #[cfg(windows)] + drag_window::windows::subclass_window_for_dragging(window); + let a = self.attributes.borrow(); if let Some(icon) = a.icon.clone() { set_window_icon(window, icon); @@ -1884,16 +1891,28 @@ fn start_window_dragging(window: &cef::Window) { #[cfg(windows)] fn start_window_dragging(window: &cef::Window) { - use windows::Win32::Foundation::HWND; - use windows::Win32::UI::WindowsAndMessaging::{SendMessageW, HTCAPTION, WM_NCLBUTTONDOWN}; + use windows::Win32::Foundation::*; + use windows::Win32::UI::Input::KeyboardAndMouse::*; + use windows::Win32::UI::WindowsAndMessaging::*; unsafe { let hwnd = window.window_handle(); - let _ = SendMessageW( - HWND(hwnd.0 as _), + + let mut pos = std::mem::zeroed(); + let _ = GetCursorPos(&mut pos); + + let points = POINTS { + x: pos.x as i16, + y: pos.y as i16, + }; + + let _ = ReleaseCapture(); + + let _ = PostMessageW( + Some(HWND(hwnd.0 as _)), WM_NCLBUTTONDOWN, - Some(windows::Win32::Foundation::WPARAM(HTCAPTION as usize)), - Some(windows::Win32::Foundation::LPARAM(0)), + WPARAM(HTCAPTION as usize), + LPARAM(&points as *const _ as isize), ); } } diff --git a/crates/tauri-runtime-cef/src/cef_impl/drag_window.rs b/crates/tauri-runtime-cef/src/cef_impl/drag_window.rs new file mode 100644 index 000000000000..3d273495bdda --- /dev/null +++ b/crates/tauri-runtime-cef/src/cef_impl/drag_window.rs @@ -0,0 +1,71 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +pub mod windows { + use cef::*; + use windows::core::{w, PCWSTR}; + use windows::Win32::Foundation::*; + use windows::Win32::UI::WindowsAndMessaging::*; + + /// Same as [WNDPROC] but without the Option wrapper. + type WindowProc = unsafe extern "system" fn(HWND, u32, WPARAM, LPARAM) -> LRESULT; + + const ORIGINAL_WND_PROP: PCWSTR = w!("TAURI_CEF_ORIGINAL_WND_PROC"); + + /// Subclasses the given window to handle draggable regions + /// by replacing its window procedure with `root_window_proc` + /// and storing the original procedure as a property to be called later. + pub fn subclass_window_for_dragging(window: &mut cef::Window) { + let hwnd = window.window_handle(); + let hwnd = HWND(hwnd.0 as _); + subclass_window(hwnd, root_window_proc); + } + + /// Subclasses a window by replacing its window procedure with the given `proc` + /// and storing the original procedure as a property for later use. + fn subclass_window(hwnd: HWND, proc: WindowProc) { + // If already subclassed, return early + let orginial_wnd_proc = unsafe { GetPropW(hwnd, ORIGINAL_WND_PROP) }; + if !orginial_wnd_proc.is_invalid() { + return; + } + + // Reset last error + unsafe { SetLastError(ERROR_SUCCESS) }; + + // Set the new window procedure and get the orginal one + let original_wnd_proc = unsafe { SetWindowLongPtrW(hwnd, GWLP_WNDPROC, proc as isize) }; + if original_wnd_proc == 0 && unsafe { GetLastError() } != ERROR_SUCCESS { + return; + } + + unsafe { + // Store the original window proc as a property for later use + let _ = SetPropW( + hwnd, + ORIGINAL_WND_PROP, + Some(HANDLE(original_wnd_proc as _)), + ); + } + } + + /// The root window procedure to handle WM_NCLBUTTONDOWN + /// by calling DefWindowProcW directly to allow dragging + /// and forwarding other messages to the original CEF window procedure. + unsafe extern "system" fn root_window_proc( + hwnd: HWND, + msg: u32, + wparam: WPARAM, + lparam: LPARAM, + ) -> LRESULT { + if msg == WM_NCLBUTTONDOWN { + return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }; + } + + // For other messages, call the original CEF window procedure + let original_wnd_proc = GetPropW(hwnd, ORIGINAL_WND_PROP); + let original_wnd_proc = std::mem::transmute::<_, WindowProc>(original_wnd_proc.0); + CallWindowProcW(Some(original_wnd_proc), hwnd, msg, wparam, lparam) + } +}