From e2038fca026a2343aff849643eff4229fe3871c9 Mon Sep 17 00:00:00 2001 From: Damyan Pepper Date: Sat, 20 Dec 2025 01:13:10 -0800 Subject: [PATCH] Replace winapi usage with windows The winapi crate is no longer maintained. This change ports everything over to use the more maintained windows crate. --- Cargo.toml | 23 +++- src/gl/win.rs | 159 +++++++++++++------------ src/win/cursor.rs | 10 +- src/win/drop_target.rs | 260 ++++++++++++++--------------------------- src/win/hook.rs | 64 +++++----- src/win/keyboard.rs | 128 +++++++++----------- src/win/window.rs | 254 +++++++++++++++++++++++----------------- 7 files changed, 427 insertions(+), 471 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d65e460..27060185 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,12 +23,31 @@ keyboard-types = { version = "0.6.1", default-features = false } raw-window-handle = "0.5" [target.'cfg(target_os="linux")'.dependencies] -x11rb = { version = "0.13.0", features = ["cursor", "resource_manager", "allow-unsafe-code"] } +x11rb = { version = "0.13.0", features = [ + "cursor", + "resource_manager", + "allow-unsafe-code", +] } x11 = { version = "2.21", features = ["xlib", "xlib_xcb"] } nix = "0.22.0" [target.'cfg(target_os="windows")'.dependencies] -winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi", "ole2", "oleidl", "shellapi", "winerror"] } +windows = { version = "0.62.2", features = [ + "Win32_Graphics_Gdi", + "Win32_Graphics_OpenGL", + "Win32_System_Com_StructuredStorage", + "Win32_System_Com", + "Win32_System_LibraryLoader", + "Win32_System_Ole", + "Win32_System_SystemServices", + "Win32_System_Threading", + "Win32_UI_Controls", + "Win32_UI_HiDpi", + "Win32_UI_Input_KeyboardAndMouse", + "Win32_UI_Shell", + "Win32_UI_WindowsAndMessaging", +] } +windows-core = { version = "0.62.2" } uuid = { version = "0.8", features = ["v4"], optional = true } [target.'cfg(target_os="macos")'.dependencies] diff --git a/src/gl/win.rs b/src/gl/win.rs index 097eb092..0be85a95 100644 --- a/src/gl/win.rs +++ b/src/gl/win.rs @@ -1,23 +1,32 @@ use std::ffi::{c_void, CString, OsStr}; use std::os::windows::ffi::OsStrExt; -use raw_window_handle::RawWindowHandle; - -use winapi::shared::minwindef::{HINSTANCE, HMODULE}; -use winapi::shared::ntdef::WCHAR; -use winapi::shared::windef::{HDC, HGLRC, HWND}; -use winapi::um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryA}; -use winapi::um::wingdi::{ - wglCreateContext, wglDeleteContext, wglGetProcAddress, wglMakeCurrent, ChoosePixelFormat, - DescribePixelFormat, SetPixelFormat, SwapBuffers, PFD_DOUBLEBUFFER, PFD_DRAW_TO_WINDOW, - PFD_MAIN_PLANE, PFD_SUPPORT_OPENGL, PFD_TYPE_RGBA, PIXELFORMATDESCRIPTOR, -}; -use winapi::um::winnt::IMAGE_DOS_HEADER; -use winapi::um::winuser::{ - CreateWindowExW, DefWindowProcW, DestroyWindow, GetDC, RegisterClassW, ReleaseDC, - UnregisterClassW, CS_OWNDC, CW_USEDEFAULT, WNDCLASSW, +use windows::{ + core::{s, PCSTR, PCWSTR}, + Win32::{ + Foundation::{FreeLibrary, HINSTANCE, HMODULE, HWND, LPARAM, LRESULT, WPARAM}, + Graphics::{ + Gdi::{GetDC, ReleaseDC, HDC}, + OpenGL::{ + wglCreateContext, wglDeleteContext, wglGetProcAddress, wglMakeCurrent, + ChoosePixelFormat, DescribePixelFormat, SetPixelFormat, SwapBuffers, HGLRC, + PFD_DOUBLEBUFFER, PFD_DRAW_TO_WINDOW, PFD_MAIN_PLANE, PFD_SUPPORT_OPENGL, + PFD_TYPE_RGBA, PIXELFORMATDESCRIPTOR, + }, + }, + System::{ + LibraryLoader::{GetProcAddress, LoadLibraryA}, + SystemServices::IMAGE_DOS_HEADER, + }, + UI::WindowsAndMessaging::{ + CreateWindowExW, DefWindowProcW, DestroyWindow, RegisterClassW, UnregisterClassW, + CS_OWNDC, CW_USEDEFAULT, WINDOW_EX_STYLE, WINDOW_STYLE, WNDCLASSW, + }, + }, }; +use raw_window_handle::RawWindowHandle; + use super::{GlConfig, GlError, Profile}; // See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt @@ -77,6 +86,7 @@ extern "C" { } impl GlContext { + #[allow(unused)] pub unsafe fn create(parent: &RawWindowHandle, config: GlConfig) -> Result { let handle = if let RawWindowHandle::Win32(handle) = parent { handle @@ -91,16 +101,24 @@ impl GlContext { // Create temporary window and context to load function pointers let class_name_str = format!("raw-gl-context-window-{}", uuid::Uuid::new_v4().to_simple()); - let mut class_name: Vec = OsStr::new(&class_name_str).encode_wide().collect(); + let mut class_name: Vec = OsStr::new(&class_name_str).encode_wide().collect(); class_name.push(0); - let hinstance = &__ImageBase as *const IMAGE_DOS_HEADER as HINSTANCE; + let pcw_class_name = PCWSTR(class_name.as_ptr()); + + let hinstance = HINSTANCE(&__ImageBase as *const IMAGE_DOS_HEADER as *mut c_void); + + extern "system" fn wndproc( + hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM, + ) -> LRESULT { + unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) } + } let wnd_class = WNDCLASSW { style: CS_OWNDC, - lpfnWndProc: Some(DefWindowProcW), + lpfnWndProc: Some(wndproc), hInstance: hinstance, - lpszClassName: class_name.as_ptr(), + lpszClassName: pcw_class_name, ..std::mem::zeroed() }; @@ -110,23 +128,24 @@ impl GlContext { } let hwnd_tmp = CreateWindowExW( - 0, - class as *const WCHAR, - [0].as_ptr(), - 0, + WINDOW_EX_STYLE(0), + pcw_class_name, + None, + WINDOW_STYLE(0), CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - std::ptr::null_mut(), - std::ptr::null_mut(), - hinstance, - std::ptr::null_mut(), + None, + None, + Some(hinstance), + None, ); - if hwnd_tmp.is_null() { + if hwnd_tmp.is_err() { return Err(GlError::CreationFailed(())); } + let hwnd_tmp = hwnd_tmp.ok(); let hdc_tmp = GetDC(hwnd_tmp); @@ -139,69 +158,49 @@ impl GlContext { cAlphaBits: 8, cDepthBits: 24, cStencilBits: 8, - iLayerType: PFD_MAIN_PLANE, + iLayerType: PFD_MAIN_PLANE.0 as u8, ..std::mem::zeroed() }; SetPixelFormat(hdc_tmp, ChoosePixelFormat(hdc_tmp, &pfd_tmp), &pfd_tmp); let hglrc_tmp = wglCreateContext(hdc_tmp); - if hglrc_tmp.is_null() { + if hglrc_tmp.is_err() { ReleaseDC(hwnd_tmp, hdc_tmp); - UnregisterClassW(class as *const WCHAR, hinstance); - DestroyWindow(hwnd_tmp); + UnregisterClassW(pcw_class_name, Some(hinstance)); + DestroyWindow(hwnd_tmp.unwrap()); return Err(GlError::CreationFailed(())); } + let hglrc_tmp = hglrc_tmp.unwrap(); wglMakeCurrent(hdc_tmp, hglrc_tmp); #[allow(non_snake_case)] let wglCreateContextAttribsARB: Option = { - let symbol = CString::new("wglCreateContextAttribsARB").unwrap(); - let addr = wglGetProcAddress(symbol.as_ptr()); - if !addr.is_null() { - #[allow(clippy::missing_transmute_annotations)] - Some(std::mem::transmute(addr)) - } else { - None - } + wglGetProcAddress(s!("wglCreateContextAttribsARB")) + .map(|addr| std::mem::transmute(addr)) }; #[allow(non_snake_case)] let wglChoosePixelFormatARB: Option = { - let symbol = CString::new("wglChoosePixelFormatARB").unwrap(); - let addr = wglGetProcAddress(symbol.as_ptr()); - if !addr.is_null() { - #[allow(clippy::missing_transmute_annotations)] - Some(std::mem::transmute(addr)) - } else { - None - } + wglGetProcAddress(s!("wglChoosePixelFormatARB")).map(|addr| std::mem::transmute(addr)) }; #[allow(non_snake_case)] - let wglSwapIntervalEXT: Option = { - let symbol = CString::new("wglSwapIntervalEXT").unwrap(); - let addr = wglGetProcAddress(symbol.as_ptr()); - if !addr.is_null() { - #[allow(clippy::missing_transmute_annotations)] - Some(std::mem::transmute(addr)) - } else { - None - } - }; + let wglSwapIntervalEXT: Option = + { wglGetProcAddress(s!("wglSwapIntervalEXT")).map(|addr| std::mem::transmute(addr)) }; - wglMakeCurrent(hdc_tmp, std::ptr::null_mut()); + wglMakeCurrent(hdc_tmp, HGLRC::default()); wglDeleteContext(hglrc_tmp); ReleaseDC(hwnd_tmp, hdc_tmp); - UnregisterClassW(class as *const WCHAR, hinstance); - DestroyWindow(hwnd_tmp); + UnregisterClassW(wnd_class.lpszClassName, Some(hinstance)); + DestroyWindow(hwnd_tmp.unwrap()); // Create actual context - let hwnd = handle.hwnd as HWND; + let hwnd = HWND(handle.hwnd); - let hdc = GetDC(hwnd); + let hdc = GetDC(Some(hwnd)); #[rustfmt::skip] let pixel_format_attribs = [ @@ -238,7 +237,7 @@ impl GlContext { hdc, pixel_format, std::mem::size_of::() as u32, - &mut pfd, + Some(&mut pfd), ); SetPixelFormat(hdc, pixel_format, &pfd); @@ -256,40 +255,45 @@ impl GlContext { ]; let hglrc = - wglCreateContextAttribsARB.unwrap()(hdc, std::ptr::null_mut(), ctx_attribs.as_ptr()); - if hglrc.is_null() { + wglCreateContextAttribsARB.unwrap()(hdc, HGLRC::default(), ctx_attribs.as_ptr()); + if hglrc.is_invalid() { return Err(GlError::CreationFailed(())); } - let gl_library_name = CString::new("opengl32.dll").unwrap(); - let gl_library = LoadLibraryA(gl_library_name.as_ptr()); + let gl_library = LoadLibraryA(s!("opengl32.dll")).unwrap_or_default(); wglMakeCurrent(hdc, hglrc); wglSwapIntervalEXT.unwrap()(config.vsync as i32); - wglMakeCurrent(hdc, std::ptr::null_mut()); + wglMakeCurrent(hdc, HGLRC::default()); Ok(GlContext { hwnd, hdc, hglrc, gl_library }) } + #[allow(unused)] pub unsafe fn make_current(&self) { wglMakeCurrent(self.hdc, self.hglrc); } + #[allow(unused)] pub unsafe fn make_not_current(&self) { - wglMakeCurrent(self.hdc, std::ptr::null_mut()); + wglMakeCurrent(self.hdc, HGLRC::default()); } pub fn get_proc_address(&self, symbol: &str) -> *const c_void { let symbol = CString::new(symbol).unwrap(); - let addr = unsafe { wglGetProcAddress(symbol.as_ptr()) as *const c_void }; - if !addr.is_null() { - addr - } else { - unsafe { GetProcAddress(self.gl_library, symbol.as_ptr()) as *const c_void } + unsafe { + wglGetProcAddress(PCSTR(symbol.as_ptr() as *const u8)) + .map(|p| p as *const c_void) + .or_else(|| { + GetProcAddress(self.gl_library, PCSTR(symbol.as_ptr() as *const u8)) + .map(|p| p as *const c_void) + }) + .unwrap_or_default() } } pub fn swap_buffers(&self) { + #[allow(unused)] unsafe { SwapBuffers(self.hdc); } @@ -298,10 +302,11 @@ impl GlContext { impl Drop for GlContext { fn drop(&mut self) { + #[allow(unused)] unsafe { - wglMakeCurrent(std::ptr::null_mut(), std::ptr::null_mut()); + wglMakeCurrent(HDC::default(), HGLRC::default()); wglDeleteContext(self.hglrc); - ReleaseDC(self.hwnd, self.hdc); + ReleaseDC(Some(self.hwnd), self.hdc); FreeLibrary(self.gl_library); } } diff --git a/src/win/cursor.rs b/src/win/cursor.rs index f9a04d3b..307cc513 100644 --- a/src/win/cursor.rs +++ b/src/win/cursor.rs @@ -1,20 +1,20 @@ use crate::MouseCursor; -use winapi::{ - shared::ntdef::LPCWSTR, - um::winuser::{ +use windows::{ + core::PCWSTR, + Win32::UI::WindowsAndMessaging::{ IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT, }, }; -pub fn cursor_to_lpcwstr(cursor: MouseCursor) -> LPCWSTR { +pub fn cursor_to_lpcwstr(cursor: MouseCursor) -> PCWSTR { match cursor { MouseCursor::Default => IDC_ARROW, MouseCursor::Hand => IDC_HAND, MouseCursor::HandGrabbing => IDC_SIZEALL, MouseCursor::Help => IDC_HELP, // an empty LPCWSTR results in the cursor being hidden - MouseCursor::Hidden => std::ptr::null(), + MouseCursor::Hidden => PCWSTR::null(), MouseCursor::Text => IDC_IBEAM, MouseCursor::VerticalText => IDC_IBEAM, diff --git a/src/win/drop_target.rs b/src/win/drop_target.rs index b9ba5807..ed4a2239 100644 --- a/src/win/drop_target.rs +++ b/src/win/drop_target.rs @@ -1,95 +1,49 @@ +use std::cell::{Cell, RefCell}; use std::ffi::OsString; -use std::mem::transmute; use std::os::windows::prelude::OsStringExt; use std::ptr::null_mut; -use std::rc::{Rc, Weak}; - -use winapi::shared::guiddef::{IsEqualIID, REFIID}; -use winapi::shared::minwindef::{DWORD, WPARAM}; -use winapi::shared::ntdef::{HRESULT, ULONG}; -use winapi::shared::windef::{POINT, POINTL}; -use winapi::shared::winerror::{E_NOINTERFACE, E_UNEXPECTED, S_OK}; -use winapi::shared::wtypes::DVASPECT_CONTENT; -use winapi::um::objidl::{IDataObject, FORMATETC, STGMEDIUM, TYMED_HGLOBAL}; -use winapi::um::oleidl::{ - IDropTarget, IDropTargetVtbl, DROPEFFECT_COPY, DROPEFFECT_LINK, DROPEFFECT_MOVE, - DROPEFFECT_NONE, DROPEFFECT_SCROLL, +use std::rc::Weak; + +use windows::{ + core::implement, + Win32::{ + Foundation::{E_UNEXPECTED, POINT, POINTL, WPARAM}, + Graphics::Gdi::ScreenToClient, + System::{ + Com::{IDataObject, DVASPECT_CONTENT, FORMATETC, TYMED_HGLOBAL}, + Ole::*, + SystemServices::MODIFIERKEYS_FLAGS, + }, + UI::Shell::{DragQueryFileW, HDROP}, + }, }; -use winapi::um::shellapi::{DragQueryFileW, HDROP}; -use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl}; -use winapi::um::winuser::{ScreenToClient, CF_HDROP}; -use winapi::Interface; +use windows_core::Ref; use crate::{DropData, DropEffect, Event, EventStatus, MouseEvent, PhyPoint, Point}; use super::WindowState; -// These function pointers have to be stored in a (const) variable before they can be transmuted -// Transmuting is needed because winapi has a bug where the pt parameter has an incorrect -// type `*const POINTL` -#[allow(non_snake_case)] -const DRAG_ENTER_PTR: unsafe extern "system" fn( - this: *mut IDropTarget, - pDataObj: *const IDataObject, - grfKeyState: DWORD, - pt: POINTL, - pdwEffect: *mut DWORD, -) -> HRESULT = DropTarget::drag_enter; -#[allow(non_snake_case)] -const DRAG_OVER_PTR: unsafe extern "system" fn( - this: *mut IDropTarget, - grfKeyState: DWORD, - pt: POINTL, - pdwEffect: *mut DWORD, -) -> HRESULT = DropTarget::drag_over; -#[allow(non_snake_case)] -const DROP_PTR: unsafe extern "system" fn( - this: *mut IDropTarget, - pDataObj: *const IDataObject, - grfKeyState: DWORD, - pt: POINTL, - pdwEffect: *mut DWORD, -) -> HRESULT = DropTarget::drop; - -#[allow(clippy::missing_transmute_annotations)] -const DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl { - parent: IUnknownVtbl { - QueryInterface: DropTarget::query_interface, - AddRef: DropTarget::add_ref, - Release: DropTarget::release, - }, - DragEnter: unsafe { transmute(DRAG_ENTER_PTR) }, - DragOver: unsafe { transmute(DRAG_OVER_PTR) }, - DragLeave: DropTarget::drag_leave, - Drop: unsafe { transmute(DROP_PTR) }, -}; - -#[repr(C)] +#[implement(IDropTarget)] pub(super) struct DropTarget { - base: IDropTarget, - window_state: Weak, // These are cached since DragOver and DragLeave callbacks don't provide them, // and handling drag move events gets awkward on the client end otherwise - drag_position: Point, - drop_data: DropData, + drag_position: Cell, + drop_data: RefCell, } impl DropTarget { pub(super) fn new(window_state: Weak) -> Self { Self { - base: IDropTarget { lpVtbl: &DROP_TARGET_VTBL }, - window_state, - - drag_position: Point::new(0.0, 0.0), - drop_data: DropData::None, + drag_position: Cell::new(Point::new(0.0, 0.0)), + drop_data: RefCell::new(DropData::None), } } #[allow(non_snake_case)] - fn on_event(&self, pdwEffect: Option<*mut DWORD>, event: MouseEvent) { + fn on_event(&self, pdwEffect: Option<*mut DROPEFFECT>, event: MouseEvent) { let Some(window_state) = self.window_state.upgrade() else { return; }; @@ -113,170 +67,136 @@ impl DropTarget { } } - fn parse_coordinates(&mut self, pt: POINTL) { + fn parse_coordinates(&self, pt: POINTL) { let Some(window_state) = self.window_state.upgrade() else { return; }; let mut pt = POINT { x: pt.x, y: pt.y }; - unsafe { ScreenToClient(window_state.hwnd, &mut pt as *mut POINT) }; + + #[allow(unused)] + unsafe { + ScreenToClient(window_state.hwnd, &mut pt) + }; let phy_point = PhyPoint::new(pt.x, pt.y); - self.drag_position = phy_point.to_logical(&window_state.window_info()); + self.drag_position.set(phy_point.to_logical(&window_state.window_info())); } - fn parse_drop_data(&mut self, data_object: &IDataObject) { + fn parse_drop_data(&self, data_object: &IDataObject) { let format = FORMATETC { - cfFormat: CF_HDROP as u16, + cfFormat: CF_HDROP.0, ptd: null_mut(), - dwAspect: DVASPECT_CONTENT, + dwAspect: DVASPECT_CONTENT.0, lindex: -1, - tymed: TYMED_HGLOBAL, + tymed: TYMED_HGLOBAL.0 as u32, }; - let mut medium = STGMEDIUM { tymed: 0, u: null_mut(), pUnkForRelease: null_mut() }; - unsafe { - let hresult = data_object.GetData(&format, &mut medium); - if hresult != S_OK { - self.drop_data = DropData::None; + let Ok(medium) = data_object.GetData(&format) else { + self.drop_data.replace(DropData::None); return; - } + }; - let hdrop = *(*medium.u).hGlobal() as HDROP; + let hdrop = HDROP(medium.u.hGlobal.0); - let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, null_mut(), 0); + let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, None); if item_count == 0 { - self.drop_data = DropData::None; + self.drop_data.replace(DropData::None); return; } let mut paths = Vec::with_capacity(item_count as usize); for i in 0..item_count { - let characters = DragQueryFileW(hdrop, i, null_mut(), 0); + let characters = DragQueryFileW(hdrop, i, None); let buffer_size = characters as usize + 1; let mut buffer = vec![0u16; buffer_size]; - DragQueryFileW(hdrop, i, buffer.as_mut_ptr().cast(), buffer_size as u32); + DragQueryFileW(hdrop, i, Some(buffer.as_mut_slice())); paths.push(OsString::from_wide(&buffer[..characters as usize]).into()) } - self.drop_data = DropData::Files(paths); - } - } - - #[allow(non_snake_case)] - unsafe extern "system" fn query_interface( - this: *mut IUnknown, riid: REFIID, ppvObject: *mut *mut winapi::ctypes::c_void, - ) -> HRESULT { - if IsEqualIID(&*riid, &IUnknown::uuidof()) || IsEqualIID(&*riid, &IDropTarget::uuidof()) { - Self::add_ref(this); - *ppvObject = this as *mut winapi::ctypes::c_void; - return S_OK; + self.drop_data.replace(DropData::Files(paths)); } - - E_NOINTERFACE - } - - unsafe extern "system" fn add_ref(this: *mut IUnknown) -> ULONG { - let arc = Rc::from_raw(this); - let result = Rc::strong_count(&arc) + 1; - let _ = Rc::into_raw(arc); - - Rc::increment_strong_count(this); - - result as ULONG - } - - unsafe extern "system" fn release(this: *mut IUnknown) -> ULONG { - let arc = Rc::from_raw(this); - let result = Rc::strong_count(&arc) - 1; - let _ = Rc::into_raw(arc); - - Rc::decrement_strong_count(this); - - result as ULONG } +} - #[allow(non_snake_case)] - unsafe extern "system" fn drag_enter( - this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, - pdwEffect: *mut DWORD, - ) -> HRESULT { - let drop_target = &mut *(this as *mut DropTarget); - let Some(window_state) = drop_target.window_state.upgrade() else { - return E_UNEXPECTED; +impl IDropTarget_Impl for DropTarget_Impl { + fn DragEnter( + &self, pdataobj: Ref, grfkeystate: MODIFIERKEYS_FLAGS, pt: &POINTL, + pdweffect: *mut DROPEFFECT, + ) -> windows_core::Result<()> { + let Some(window_state) = self.window_state.upgrade() else { + return Err(E_UNEXPECTED.into()); }; - let modifiers = - window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM); + let modifiers = window_state + .keyboard_state() + .get_modifiers_from_mouse_wparam(WPARAM(grfkeystate.0 as usize)); - drop_target.parse_coordinates(pt); - drop_target.parse_drop_data(&*pDataObj); + self.parse_coordinates(*pt); + self.parse_drop_data(pdataobj.unwrap()); let event = MouseEvent::DragEntered { - position: drop_target.drag_position, + position: self.drag_position.get(), modifiers, - data: drop_target.drop_data.clone(), + data: self.drop_data.borrow().clone(), }; - drop_target.on_event(Some(pdwEffect), event); - S_OK + self.on_event(Some(pdweffect), event); + Ok(()) } - #[allow(non_snake_case)] - unsafe extern "system" fn drag_over( - this: *mut IDropTarget, grfKeyState: DWORD, pt: POINTL, pdwEffect: *mut DWORD, - ) -> HRESULT { - let drop_target = &mut *(this as *mut DropTarget); - let Some(window_state) = drop_target.window_state.upgrade() else { - return E_UNEXPECTED; + fn DragOver( + &self, grfkeystate: MODIFIERKEYS_FLAGS, pt: &POINTL, pdweffect: *mut DROPEFFECT, + ) -> windows_core::Result<()> { + let Some(window_state) = self.window_state.upgrade() else { + return Err(E_UNEXPECTED.into()); }; - let modifiers = - window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM); + let modifiers = window_state + .keyboard_state() + .get_modifiers_from_mouse_wparam(WPARAM(grfkeystate.0 as usize)); - drop_target.parse_coordinates(pt); + self.parse_coordinates(*pt); let event = MouseEvent::DragMoved { - position: drop_target.drag_position, + position: self.drag_position.get(), modifiers, - data: drop_target.drop_data.clone(), + data: self.drop_data.borrow().clone(), }; - drop_target.on_event(Some(pdwEffect), event); - S_OK + self.on_event(Some(pdweffect), event); + Ok(()) } - unsafe extern "system" fn drag_leave(this: *mut IDropTarget) -> HRESULT { - let drop_target = &mut *(this as *mut DropTarget); - drop_target.on_event(None, MouseEvent::DragLeft); - S_OK + fn DragLeave(&self) -> windows_core::Result<()> { + self.on_event(None, MouseEvent::DragLeft); + Ok(()) } - #[allow(non_snake_case)] - unsafe extern "system" fn drop( - this: *mut IDropTarget, pDataObj: *const IDataObject, grfKeyState: DWORD, pt: POINTL, - pdwEffect: *mut DWORD, - ) -> HRESULT { - let drop_target = &mut *(this as *mut DropTarget); - let Some(window_state) = drop_target.window_state.upgrade() else { - return E_UNEXPECTED; + fn Drop( + &self, pdataobj: Ref, grfkeystate: MODIFIERKEYS_FLAGS, pt: &POINTL, + pdweffect: *mut DROPEFFECT, + ) -> windows_core::Result<()> { + let Some(window_state) = self.window_state.upgrade() else { + return Err(E_UNEXPECTED.into()); }; - let modifiers = - window_state.keyboard_state().get_modifiers_from_mouse_wparam(grfKeyState as WPARAM); + let modifiers = window_state + .keyboard_state() + .get_modifiers_from_mouse_wparam(WPARAM(grfkeystate.0 as usize)); - drop_target.parse_coordinates(pt); - drop_target.parse_drop_data(&*pDataObj); + self.parse_coordinates(*pt); + self.parse_drop_data(pdataobj.unwrap()); let event = MouseEvent::DragDropped { - position: drop_target.drag_position, + position: self.drag_position.get(), modifiers, - data: drop_target.drop_data.clone(), + data: self.drop_data.borrow().clone(), }; - drop_target.on_event(Some(pdwEffect), event); - S_OK + self.on_event(Some(pdweffect), event); + Ok(()) } } diff --git a/src/win/hook.rs b/src/win/hook.rs index 0f6a3e8c..8a962c8c 100644 --- a/src/win/hook.rs +++ b/src/win/hook.rs @@ -1,23 +1,16 @@ use std::{ collections::HashSet, - ffi::c_int, - ptr, + hash::Hash, sync::{LazyLock, RwLock}, }; -use winapi::{ - shared::{ - minwindef::{LPARAM, WPARAM}, - windef::{HHOOK, HWND, POINT}, - }, - um::{ - libloaderapi::GetModuleHandleW, - processthreadsapi::GetCurrentThreadId, - winuser::{ - CallNextHookEx, SetWindowsHookExW, UnhookWindowsHookEx, HC_ACTION, MSG, PM_REMOVE, - WH_GETMESSAGE, WM_CHAR, WM_KEYDOWN, WM_KEYUP, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, - WM_USER, - }, +use windows::Win32::{ + Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM}, + System::{LibraryLoader::GetModuleHandleW, Threading::GetCurrentThreadId}, + UI::WindowsAndMessaging::{ + CallNextHookEx, SetWindowsHookExW, UnhookWindowsHookEx, HC_ACTION, HHOOK, MSG, PM_REMOVE, + WH_GETMESSAGE, WM_CHAR, WM_KEYDOWN, WM_KEYUP, WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, + WM_USER, }, }; @@ -26,7 +19,7 @@ use crate::win::wnd_proc; // track all windows opened by this instance of baseview // we use an RwLock here since the vast majority of uses (event interceptions) // will only need to read from the HashSet -static HOOK_STATE: LazyLock> = LazyLock::new(|| RwLock::default()); +static HOOK_STATE: LazyLock> = LazyLock::new(RwLock::default); pub(crate) struct KeyboardHookHandle(HWNDWrapper); @@ -36,9 +29,15 @@ struct KeyboardHookState { open_windows: HashSet, } -#[derive(Hash, PartialEq, Eq, Clone, Copy)] +#[derive(PartialEq, Eq, Clone, Copy)] struct HWNDWrapper(HWND); +impl Hash for HWNDWrapper { + fn hash(&self, state: &mut H) { + (self.0 .0 as usize).hash(state); + } +} + // SAFETY: it's a pointer behind an RwLock. we'll live unsafe impl Send for KeyboardHookState {} unsafe impl Sync for KeyboardHookState {} @@ -71,12 +70,12 @@ pub(crate) fn init_keyboard_hook(hwnd: HWND) -> KeyboardHookHandle { SetWindowsHookExW( WH_GETMESSAGE, Some(keyboard_hook_callback), - GetModuleHandleW(ptr::null()), + GetModuleHandleW(None).ok().map(|h| HINSTANCE(h.0)), GetCurrentThreadId(), ) }; - state.hook = Some(new_hook); + state.hook = new_hook.ok(); KeyboardHookHandle(HWNDWrapper(hwnd)) } @@ -89,6 +88,7 @@ fn deinit_keyboard_hook(hwnd: HWNDWrapper) { if state.open_windows.is_empty() { if let Some(hhook) = state.hook { + #[allow(unused)] unsafe { UnhookWindowsHookEx(hhook); } @@ -99,23 +99,19 @@ fn deinit_keyboard_hook(hwnd: HWNDWrapper) { } unsafe extern "system" fn keyboard_hook_callback( - n_code: c_int, wparam: WPARAM, lparam: LPARAM, -) -> isize { - let msg = lparam as *mut MSG; - - if n_code == HC_ACTION && wparam == PM_REMOVE as usize && offer_message_to_baseview(msg) { - *msg = MSG { - hwnd: ptr::null_mut(), - message: WM_USER, - wParam: 0, - lParam: 0, - time: 0, - pt: POINT { x: 0, y: 0 }, - }; + n_code: i32, wparam: WPARAM, lparam: LPARAM, +) -> LRESULT { + let msg = lparam.0 as *mut MSG; + + if n_code == HC_ACTION as i32 + && wparam.0 == PM_REMOVE.0 as usize + && offer_message_to_baseview(msg) + { + *msg = MSG { message: WM_USER, ..Default::default() }; - 0 + LRESULT::default() } else { - CallNextHookEx(ptr::null_mut(), n_code, wparam, lparam) + CallNextHookEx(None, n_code, wparam, lparam) } } diff --git a/src/win/keyboard.rs b/src/win/keyboard.rs index ed696ccd..5ab7cfe7 100644 --- a/src/win/keyboard.rs +++ b/src/win/keyboard.rs @@ -19,35 +19,23 @@ use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; +use std::ffi::c_void; use std::mem; use std::ops::RangeInclusive; use keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers}; - -use winapi::shared::minwindef::{HKL, INT, LPARAM, UINT, WPARAM}; -use winapi::shared::ntdef::SHORT; -use winapi::shared::windef::HWND; -use winapi::um::winuser::{ - GetKeyState, GetKeyboardLayout, MapVirtualKeyExW, PeekMessageW, ToUnicodeEx, MAPVK_VK_TO_CHAR, - MAPVK_VSC_TO_VK_EX, MK_CONTROL, MK_SHIFT, PM_NOREMOVE, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN, - VK_BACK, VK_BROWSER_BACK, VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME, - VK_BROWSER_REFRESH, VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR, - VK_CONTROL, VK_CONVERT, VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF, - VK_ESCAPE, VK_EXECUTE, VK_EXSEL, VK_F1, VK_F10, VK_F11, VK_F12, VK_F2, VK_F3, VK_F4, VK_F5, - VK_F6, VK_F7, VK_F8, VK_F9, VK_FINAL, VK_HELP, VK_HOME, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI, - VK_LAUNCH_APP1, VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT, - VK_LMENU, VK_LSHIFT, VK_LWIN, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK, - VK_MEDIA_STOP, VK_MENU, VK_MODECHANGE, VK_MULTIPLY, VK_NEXT, VK_NONCONVERT, VK_NUMLOCK, - VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, - VK_NUMPAD8, VK_NUMPAD9, VK_OEM_ATTN, VK_OEM_CLEAR, VK_PAUSE, VK_PLAY, VK_PRINT, VK_PRIOR, - VK_PROCESSKEY, VK_RCONTROL, VK_RETURN, VK_RIGHT, VK_RMENU, VK_RSHIFT, VK_RWIN, VK_SCROLL, - VK_SELECT, VK_SHIFT, VK_SLEEP, VK_SNAPSHOT, VK_SUBTRACT, VK_TAB, VK_UP, VK_VOLUME_DOWN, - VK_VOLUME_MUTE, VK_VOLUME_UP, VK_ZOOM, WM_CHAR, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, - WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, +use windows::Win32::{ + Foundation::{HWND, LPARAM, WPARAM}, + System::SystemServices::{MK_CONTROL, MK_SHIFT}, + UI::{ + Input::KeyboardAndMouse::*, + WindowsAndMessaging::{ + PeekMessageW, PM_NOREMOVE, WM_CHAR, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, + WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, + }, + }, }; -const VK_ABNT_C2: INT = 0xc2; - /// A (non-extended) virtual key code. type VkCode = u8; @@ -84,25 +72,25 @@ const PRINTABLE_VKS: &[RangeInclusive] = &[ ]; /// Bits of lparam indicating scan code, including extended bit. -const SCAN_MASK: LPARAM = 0x1ff_0000; +const SCAN_MASK: isize = 0x1ff_0000; /// Determine whether there are more messages in the queue for this key event. /// /// When this function returns `false`, there is another message in the queue /// with a matching scan code, therefore it is reasonable to stash the data /// from this message and defer til later to actually produce the event. -unsafe fn is_last_message(hwnd: HWND, msg: UINT, lparam: LPARAM) -> bool { +unsafe fn is_last_message(hwnd: HWND, msg: u32, lparam: LPARAM) -> bool { let expected_msg = match msg { WM_KEYDOWN | WM_CHAR => WM_CHAR, WM_SYSKEYDOWN | WM_SYSCHAR => WM_SYSCHAR, _ => unreachable!(), }; let mut msg = mem::zeroed(); - let avail = PeekMessageW(&mut msg, hwnd, expected_msg, expected_msg, PM_NOREMOVE); - avail == 0 || msg.lParam & SCAN_MASK != lparam & SCAN_MASK + let avail = PeekMessageW(&mut msg, Some(hwnd), expected_msg, expected_msg, PM_NOREMOVE); + avail.as_bool() || msg.lParam.0 & SCAN_MASK != lparam.0 & SCAN_MASK } -const MODIFIER_MAP: &[(INT, Modifiers, SHORT)] = &[ +const MODIFIER_MAP: &[(VIRTUAL_KEY, Modifiers, u16)] = &[ (VK_MENU, Modifiers::ALT, 0x80), (VK_CAPITAL, Modifiers::CAPS_LOCK, 0x1), (VK_CONTROL, Modifiers::CONTROL, 0x80), @@ -259,7 +247,7 @@ fn scan_to_code(scan_code: u32) -> Code { } fn vk_to_key(vk: VkCode) -> Option { - Some(match vk as INT { + Some(match VIRTUAL_KEY(vk as u16) { VK_CANCEL => Key::Cancel, VK_BACK => Key::Backspace, VK_TAB => Key::Tab, @@ -365,7 +353,7 @@ fn code_unit_to_key(code_unit: u32) -> Key { /// /// This logic is based on NativeKey::GetKeyLocation from Mozilla. fn vk_to_location(vk: VkCode, is_extended: bool) -> Location { - match vk as INT { + match VIRTUAL_KEY(vk as u16) { VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => Location::Left, VK_RSHIFT | VK_RCONTROL | VK_RMENU | VK_RWIN => Location::Right, VK_RETURN if is_extended => Location::Numpad, @@ -437,19 +425,19 @@ impl KeyboardState { /// a valid `HKL` reference in the `WM_INPUTLANGCHANGE` message. Actual danger /// is likely low, though. pub(crate) unsafe fn process_message( - &mut self, hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM, + &mut self, hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM, ) -> Option { match msg { WM_KEYDOWN | WM_SYSKEYDOWN => { //println!("keydown wparam {:x} lparam {:x}", wparam, lparam); - let scan_code = ((lparam & SCAN_MASK) >> 16) as u32; - let vk = self.refine_vk(wparam as u8, scan_code); + let scan_code = ((lparam.0 & SCAN_MASK) >> 16) as u32; + let vk = self.refine_vk(wparam.0 as u8, scan_code); if is_last_message(hwnd, msg, lparam) { let modifiers = self.get_modifiers(); let code = scan_to_code(scan_code); let key = vk_to_key(vk).unwrap_or_else(|| self.get_base_key(vk, modifiers)); - let repeat = (lparam & 0x4000_0000) != 0; - let is_extended = (lparam & 0x100_0000) != 0; + let repeat = (lparam.0 & 0x4000_0000) != 0; + let is_extended = (lparam.0 & 0x100_0000) != 0; let location = vk_to_location(vk, is_extended); let state = KeyState::Down; let event = KeyboardEvent { @@ -468,13 +456,13 @@ impl KeyboardState { } } WM_KEYUP | WM_SYSKEYUP => { - let scan_code = ((lparam & SCAN_MASK) >> 16) as u32; - let vk = self.refine_vk(wparam as u8, scan_code); + let scan_code = ((lparam.0 & SCAN_MASK) >> 16) as u32; + let vk = self.refine_vk(wparam.0 as u8, scan_code); let modifiers = self.get_modifiers(); let code = scan_to_code(scan_code); let key = vk_to_key(vk).unwrap_or_else(|| self.get_base_key(vk, modifiers)); let repeat = false; - let is_extended = (lparam & 0x100_0000) != 0; + let is_extended = (lparam.0 & 0x100_0000) != 0; let location = vk_to_location(vk, is_extended); let state = KeyState::Up; let event = KeyboardEvent { @@ -493,13 +481,13 @@ impl KeyboardState { if is_last_message(hwnd, msg, lparam) { let stash_vk = self.stash_vk.take(); let modifiers = self.get_modifiers(); - let scan_code = ((lparam & SCAN_MASK) >> 16) as u32; + let scan_code = ((lparam.0 & SCAN_MASK) >> 16) as u32; let vk = self.refine_vk(stash_vk.unwrap_or(0), scan_code); let code = scan_to_code(scan_code); - let key = if self.stash_utf16.is_empty() && wparam < 0x20 { + let key = if self.stash_utf16.is_empty() && wparam.0 < 0x20 { vk_to_key(vk).unwrap_or_else(|| self.get_base_key(vk, modifiers)) } else { - self.stash_utf16.push(wparam as u16); + self.stash_utf16.push(wparam.0 as u16); if let Ok(s) = String::from_utf16(&self.stash_utf16) { Key::Character(s) } else { @@ -507,8 +495,8 @@ impl KeyboardState { } }; self.stash_utf16.clear(); - let repeat = (lparam & 0x4000_0000) != 0; - let is_extended = (lparam & 0x100_0000) != 0; + let repeat = (lparam.0 & 0x4000_0000) != 0; + let is_extended = (lparam.0 & 0x100_0000) != 0; let location = vk_to_location(vk, is_extended); let state = KeyState::Down; let event = KeyboardEvent { @@ -522,12 +510,12 @@ impl KeyboardState { }; Some(event) } else { - self.stash_utf16.push(wparam as u16); + self.stash_utf16.push(wparam.0 as u16); None } } WM_INPUTLANGCHANGE => { - self.hkl = lparam as HKL; + self.hkl = HKL(lparam.0 as *mut c_void); self.load_keyboard_layout(); None } @@ -550,11 +538,11 @@ impl KeyboardState { unsafe { let mut modifiers = Modifiers::empty(); for &(vk, modifier, mask) in MODIFIER_MAP { - if GetKeyState(vk) & mask != 0 { + if GetKeyState(vk.0.into()) as u16 & mask != 0 { modifiers |= modifier; } } - if self.has_altgr && GetKeyState(VK_RMENU) & 0x80 != 0 { + if self.has_altgr && GetKeyState(VK_RMENU.0.into()) & 0x80 != 0 { modifiers |= Modifiers::ALT_GRAPH; modifiers &= !(Modifiers::CONTROL | Modifiers::ALT); } @@ -569,16 +557,16 @@ impl KeyboardState { let mut modifiers = Modifiers::empty(); for &(vk, modifier, mask) in MODIFIER_MAP { let modifier_active = match modifier { - Modifiers::CONTROL => wparam & MK_CONTROL != 0, - Modifiers::SHIFT => wparam & MK_SHIFT != 0, - _ => GetKeyState(vk) & mask != 0, + Modifiers::CONTROL => wparam.0 & MK_CONTROL.0 as usize != 0, + Modifiers::SHIFT => wparam.0 & MK_SHIFT.0 as usize != 0, + _ => GetKeyState(vk.0.into()) as u16 & mask != 0, }; if modifier_active { modifiers |= modifier; } } - if self.has_altgr && GetKeyState(VK_RMENU) & 0x80 != 0 { + if self.has_altgr && GetKeyState(VK_RMENU.0.into()) & 0x80 != 0 { modifiers |= Modifiers::ALT_GRAPH; modifiers &= !(Modifiers::CONTROL | Modifiers::ALT); } @@ -606,22 +594,15 @@ impl KeyboardState { for shift_state in 0..N_SHIFT_STATE { let has_shift = shift_state & SHIFT_STATE_SHIFT != 0; let has_altgr = shift_state & SHIFT_STATE_ALTGR != 0; - key_state[VK_SHIFT as usize] = if has_shift { 0x80 } else { 0 }; - key_state[VK_CONTROL as usize] = if has_altgr { 0x80 } else { 0 }; - key_state[VK_LCONTROL as usize] = if has_altgr { 0x80 } else { 0 }; - key_state[VK_MENU as usize] = if has_altgr { 0x80 } else { 0 }; - key_state[VK_RMENU as usize] = if has_altgr { 0x80 } else { 0 }; + key_state[VK_SHIFT.0 as usize] = if has_shift { 0x80 } else { 0 }; + key_state[VK_CONTROL.0 as usize] = if has_altgr { 0x80 } else { 0 }; + key_state[VK_LCONTROL.0 as usize] = if has_altgr { 0x80 } else { 0 }; + key_state[VK_MENU.0 as usize] = if has_altgr { 0x80 } else { 0 }; + key_state[VK_RMENU.0 as usize] = if has_altgr { 0x80 } else { 0 }; #[allow(clippy::iter_overeager_cloned)] for vk in PRINTABLE_VKS.iter().cloned().flatten() { - let ret = ToUnicodeEx( - vk as UINT, - 0, - key_state.as_ptr(), - uni_chars.as_mut_ptr(), - uni_chars.len() as _, - 0, - self.hkl, - ); + let ret = + ToUnicodeEx(vk as u32, 0, &key_state, &mut uni_chars, 0, Some(self.hkl)); match ret.cmp(&0) { Ordering::Greater => { let utf16_slice = &uni_chars[..ret as usize]; @@ -643,13 +624,12 @@ impl KeyboardState { // It's a dead key, press it again to reset the state. self.dead_keys.insert((vk, shift_state)); let _ = ToUnicodeEx( - vk as UINT, + vk as u32, 0, - key_state.as_ptr(), - uni_chars.as_mut_ptr(), - uni_chars.len() as _, + &key_state, + &mut uni_chars, 0, - self.hkl, + Some(self.hkl), ); } _ => (), @@ -683,7 +663,7 @@ impl KeyboardState { /// /// Bit 31 is set if the mapping is to a dead key. The bottom bits contain the code unit. fn map_vk(&self, vk: VkCode) -> u32 { - unsafe { MapVirtualKeyExW(vk as _, MAPVK_VK_TO_CHAR, self.hkl) } + unsafe { MapVirtualKeyExW(vk as _, MAPVK_VK_TO_CHAR, Some(self.hkl)) } } /// Refine a virtual key code to distinguish left and right. @@ -691,12 +671,12 @@ impl KeyboardState { /// This only does the mapping if the original code is ambiguous, as otherwise the /// virtual key code reported in `wparam` is more reliable. fn refine_vk(&self, vk: VkCode, mut scan_code: u32) -> VkCode { - match vk as INT { - 0 | VK_SHIFT | VK_CONTROL | VK_MENU => { + match VIRTUAL_KEY(vk as u16) { + VIRTUAL_KEY(0) | VK_SHIFT | VK_CONTROL | VK_MENU => { if scan_code >= 0x100 { scan_code += 0xE000 - 0x100; } - unsafe { MapVirtualKeyExW(scan_code, MAPVK_VSC_TO_VK_EX, self.hkl) as u8 } + unsafe { MapVirtualKeyExW(scan_code, MAPVK_VSC_TO_VK_EX, Some(self.hkl)) as u8 } } _ => vk, } diff --git a/src/win/window.rs b/src/win/window.rs index 51619d00..9d65cc15 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -1,29 +1,39 @@ -use winapi::shared::guiddef::GUID; -use winapi::shared::minwindef::{ATOM, FALSE, LOWORD, LPARAM, LRESULT, UINT, WPARAM}; -use winapi::shared::windef::{HWND, RECT}; -use winapi::um::combaseapi::CoCreateGuid; -use winapi::um::ole2::{OleInitialize, RegisterDragDrop, RevokeDragDrop}; -use winapi::um::oleidl::LPDROPTARGET; -use winapi::um::winuser::{ - AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, - GetDpiForWindow, GetFocus, GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, - RegisterClassW, ReleaseCapture, SetCapture, SetCursor, SetFocus, SetProcessDpiAwarenessContext, - SetTimer, SetWindowLongPtrW, SetWindowPos, TrackMouseEvent, TranslateMessage, UnregisterClassW, - CS_OWNDC, GET_XBUTTON_WPARAM, GWLP_USERDATA, HTCLIENT, IDC_ARROW, MSG, SWP_NOMOVE, - SWP_NOZORDER, TRACKMOUSEEVENT, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED, - WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, - WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSELEAVE, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY, - WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN, - WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, - WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, - XBUTTON1, XBUTTON2, +use windows::Win32::{ + Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM}, + System::{ + Com::CoCreateGuid, + Ole::{OleInitialize, RegisterDragDrop, RevokeDragDrop}, + }, + UI::{ + Controls::{HOVER_DEFAULT, WM_MOUSELEAVE}, + HiDpi::{ + GetDpiForWindow, SetProcessDpiAwarenessContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, + }, + Input::KeyboardAndMouse::{ + GetFocus, ReleaseCapture, SetCapture, SetFocus, TrackMouseEvent, TME_LEAVE, + TRACKMOUSEEVENT, + }, + WindowsAndMessaging::{ + AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, + GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, RegisterClassW, SetCursor, + SetTimer, SetWindowLongPtrW, SetWindowPos, TranslateMessage, UnregisterClassW, + CS_OWNDC, GWLP_USERDATA, HTCLIENT, IDC_ARROW, MSG, SWP_NOMOVE, SWP_NOZORDER, + WHEEL_DELTA, WINDOW_EX_STYLE, WINDOW_STYLE, WM_CHAR, WM_CLOSE, WM_CREATE, + WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, + WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, + WM_NCDESTROY, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SHOWWINDOW, WM_SIZE, + WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, + WM_XBUTTONUP, WNDCLASSW, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, + WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, + }, + }, }; +use windows_core::{ComObject, PCWSTR}; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::VecDeque; -use std::ffi::{c_void, OsStr}; +use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; -use std::ptr::null_mut; use std::rc::Rc; use raw_window_handle::{ @@ -31,7 +41,7 @@ use raw_window_handle::{ WindowsDisplayHandle, }; -const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1; +const BV_WINDOW_MUST_CLOSE: u32 = WM_USER + 1; use crate::win::hook::{self, KeyboardHookHandle}; use crate::{ @@ -47,24 +57,33 @@ use super::keyboard::KeyboardState; use crate::gl::GlContext; unsafe fn generate_guid() -> String { - let mut guid: GUID = std::mem::zeroed(); - CoCreateGuid(&mut guid); + let guid = CoCreateGuid().ok().unwrap(); format!( "{:0X}-{:0X}-{:0X}-{:0X}{:0X}-{:0X}{:0X}{:0X}{:0X}{:0X}{:0X}\0", - guid.Data1, - guid.Data2, - guid.Data3, - guid.Data4[0], - guid.Data4[1], - guid.Data4[2], - guid.Data4[3], - guid.Data4[4], - guid.Data4[5], - guid.Data4[6], - guid.Data4[7] + guid.data1, + guid.data2, + guid.data3, + guid.data4[0], + guid.data4[1], + guid.data4[2], + guid.data4[3], + guid.data4[4], + guid.data4[5], + guid.data4[6], + guid.data4[7] ) } +#[allow(non_snake_case)] +fn HIWORD(wparam: WPARAM) -> u16 { + ((wparam.0 >> 16) & 0xffff) as u16 +} + +#[allow(non_snake_case)] +fn LOWORD(lparam: LPARAM) -> u16 { + (lparam.0 & 0xffff) as u16 +} + const WIN_FRAME_TIMER: usize = 4242; pub struct WindowHandle { @@ -74,9 +93,15 @@ pub struct WindowHandle { impl WindowHandle { pub fn close(&mut self) { - if let Some(hwnd) = self.hwnd.take() { + if self.hwnd.is_some() { + #[allow(unused)] unsafe { - PostMessageW(hwnd, BV_WINDOW_MUST_CLOSE, 0, 0); + PostMessageW( + self.hwnd.take(), + BV_WINDOW_MUST_CLOSE, + WPARAM::default(), + LPARAM::default(), + ); } } } @@ -90,7 +115,7 @@ unsafe impl HasRawWindowHandle for WindowHandle { fn raw_window_handle(&self) -> RawWindowHandle { if let Some(hwnd) = self.hwnd { let mut handle = Win32WindowHandle::empty(); - handle.hwnd = hwnd as *mut c_void; + handle.hwnd = hwnd.0; RawWindowHandle::Win32(handle) } else { @@ -119,12 +144,13 @@ impl Drop for ParentHandle { } } +#[allow(unused)] pub(crate) unsafe extern "system" fn wnd_proc( - hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM, + hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM, ) -> LRESULT { if msg == WM_CREATE { - PostMessageW(hwnd, WM_SHOWWINDOW, 0, 0); - return 0; + PostMessageW(Some(hwnd), WM_SHOWWINDOW, WPARAM::default(), LPARAM::default()); + return LRESULT(0); } let window_state_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut WindowState; @@ -166,8 +192,9 @@ pub(crate) unsafe extern "system" fn wnd_proc( /// Our custom `wnd_proc` handler. If the result contains a value, then this is returned after /// handling any deferred tasks. otherwise the default window procedure is invoked. +#[allow(unused)] unsafe fn wnd_proc_inner( - hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM, window_state: &WindowState, + hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM, window_state: &WindowState, ) -> Option { match msg { WM_MOUSEMOVE => { @@ -179,9 +206,9 @@ unsafe fn wnd_proc_inner( // When the mouse leaves it results in a `WM_MOUSELEAVE` event. let mut track_mouse = TRACKMOUSEEVENT { cbSize: std::mem::size_of::() as u32, - dwFlags: winapi::um::winuser::TME_LEAVE, + dwFlags: TME_LEAVE, hwndTrack: hwnd, - dwHoverTime: winapi::um::winuser::HOVER_DEFAULT, + dwHoverTime: HOVER_DEFAULT, }; // Couldn't find a good way to track whether the mouse enters, // but if `WM_MOUSEMOVE` happens, the mouse must have entered. @@ -197,8 +224,8 @@ unsafe fn wnd_proc_inner( .on_event(&mut window, enter_event); } - let x = (lparam & 0xFFFF) as i16 as i32; - let y = ((lparam >> 16) & 0xFFFF) as i16 as i32; + let x = (lparam.0 & 0xFFFF) as i16 as i32; + let y = ((lparam.0 >> 16) & 0xFFFF) as i16 as i32; let physical_pos = PhyPoint { x, y }; let logical_pos = physical_pos.to_logical(&window_state.window_info.borrow()); @@ -210,7 +237,7 @@ unsafe fn wnd_proc_inner( .get_modifiers_from_mouse_wparam(wparam), }); window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, move_event); - Some(0) + Some(LRESULT(0)) } WM_MOUSELEAVE => { @@ -219,12 +246,12 @@ unsafe fn wnd_proc_inner( window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event); *window_state.mouse_was_outside_window.borrow_mut() = true; - Some(0) + Some(LRESULT(0)) } WM_MOUSEWHEEL | WM_MOUSEHWHEEL => { let mut window = crate::Window::new(window_state.create_window()); - let value = (wparam >> 16) as i16; + let value = (wparam.0 >> 16) as i16; let value = value as i32; let value = value as f32 / WHEEL_DELTA as f32; @@ -242,7 +269,7 @@ unsafe fn wnd_proc_inner( window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event); - Some(0) + Some(LRESULT(0)) } WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => { @@ -250,6 +277,14 @@ unsafe fn wnd_proc_inner( let mut mouse_button_counter = window_state.mouse_button_counter.get(); + #[allow(non_snake_case)] + fn GET_XBUTTON_WPARAM(wparam: WPARAM) -> u16 { + HIWORD(wparam) + } + + const XBUTTON1: u16 = 0x1; + const XBUTTON2: u16 = 0x2; + let button = match msg { WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left), WM_MBUTTONDOWN | WM_MBUTTONUP => Some(MouseButton::Middle), @@ -311,11 +346,11 @@ unsafe fn wnd_proc_inner( WM_TIMER => { let mut window = crate::Window::new(window_state.create_window()); - if wparam == WIN_FRAME_TIMER { + if wparam.0 == WIN_FRAME_TIMER { window_state.handler.borrow_mut().as_mut().unwrap().on_frame(&mut window); } - Some(0) + Some(LRESULT(0)) } WM_CLOSE => { // Make sure to release the borrow before the DefWindowProc call @@ -351,7 +386,7 @@ unsafe fn wnd_proc_inner( } if msg != WM_SYSKEYDOWN { - Some(0) + Some(LRESULT(0)) } else { None } @@ -359,8 +394,8 @@ unsafe fn wnd_proc_inner( WM_SIZE => { let mut window = crate::Window::new(window_state.create_window()); - let width = (lparam & 0xFFFF) as u16 as u32; - let height = ((lparam >> 16) & 0xFFFF) as u16 as u32; + let width = (lparam.0 & 0xFFFF) as u16 as u32; + let height = ((lparam.0 >> 16) & 0xFFFF) as u16 as u32; let new_window_info = { let mut window_info = window_state.window_info.borrow_mut(); @@ -390,7 +425,7 @@ unsafe fn wnd_proc_inner( // To avoid weirdness with the realtime borrow checker. let new_rect = { if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy { - let dpi = (wparam & 0xFFFF) as u16 as u32; + let dpi = (wparam.0 & 0xFFFF) as u16 as u32; let scale_factor = dpi as f64 / 96.0; let mut window_info = window_state.window_info.borrow_mut(); @@ -414,13 +449,13 @@ unsafe fn wnd_proc_inner( if let Some((mut new_rect, dw_style)) = new_rect { // Convert this desired "client rectangle" size to the actual "window rectangle" // size (Because of course you have to do that). - AdjustWindowRectEx(&mut new_rect, dw_style, 0, 0); + AdjustWindowRectEx(&mut new_rect, dw_style, false, WINDOW_EX_STYLE::default()); // Windows makes us resize the window manually. This will trigger another `WM_SIZE` event, // which we can then send the user the new scale factor. SetWindowPos( hwnd, - hwnd, + Some(hwnd), new_rect.left, new_rect.top, new_rect.right - new_rect.left, @@ -434,16 +469,13 @@ unsafe fn wnd_proc_inner( // If WM_SETCURSOR returns `None`, WM_SETCURSOR continues to get handled by the outer window(s), // If it returns `Some(1)`, the current window decides what the cursor is WM_SETCURSOR => { - let low_word = LOWORD(lparam as u32) as isize; - let mouse_in_window = low_word == HTCLIENT; + let low_word = LOWORD(lparam) as isize; + let mouse_in_window = low_word == HTCLIENT as isize; if mouse_in_window { // Here we need to set the cursor back to what the state says, since it can have changed when outside the window - let cursor = - LoadCursorW(null_mut(), cursor_to_lpcwstr(window_state.cursor_icon.get())); - unsafe { - SetCursor(cursor); - } - Some(1) + let cursor = LoadCursorW(None, cursor_to_lpcwstr(window_state.cursor_icon.get())); + SetCursor(cursor.ok()); + Some(LRESULT(1)) } else { // Cursor is being changed by some other window, e.g. when having mouse on the borders to resize it None @@ -453,12 +485,15 @@ unsafe fn wnd_proc_inner( // state BV_WINDOW_MUST_CLOSE => { DestroyWindow(hwnd); - Some(0) + Some(LRESULT(0)) } _ => None, } } +#[allow(clippy::upper_case_acronyms)] +type ATOM = u16; + unsafe fn register_wnd_class() -> ATOM { // We generate a unique name for the new window class to prevent name collisions let class_name_str = format!("Baseview-{}", generate_guid()); @@ -468,21 +503,17 @@ unsafe fn register_wnd_class() -> ATOM { let wnd_class = WNDCLASSW { style: CS_OWNDC, lpfnWndProc: Some(wnd_proc), - hInstance: null_mut(), - lpszClassName: class_name.as_ptr(), - cbClsExtra: 0, - cbWndExtra: 0, - hIcon: null_mut(), - hCursor: LoadCursorW(null_mut(), IDC_ARROW), - hbrBackground: null_mut(), - lpszMenuName: null_mut(), + lpszClassName: PCWSTR(class_name.as_ptr()), + hCursor: LoadCursorW(None, IDC_ARROW).unwrap(), + ..Default::default() }; RegisterClassW(&wnd_class) } +#[allow(unused)] unsafe fn unregister_wnd_class(wnd_class: ATOM) { - UnregisterClassW(wnd_class as _, null_mut()); + UnregisterClassW(PCWSTR(wnd_class as _), None); } /// All data associated with the window. This uses internal mutability so the outer struct doesn't @@ -504,9 +535,9 @@ pub(super) struct WindowState { cursor_icon: Cell, // Initialized late so the `Window` can hold a reference to this `WindowState` handler: RefCell>>, - _drop_target: RefCell>>, + _drop_target: RefCell>>, scale_policy: WindowScalePolicy, - dw_style: u32, + dw_style: WINDOW_STYLE, // handle to the win32 keyboard hook // we don't need to read from this, just carry it around so the Drop impl can run @@ -558,11 +589,12 @@ impl WindowState { right: window_info.physical_size().width as i32, bottom: window_info.physical_size().height as i32, }; + #[allow(unused)] unsafe { - AdjustWindowRectEx(&mut rect, self.dw_style, 0, 0); + AdjustWindowRectEx(&mut rect, self.dw_style, false, WINDOW_EX_STYLE::default()); SetWindowPos( self.hwnd, - self.hwnd, + Some(self.hwnd), 0, 0, rect.right - rect.left, @@ -597,11 +629,11 @@ impl Window<'_> { B: Send + 'static, { let parent = match parent.raw_window_handle() { - RawWindowHandle::Win32(h) => h.hwnd as HWND, + RawWindowHandle::Win32(h) => HWND(h.hwnd), h => panic!("unsupported parent handle {:?}", h), }; - let (window_handle, _) = Self::open(true, parent, options, build); + let (window_handle, _) = Self::open(true, Some(parent), options, build); window_handle } @@ -612,15 +644,16 @@ impl Window<'_> { B: FnOnce(&mut crate::Window) -> H, B: Send + 'static, { - let (_, hwnd) = Self::open(false, null_mut(), options, build); + let (_, hwnd) = Self::open(false, None, options, build); unsafe { let mut msg: MSG = std::mem::zeroed(); + #[allow(unused)] loop { - let status = GetMessageW(&mut msg, hwnd, 0, 0); + let status = GetMessageW(&mut msg, Some(hwnd), 0, 0); - if status == -1 { + if status.0 == -1 { break; } @@ -631,13 +664,14 @@ impl Window<'_> { } fn open( - parented: bool, parent: HWND, options: WindowOpenOptions, build: B, + parented: bool, parent: Option, options: WindowOpenOptions, build: B, ) -> (WindowHandle, HWND) where H: WindowHandler + 'static, B: FnOnce(&mut crate::Window) -> H, B: Send + 'static, { + #[allow(unused)] unsafe { let mut title: Vec = OsStr::new(&options.title[..]).encode_wide().collect(); title.push(0); @@ -673,23 +707,24 @@ impl Window<'_> { }; if !parented { - AdjustWindowRectEx(&mut rect, flags, FALSE, 0); + AdjustWindowRectEx(&mut rect, flags, false, WINDOW_EX_STYLE::default()); } let hwnd = CreateWindowExW( - 0, - window_class as _, - title.as_ptr(), + WINDOW_EX_STYLE::default(), + PCWSTR(window_class as _), + PCWSTR(title.as_ptr()), flags, 0, 0, rect.right - rect.left, rect.bottom - rect.top, - parent as *mut _, - null_mut(), - null_mut(), - null_mut(), - ); + parent, + None, + None, + None, + ) + .unwrap(); // todo: manage error ^ let kb_hook = hook::init_keyboard_hook(hwnd); @@ -697,7 +732,7 @@ impl Window<'_> { #[cfg(feature = "opengl")] let gl_context: Option = options.gl_config.map(|gl_config| { let mut handle = Win32WindowHandle::empty(); - handle.hwnd = hwnd as *mut c_void; + handle.hwnd = hwnd.0; let handle = RawWindowHandle::Win32(handle); GlContext::create(&handle, gl_config).expect("Could not create OpenGL context") @@ -738,9 +773,7 @@ impl Window<'_> { *window_state.handler.borrow_mut() = Some(Box::new(handler)); // Only works on Windows 10 unfortunately. - SetProcessDpiAwarenessContext( - winapi::shared::windef::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, - ); + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); // Now we can get the actual dpi of the window. let new_rect = if let WindowScalePolicy::SystemScaleFactor = options.scale { @@ -767,25 +800,26 @@ impl Window<'_> { None }; - let drop_target = Rc::new(DropTarget::new(Rc::downgrade(&window_state))); + let drop_target = ComObject::new(DropTarget::new(Rc::downgrade(&window_state))); *window_state._drop_target.borrow_mut() = Some(drop_target.clone()); - OleInitialize(null_mut()); - RegisterDragDrop(hwnd, Rc::as_ptr(&drop_target) as LPDROPTARGET); + OleInitialize(None); + + RegisterDragDrop(hwnd, drop_target.as_interface()); SetWindowLongPtrW(hwnd, GWLP_USERDATA, Rc::into_raw(window_state) as *const _ as _); - SetTimer(hwnd, WIN_FRAME_TIMER, 15, None); + SetTimer(Some(hwnd), WIN_FRAME_TIMER, 15, None); if let Some(mut new_rect) = new_rect { // Convert this desired"client rectangle" size to the actual "window rectangle" // size (Because of course you have to do that). - AdjustWindowRectEx(&mut new_rect, flags, 0, 0); + AdjustWindowRectEx(&mut new_rect, flags, false, WINDOW_EX_STYLE::default()); // Windows makes us resize the window manually. This will trigger another `WM_SIZE` event, // which we can then send the user the new scale factor. SetWindowPos( hwnd, - hwnd, + Some(hwnd), new_rect.left, new_rect.top, new_rect.right - new_rect.left, @@ -799,8 +833,9 @@ impl Window<'_> { } pub fn close(&mut self) { + #[allow(unused)] unsafe { - PostMessageW(self.state.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0); + PostMessageW(Some(self.state.hwnd), BV_WINDOW_MUST_CLOSE, WPARAM(0), LPARAM(0)); } } @@ -810,8 +845,9 @@ impl Window<'_> { } pub fn focus(&mut self) { + #[allow(unused)] unsafe { - SetFocus(self.state.hwnd); + SetFocus(Some(self.state.hwnd)); } } @@ -825,7 +861,7 @@ impl Window<'_> { pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) { self.state.cursor_icon.set(mouse_cursor); unsafe { - let cursor = LoadCursorW(null_mut(), cursor_to_lpcwstr(mouse_cursor)); + let cursor = LoadCursorW(None, cursor_to_lpcwstr(mouse_cursor)).ok(); SetCursor(cursor); } } @@ -839,7 +875,7 @@ impl Window<'_> { unsafe impl HasRawWindowHandle for Window<'_> { fn raw_window_handle(&self) -> RawWindowHandle { let mut handle = Win32WindowHandle::empty(); - handle.hwnd = self.state.hwnd as *mut c_void; + handle.hwnd = self.state.hwnd.0; RawWindowHandle::Win32(handle) }