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//! Also requires the `bit-ops` feature for bit manipulation utilities.
8
9// Import bit operations from the bit_ops module directly
10#[cfg(feature = "bit-ops")]
11use crate::bit_ops::{hiword, loword, make_long};
12
13/// Windows message parameter (platform-dependent size).
14#[cfg(target_pointer_width = "32")]
15pub type WPARAM = u32;
16
17/// Windows message parameter (platform-dependent size).
18#[cfg(target_pointer_width = "64")]
19pub type WPARAM = u64;
20
21/// Windows message parameter (platform-dependent size).
22#[cfg(target_pointer_width = "32")]
23pub type LPARAM = i32;
24
25/// Windows message parameter (platform-dependent size).
26#[cfg(target_pointer_width = "64")]
27pub type LPARAM = i64;
28
29/// Message result type (platform-dependent size).
30#[cfg(target_pointer_width = "32")]
31pub type LRESULT = i32;
32
33/// Message result type (platform-dependent size).
34#[cfg(target_pointer_width = "64")]
35pub type LRESULT = i64;
36
37/// Newtype wrapper for WPARAM with utility methods.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40pub struct WParam(WPARAM);
41
42impl WParam {
43    /// Creates a new WPARAM wrapper.
44    pub const fn new(value: WPARAM) -> Self {
45        Self(value)
46    }
47
48    /// Returns the raw value.
49    pub const fn raw(&self) -> WPARAM {
50        self.0
51    }
52
53    /// Converts to u32 (truncating on 64-bit platforms).
54    pub fn as_u32(&self) -> u32 {
55        self.0 as u32
56    }
57
58    /// Converts to i32 (truncating on 64-bit platforms).
59    pub fn as_i32(&self) -> i32 {
60        self.0 as i32
61    }
62}
63
64impl From<WPARAM> for WParam {
65    fn from(value: WPARAM) -> Self {
66        Self::new(value)
67    }
68}
69
70impl From<WParam> for WPARAM {
71    fn from(wparam: WParam) -> Self {
72        wparam.raw()
73    }
74}
75
76/// Newtype wrapper for LPARAM with utility methods.
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79pub struct LParam(LPARAM);
80
81impl LParam {
82    /// Creates a new LPARAM wrapper.
83    pub const fn new(value: LPARAM) -> Self {
84        Self(value)
85    }
86
87    /// Returns the raw value.
88    pub const fn raw(&self) -> LPARAM {
89        self.0
90    }
91
92    /// Converts to u32 (truncating on 64-bit platforms).
93    pub fn as_u32(&self) -> u32 {
94        self.0 as u32
95    }
96
97    /// Converts to i32 (truncating on 64-bit platforms).
98    pub fn as_i32(&self) -> i32 {
99        self.0 as i32
100    }
101
102    /// Extracts coordinates from LPARAM (for mouse messages).
103    pub fn as_point(&self) -> (i16, i16) {
104        let value = self.as_u32();
105        // Use the functions directly if bit-ops feature is enabled
106        #[cfg(feature = "bit-ops")]
107        {
108            (loword(value) as i16, hiword(value) as i16)
109        }
110        // Fallback implementation if bit-ops is not enabled
111        #[cfg(not(feature = "bit-ops"))]
112        {
113            ((value & 0xFFFF) as i16, ((value >> 16) & 0xFFFF) as i16)
114        }
115    }
116
117    /// Creates LPARAM from coordinates.
118    pub fn from_point(x: i16, y: i16) -> Self {
119        #[cfg(feature = "bit-ops")]
120        {
121            Self::new(make_long(y as u16, x as u16) as LPARAM)
122        }
123        #[cfg(not(feature = "bit-ops"))]
124        {
125            let value = ((y as u32) << 16) | (x as u32);
126            Self::new(value as LPARAM)
127        }
128    }
129}
130
131impl From<LPARAM> for LParam {
132    fn from(value: LPARAM) -> Self {
133        Self::new(value)
134    }
135}
136
137impl From<LParam> for LPARAM {
138    fn from(lparam: LParam) -> Self {
139        lparam.raw()
140    }
141}
142
143/// Mouse button enumeration.
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146pub enum MouseButton {
147    /// Left mouse button
148    Left,
149    /// Right mouse button
150    Right,
151    /// Middle mouse button
152    Middle,
153    /// X1 button (back)
154    X1,
155    /// X2 button (forward)
156    X2,
157}
158
159/// Keyboard modifier keys.
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
161#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
162pub struct KeyModifiers {
163    /// Shift key pressed
164    pub shift: bool,
165    /// Control key pressed
166    pub ctrl: bool,
167    /// Alt key pressed
168    pub alt: bool,
169}
170
171/// Mouse event information.
172#[derive(Debug, Clone, Copy, PartialEq)]
173#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
174pub struct MouseEvent {
175    /// X coordinate
176    pub x: i32,
177    /// Y coordinate
178    pub y: i32,
179    /// Mouse button (if any)
180    pub button: Option<MouseButton>,
181    /// Modifier keys
182    pub modifiers: KeyModifiers,
183}
184
185/// Keyboard event information.
186#[derive(Debug, Clone, Copy, PartialEq, Eq)]
187#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
188pub struct KeyEvent {
189    /// Virtual key code
190    pub virtual_key: u16,
191    /// Scan code
192    pub scan_code: u16,
193    /// Modifier keys
194    pub modifiers: KeyModifiers,
195    /// Repeat count
196    pub repeat_count: u16,
197}
198
199/// Windows message structure.
200#[derive(Debug, Clone, Copy)]
201#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
202pub struct WindowMessage {
203    /// Message identifier
204    pub msg: u32,
205    /// WPARAM parameter
206    pub wparam: WParam,
207    /// LPARAM parameter
208    pub lparam: LParam,
209}
210
211impl WindowMessage {
212    /// Creates a new window message.
213    pub const fn new(msg: u32, wparam: WParam, lparam: LParam) -> Self {
214        Self {
215            msg,
216            wparam,
217            lparam,
218        }
219    }
220
221    /// Creates a mouse movement message.
222    pub fn mouse_move(x: i16, y: i16, modifiers: KeyModifiers) -> Self {
223        let wparam = Self::make_mouse_wparam(modifiers, None);
224        let lparam = LParam::from_point(x, y);
225        Self::new(windows_messages::WM_MOUSEMOVE, wparam, lparam)
226    }
227
228    /// Creates a mouse button message.
229    pub fn mouse_button(
230        button: MouseButton,
231        pressed: bool,
232        x: i16,
233        y: i16,
234        modifiers: KeyModifiers,
235    ) -> Self {
236        let msg = match (button, pressed) {
237            (MouseButton::Left, true) => windows_messages::WM_LBUTTONDOWN,
238            (MouseButton::Left, false) => windows_messages::WM_LBUTTONUP,
239            (MouseButton::Right, true) => windows_messages::WM_RBUTTONDOWN,
240            (MouseButton::Right, false) => windows_messages::WM_RBUTTONUP,
241            (MouseButton::Middle, true) => windows_messages::WM_MBUTTONDOWN,
242            (MouseButton::Middle, false) => windows_messages::WM_MBUTTONUP,
243            _ => windows_messages::WM_MOUSEMOVE,
244        };
245
246        let wparam = Self::make_mouse_wparam(modifiers, Some(button));
247        let lparam = LParam::from_point(x, y);
248        Self::new(msg, wparam, lparam)
249    }
250
251    /// Creates a keyboard message.
252    pub fn key_event(msg: u32, virtual_key: u16, scan_code: u16, repeat_count: u16) -> Self {
253        let wparam = WParam::new(virtual_key as WPARAM);
254        #[cfg(feature = "bit-ops")]
255        let lparam_value = {
256            let high = ((scan_code as u32) << 16 | (repeat_count as u32)) >> 16;
257            let low = (repeat_count as u32) & 0xFFFF;
258            make_long(high as u16, low as u16) as LPARAM
259        };
260        #[cfg(not(feature = "bit-ops"))]
261        let lparam_value = (((scan_code as u32) << 16) | (repeat_count as u32)) as LPARAM;
262        Self::new(msg, wparam, LParam::new(lparam_value))
263    }
264
265    fn make_mouse_wparam(modifiers: KeyModifiers, button: Option<MouseButton>) -> WParam {
266        let mut wparam = 0u32;
267
268        if modifiers.shift {
269            wparam |= 0x0004; // MK_SHIFT
270        }
271        if modifiers.ctrl {
272            wparam |= 0x0008; // MK_CONTROL
273        }
274
275        if let Some(button) = button {
276            match button {
277                MouseButton::Left => wparam |= 0x0001,   // MK_LBUTTON
278                MouseButton::Right => wparam |= 0x0002,  // MK_RBUTTON
279                MouseButton::Middle => wparam |= 0x0010, // MK_MBUTTON
280                MouseButton::X1 => wparam |= 0x0020,     // MK_XBUTTON1
281                MouseButton::X2 => wparam |= 0x0040,     // MK_XBUTTON2
282            }
283        }
284
285        WParam::new(wparam as WPARAM)
286    }
287}
288
289/// Parser for Windows message parameters.
290pub struct MessageParser;
291
292impl MessageParser {
293    /// Parses a mouse message.
294    pub fn parse_mouse_message(message: WindowMessage) -> Option<MouseEvent> {
295        match message.msg {
296            windows_messages::WM_MOUSEMOVE
297            | windows_messages::WM_LBUTTONDOWN
298            | windows_messages::WM_LBUTTONUP
299            | windows_messages::WM_RBUTTONDOWN
300            | windows_messages::WM_RBUTTONUP
301            | windows_messages::WM_MBUTTONDOWN
302            | windows_messages::WM_MBUTTONUP => {
303                let (x, y) = message.lparam.as_point();
304                let wparam = message.wparam.as_u32();
305
306                let modifiers = KeyModifiers {
307                    shift: (wparam & 0x0004) != 0,
308                    ctrl: (wparam & 0x0008) != 0,
309                    alt: false, // ALT state not in wparam
310                };
311
312                let button = match message.msg {
313                    windows_messages::WM_LBUTTONDOWN | windows_messages::WM_LBUTTONUP => {
314                        Some(MouseButton::Left)
315                    }
316                    windows_messages::WM_RBUTTONDOWN | windows_messages::WM_RBUTTONUP => {
317                        Some(MouseButton::Right)
318                    }
319                    windows_messages::WM_MBUTTONDOWN | windows_messages::WM_MBUTTONUP => {
320                        Some(MouseButton::Middle)
321                    }
322                    _ => None,
323                };
324
325                Some(MouseEvent {
326                    x: x as i32,
327                    y: y as i32,
328                    button,
329                    modifiers,
330                })
331            }
332            _ => None,
333        }
334    }
335
336    /// Parses a keyboard message.
337    pub fn parse_key_message(message: WindowMessage) -> Option<KeyEvent> {
338        match message.msg {
339            windows_messages::WM_KEYDOWN
340            | windows_messages::WM_KEYUP
341            | windows_messages::WM_SYSKEYDOWN
342            | windows_messages::WM_SYSKEYUP => {
343                let virtual_key = message.wparam.as_u32() as u16;
344                let lparam = message.lparam.as_u32();
345
346                let scan_code = ((lparam >> 16) & 0xFF) as u16;
347                let repeat_count = (lparam & 0xFFFF) as u16;
348
349                // In real implementation, you'd query key states
350                let modifiers = KeyModifiers::default();
351
352                Some(KeyEvent {
353                    virtual_key,
354                    scan_code,
355                    modifiers,
356                    repeat_count,
357                })
358            }
359            _ => None,
360        }
361    }
362
363    /// Parses a window size message.
364    pub fn parse_size_message(message: WindowMessage) -> Option<(i32, i32)> {
365        if message.msg == windows_messages::WM_SIZE {
366            let (width, height) = message.lparam.as_point();
367            Some((width as i32, height as i32))
368        } else {
369            None
370        }
371    }
372}
373
374/// Common Windows message constants.
375///
376/// This module contains common Windows message constants used throughout the crate.
377pub mod windows_messages {
378    /// Null message.
379    pub const WM_NULL: u32 = 0x0000;
380    /// Window creation message.
381    pub const WM_CREATE: u32 = 0x0001;
382    /// Window destruction message.
383    pub const WM_DESTROY: u32 = 0x0002;
384    /// Window move message.
385    pub const WM_MOVE: u32 = 0x0003;
386    /// Window size message.
387    pub const WM_SIZE: u32 = 0x0005;
388    /// Paint message.
389    pub const WM_PAINT: u32 = 0x000F;
390    /// Close message.
391    pub const WM_CLOSE: u32 = 0x0010;
392    /// Quit message.
393    pub const WM_QUIT: u32 = 0x0012;
394
395    /// Key down message.
396    pub const WM_KEYDOWN: u32 = 0x0100;
397    /// Key up message.
398    pub const WM_KEYUP: u32 = 0x0101;
399    /// System key down message.
400    pub const WM_SYSKEYDOWN: u32 = 0x0104;
401    /// System key up message.
402    pub const WM_SYSKEYUP: u32 = 0x0105;
403
404    /// Mouse move message.
405    pub const WM_MOUSEMOVE: u32 = 0x0200;
406    /// Left button down message.
407    pub const WM_LBUTTONDOWN: u32 = 0x0201;
408    /// Left button up message.
409    pub const WM_LBUTTONUP: u32 = 0x0202;
410    /// Right button down message.
411    pub const WM_RBUTTONDOWN: u32 = 0x0204;
412    /// Right button up message.
413    pub const WM_RBUTTONUP: u32 = 0x0205;
414    /// Middle button down message.
415    pub const WM_MBUTTONDOWN: u32 = 0x0207;
416    /// Middle button up message.
417    pub const WM_MBUTTONUP: u32 = 0x0208;
418}
419
420#[cfg(test)]
421mod tests {
422    use super::*;
423
424    #[test]
425    fn test_message_creation() {
426        let msg = WindowMessage::mouse_move(100, 200, KeyModifiers::default());
427        assert_eq!(msg.msg, windows_messages::WM_MOUSEMOVE);
428
429        if let Some(mouse_event) = MessageParser::parse_mouse_message(msg) {
430            assert_eq!(mouse_event.x, 100);
431            assert_eq!(mouse_event.y, 200);
432        }
433    }
434
435    #[test]
436    fn test_lparam_point_conversion() {
437        let lparam = LParam::from_point(100, 200);
438        let (x, y) = lparam.as_point();
439        assert_eq!(x, 100);
440        assert_eq!(y, 200);
441    }
442}