windows_api_utils/
messages.rs

1//! Windows message parameter handling.
2//!
3//! This module provides types and utilities for working with
4//! Windows message parameters (WPARAM, LPARAM) and common messages.
5//!
6//! Requires the `messages` feature to be enabled.
7
8use core::fmt;
9
10/// Windows message parameter - 完全匹配 windows crate 的 WPARAM
11#[repr(transparent)]
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct WPARAM(pub usize);
15
16/// Windows message parameter - 完全匹配 windows crate 的 LPARAM
17#[repr(transparent)]
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct LPARAM(pub isize);
21
22/// Message result type - 完全匹配 windows crate 的 LRESULT
23#[repr(transparent)]
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct LRESULT(pub isize);
27
28/// Newtype wrapper for WPARAM with utility methods.
29#[repr(transparent)]
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct WParam(pub WPARAM);
33
34impl WParam {
35    /// Creates a new WPARAM wrapper.
36    pub const fn new(value: usize) -> Self {
37        Self(WPARAM(value))
38    }
39
40    /// Returns the raw value.
41    pub const fn raw(&self) -> WPARAM {
42        self.0
43    }
44
45    /// Returns the inner usize value.
46    pub const fn as_usize(&self) -> usize {
47        self.0 .0
48    }
49
50    /// Converts to u32 (truncating on 64-bit platforms).
51    pub fn as_u32(&self) -> u32 {
52        self.0 .0 as u32
53    }
54
55    /// Converts to i32 (truncating on 64-bit platforms).
56    pub fn as_i32(&self) -> i32 {
57        self.0 .0 as i32
58    }
59}
60
61impl From<usize> for WParam {
62    fn from(value: usize) -> Self {
63        Self::new(value)
64    }
65}
66
67impl From<WParam> for usize {
68    fn from(wparam: WParam) -> Self {
69        wparam.as_usize()
70    }
71}
72
73impl fmt::Display for WParam {
74    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        write!(f, "WPARAM(0x{:X})", self.0 .0)
76    }
77}
78
79/// Newtype wrapper for LPARAM with utility methods.
80#[repr(transparent)]
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83pub struct LParam(pub LPARAM);
84
85impl LParam {
86    /// Creates a new LPARAM wrapper.
87    pub const fn new(value: isize) -> Self {
88        Self(LPARAM(value))
89    }
90
91    /// Returns the raw value.
92    pub const fn raw(&self) -> LPARAM {
93        self.0
94    }
95
96    /// Returns the inner isize value.
97    pub const fn as_isize(&self) -> isize {
98        self.0 .0
99    }
100
101    /// Converts to u32 (truncating on 64-bit platforms).
102    pub fn as_u32(&self) -> u32 {
103        self.0 .0 as u32
104    }
105
106    /// Converts to i32 (truncating on 64-bit platforms).
107    pub fn as_i32(&self) -> i32 {
108        self.0 .0 as i32
109    }
110
111    /// Extracts coordinates from LPARAM (for mouse messages).
112    pub fn as_point(&self) -> (i16, i16) {
113        let value = self.as_u32();
114        ((value & 0xFFFF) as i16, ((value >> 16) & 0xFFFF) as i16)
115    }
116
117    /// Creates LPARAM from coordinates.
118    pub fn from_point(x: i16, y: i16) -> Self {
119        let value = ((y as u32) << 16) | (x as u32 & 0xFFFF);
120        Self::new(value as isize)
121    }
122}
123
124impl From<isize> for LParam {
125    fn from(value: isize) -> Self {
126        Self::new(value)
127    }
128}
129
130impl From<LParam> for isize {
131    fn from(lparam: LParam) -> Self {
132        lparam.as_isize()
133    }
134}
135
136impl fmt::Display for LParam {
137    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138        write!(f, "LPARAM(0x{:X})", self.0 .0)
139    }
140}
141
142/// Mouse button enumeration.
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
145pub enum MouseButton {
146    /// Left mouse button
147    Left,
148    /// Right mouse button
149    Right,
150    /// Middle mouse button
151    Middle,
152    /// X1 button (back)
153    X1,
154    /// X2 button (forward)
155    X2,
156}
157
158/// Keyboard modifier keys.
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161pub struct KeyModifiers {
162    /// Shift key pressed
163    pub shift: bool,
164    /// Control key pressed
165    pub ctrl: bool,
166    /// Alt key pressed
167    pub alt: bool,
168}
169
170/// Mouse event information.
171#[derive(Debug, Clone, Copy, PartialEq)]
172#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
173pub struct MouseEvent {
174    /// X coordinate
175    pub x: i32,
176    /// Y coordinate
177    pub y: i32,
178    /// Mouse button (if any)
179    pub button: Option<MouseButton>,
180    /// Modifier keys
181    pub modifiers: KeyModifiers,
182}
183
184/// Keyboard event information.
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
187pub struct KeyEvent {
188    /// Virtual key code
189    pub virtual_key: u16,
190    /// Scan code
191    pub scan_code: u16,
192    /// Modifier keys
193    pub modifiers: KeyModifiers,
194    /// Repeat count
195    pub repeat_count: u16,
196}
197
198/// Windows message structure.
199#[derive(Debug, Clone, Copy)]
200#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
201pub struct WindowMessage {
202    /// Message identifier
203    pub msg: u32,
204    /// WPARAM parameter
205    pub wparam: WParam,
206    /// LPARAM parameter
207    pub lparam: LParam,
208}
209
210impl WindowMessage {
211    /// Creates a new window message.
212    pub const fn new(msg: u32, wparam: WParam, lparam: LParam) -> Self {
213        Self {
214            msg,
215            wparam,
216            lparam,
217        }
218    }
219
220    /// Creates a mouse movement message.
221    pub fn mouse_move(x: i16, y: i16, modifiers: KeyModifiers) -> Self {
222        let wparam = Self::make_mouse_wparam(modifiers, None);
223        let lparam = LParam::from_point(x, y);
224        Self::new(windows_messages::WM_MOUSEMOVE, wparam, lparam)
225    }
226
227    /// Creates a mouse button message.
228    pub fn mouse_button(
229        button: MouseButton,
230        pressed: bool,
231        x: i16,
232        y: i16,
233        modifiers: KeyModifiers,
234    ) -> Self {
235        let msg = match (button, pressed) {
236            (MouseButton::Left, true) => windows_messages::WM_LBUTTONDOWN,
237            (MouseButton::Left, false) => windows_messages::WM_LBUTTONUP,
238            (MouseButton::Right, true) => windows_messages::WM_RBUTTONDOWN,
239            (MouseButton::Right, false) => windows_messages::WM_RBUTTONUP,
240            (MouseButton::Middle, true) => windows_messages::WM_MBUTTONDOWN,
241            (MouseButton::Middle, false) => windows_messages::WM_MBUTTONUP,
242            _ => windows_messages::WM_MOUSEMOVE,
243        };
244
245        let wparam = Self::make_mouse_wparam(modifiers, Some(button));
246        let lparam = LParam::from_point(x, y);
247        Self::new(msg, wparam, lparam)
248    }
249
250    /// Creates a keyboard message.
251    pub fn key_event(msg: u32, virtual_key: u16, scan_code: u16, repeat_count: u16) -> Self {
252        let wparam = WParam::new(virtual_key as usize);
253        let lparam_value = (((scan_code as u32) << 16) | (repeat_count as u32)) as isize;
254        Self::new(msg, wparam, LParam::new(lparam_value))
255    }
256
257    fn make_mouse_wparam(modifiers: KeyModifiers, button: Option<MouseButton>) -> WParam {
258        let mut wparam = 0u32;
259
260        if modifiers.shift {
261            wparam |= 0x0004; // MK_SHIFT
262        }
263        if modifiers.ctrl {
264            wparam |= 0x0008; // MK_CONTROL
265        }
266
267        if let Some(button) = button {
268            match button {
269                MouseButton::Left => wparam |= 0x0001,   // MK_LBUTTON
270                MouseButton::Right => wparam |= 0x0002,  // MK_RBUTTON
271                MouseButton::Middle => wparam |= 0x0010, // MK_MBUTTON
272                MouseButton::X1 => wparam |= 0x0020,     // MK_XBUTTON1
273                MouseButton::X2 => wparam |= 0x0040,     // MK_XBUTTON2
274            }
275        }
276
277        WParam::new(wparam as usize)
278    }
279}
280
281/// Parser for Windows message parameters.
282pub struct MessageParser;
283
284impl MessageParser {
285    /// Parses a mouse message.
286    pub fn parse_mouse_message(message: WindowMessage) -> Option<MouseEvent> {
287        match message.msg {
288            windows_messages::WM_MOUSEMOVE
289            | windows_messages::WM_LBUTTONDOWN
290            | windows_messages::WM_LBUTTONUP
291            | windows_messages::WM_RBUTTONDOWN
292            | windows_messages::WM_RBUTTONUP
293            | windows_messages::WM_MBUTTONDOWN
294            | windows_messages::WM_MBUTTONUP => {
295                let (x, y) = message.lparam.as_point();
296                let wparam = message.wparam.as_u32();
297
298                let modifiers = KeyModifiers {
299                    shift: (wparam & 0x0004) != 0,
300                    ctrl: (wparam & 0x0008) != 0,
301                    alt: false, // ALT state not in wparam
302                };
303
304                let button = match message.msg {
305                    windows_messages::WM_LBUTTONDOWN | windows_messages::WM_LBUTTONUP => {
306                        Some(MouseButton::Left)
307                    }
308                    windows_messages::WM_RBUTTONDOWN | windows_messages::WM_RBUTTONUP => {
309                        Some(MouseButton::Right)
310                    }
311                    windows_messages::WM_MBUTTONDOWN | windows_messages::WM_MBUTTONUP => {
312                        Some(MouseButton::Middle)
313                    }
314                    _ => None,
315                };
316
317                Some(MouseEvent {
318                    x: x as i32,
319                    y: y as i32,
320                    button,
321                    modifiers,
322                })
323            }
324            _ => None,
325        }
326    }
327
328    /// Parses a keyboard message.
329    pub fn parse_key_message(message: WindowMessage) -> Option<KeyEvent> {
330        match message.msg {
331            windows_messages::WM_KEYDOWN
332            | windows_messages::WM_KEYUP
333            | windows_messages::WM_SYSKEYDOWN
334            | windows_messages::WM_SYSKEYUP => {
335                let virtual_key = message.wparam.as_u32() as u16;
336                let lparam = message.lparam.as_u32();
337
338                let scan_code = ((lparam >> 16) & 0xFF) as u16;
339                let repeat_count = (lparam & 0xFFFF) as u16;
340
341                // In real implementation, you'd query key states
342                let modifiers = KeyModifiers::default();
343
344                Some(KeyEvent {
345                    virtual_key,
346                    scan_code,
347                    modifiers,
348                    repeat_count,
349                })
350            }
351            _ => None,
352        }
353    }
354
355    /// Parses a window size message.
356    pub fn parse_size_message(message: WindowMessage) -> Option<(i32, i32)> {
357        if message.msg == windows_messages::WM_SIZE {
358            let (width, height) = message.lparam.as_point();
359            Some((width as i32, height as i32))
360        } else {
361            None
362        }
363    }
364}
365
366/// Common Windows message constants.
367///
368/// This module contains common Windows message constants used throughout the crate.
369pub mod windows_messages {
370    /// Null message.
371    pub const WM_NULL: u32 = 0x0000;
372    /// Window creation message.
373    pub const WM_CREATE: u32 = 0x0001;
374    /// Window destruction message.
375    pub const WM_DESTROY: u32 = 0x0002;
376    /// Window move message.
377    pub const WM_MOVE: u32 = 0x0003;
378    /// Window size message.
379    pub const WM_SIZE: u32 = 0x0005;
380    /// Paint message.
381    pub const WM_PAINT: u32 = 0x000F;
382    /// Close message.
383    pub const WM_CLOSE: u32 = 0x0010;
384    /// Quit message.
385    pub const WM_QUIT: u32 = 0x0012;
386
387    /// Key down message.
388    pub const WM_KEYDOWN: u32 = 0x0100;
389    /// Key up message.
390    pub const WM_KEYUP: u32 = 0x0101;
391    /// System key down message.
392    pub const WM_SYSKEYDOWN: u32 = 0x0104;
393    /// System key up message.
394    pub const WM_SYSKEYUP: u32 = 0x0105;
395
396    /// Mouse move message.
397    pub const WM_MOUSEMOVE: u32 = 0x0200;
398    /// Left button down message.
399    pub const WM_LBUTTONDOWN: u32 = 0x0201;
400    /// Left button up message.
401    pub const WM_LBUTTONUP: u32 = 0x0202;
402    /// Right button down message.
403    pub const WM_RBUTTONDOWN: u32 = 0x0204;
404    /// Right button up message.
405    pub const WM_RBUTTONUP: u32 = 0x0205;
406    /// Middle button down message.
407    pub const WM_MBUTTONDOWN: u32 = 0x0207;
408    /// Middle button up message.
409    pub const WM_MBUTTONUP: u32 = 0x0208;
410}
411
412// Windows crate 互操作性
413#[cfg(feature = "windows-interop")]
414/// Windows crate interoperability module.
415///
416/// This module provides seamless conversion between `windows-api-utils` types
417/// and the `windows` crate types for WPARAM, LPARAM, and LRESULT.
418///
419/// # Examples
420///
421/// ```rust
422/// use windows_api_utils::prelude::*;
423/// use windows::Win32::Foundation::{WPARAM, LPARAM};
424///
425/// // Convert from windows crate types
426/// let win_wparam = WPARAM(0x1234);
427/// let our_wparam = WParam::from(win_wparam);
428///
429/// // Convert back to windows crate types
430/// let back_to_win: WPARAM = our_wparam.into();
431/// assert_eq!(win_wparam.0, back_to_win.0);
432///
433/// // Test coordinate conversion
434/// let win_lparam = LPARAM(0x00C80064); // x=100, y=200
435/// let our_lparam = LParam::from(win_lparam);
436/// let (x, y) = our_lparam.as_point();
437/// assert_eq!(x, 100);
438/// assert_eq!(y, 200);
439/// ```
440pub mod windows_interop {
441    use super::*;
442
443    impl From<WParam> for windows::Win32::Foundation::WPARAM {
444        fn from(wparam: WParam) -> Self {
445            windows::Win32::Foundation::WPARAM(wparam.as_usize())
446        }
447    }
448
449    impl From<windows::Win32::Foundation::WPARAM> for WParam {
450        fn from(wparam: windows::Win32::Foundation::WPARAM) -> Self {
451            WParam::new(wparam.0)
452        }
453    }
454
455    impl From<LParam> for windows::Win32::Foundation::LPARAM {
456        fn from(lparam: LParam) -> Self {
457            windows::Win32::Foundation::LPARAM(lparam.as_isize())
458        }
459    }
460
461    impl From<windows::Win32::Foundation::LPARAM> for LParam {
462        fn from(lparam: windows::Win32::Foundation::LPARAM) -> Self {
463            LParam::new(lparam.0)
464        }
465    }
466
467    impl From<LRESULT> for windows::Win32::Foundation::LRESULT {
468        fn from(result: LRESULT) -> Self {
469            windows::Win32::Foundation::LRESULT(result.0)
470        }
471    }
472
473    impl From<windows::Win32::Foundation::LRESULT> for LRESULT {
474        fn from(result: windows::Win32::Foundation::LRESULT) -> Self {
475            LRESULT(result.0)
476        }
477    }
478}
479
480#[cfg(test)]
481mod tests {
482    use super::*;
483
484    #[test]
485    fn test_message_creation() {
486        let msg = WindowMessage::mouse_move(100, 200, KeyModifiers::default());
487        assert_eq!(msg.msg, windows_messages::WM_MOUSEMOVE);
488
489        if let Some(mouse_event) = MessageParser::parse_mouse_message(msg) {
490            assert_eq!(mouse_event.x, 100);
491            assert_eq!(mouse_event.y, 200);
492        }
493    }
494
495    #[test]
496    fn test_lparam_point_conversion() {
497        let lparam = LParam::from_point(100, 200);
498        let (x, y) = lparam.as_point();
499        assert_eq!(x, 100);
500        assert_eq!(y, 200);
501    }
502
503    #[test]
504    fn test_type_compatibility() {
505        // 测试我们的类型与 windows crate 的内存布局兼容性
506        let our_wparam = WParam::new(0x12345678);
507        let our_lparam = LParam::new(0x87654321);
508
509        // 验证底层表示
510        assert_eq!(our_wparam.as_usize(), 0x12345678);
511        assert_eq!(our_lparam.as_isize(), 0x87654321);
512    }
513}