use core::fmt;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WPARAM(pub usize);
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LPARAM(pub isize);
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LRESULT(pub isize);
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WParam(pub WPARAM);
impl WParam {
pub const fn new(value: usize) -> Self {
Self(WPARAM(value))
}
pub const fn raw(&self) -> WPARAM {
self.0
}
pub const fn as_usize(&self) -> usize {
self.0 .0
}
pub fn as_u32(&self) -> u32 {
self.0 .0 as u32
}
pub fn as_i32(&self) -> i32 {
self.0 .0 as i32
}
}
impl From<usize> for WParam {
fn from(value: usize) -> Self {
Self::new(value)
}
}
impl From<WParam> for usize {
fn from(wparam: WParam) -> Self {
wparam.as_usize()
}
}
impl fmt::Display for WParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WPARAM(0x{:X})", self.0 .0)
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LParam(pub LPARAM);
impl LParam {
pub const fn new(value: isize) -> Self {
Self(LPARAM(value))
}
pub const fn raw(&self) -> LPARAM {
self.0
}
pub const fn as_isize(&self) -> isize {
self.0 .0
}
pub fn as_u32(&self) -> u32 {
self.0 .0 as u32
}
pub fn as_i32(&self) -> i32 {
self.0 .0 as i32
}
pub fn as_point(&self) -> (i16, i16) {
let value = self.as_u32();
((value & 0xFFFF) as i16, ((value >> 16) & 0xFFFF) as i16)
}
pub fn from_point(x: i16, y: i16) -> Self {
let value = ((y as u32) << 16) | (x as u32 & 0xFFFF);
Self::new(value as isize)
}
}
impl From<isize> for LParam {
fn from(value: isize) -> Self {
Self::new(value)
}
}
impl From<LParam> for isize {
fn from(lparam: LParam) -> Self {
lparam.as_isize()
}
}
impl fmt::Display for LParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LPARAM(0x{:X})", self.0 .0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MouseButton {
Left,
Right,
Middle,
X1,
X2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct KeyModifiers {
pub shift: bool,
pub ctrl: bool,
pub alt: bool,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MouseEvent {
pub x: i32,
pub y: i32,
pub button: Option<MouseButton>,
pub modifiers: KeyModifiers,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct KeyEvent {
pub virtual_key: u16,
pub scan_code: u16,
pub modifiers: KeyModifiers,
pub repeat_count: u16,
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WindowMessage {
pub msg: u32,
pub wparam: WParam,
pub lparam: LParam,
}
impl WindowMessage {
pub const fn new(msg: u32, wparam: WParam, lparam: LParam) -> Self {
Self {
msg,
wparam,
lparam,
}
}
pub fn mouse_move(x: i16, y: i16, modifiers: KeyModifiers) -> Self {
let wparam = Self::make_mouse_wparam(modifiers, None);
let lparam = LParam::from_point(x, y);
Self::new(windows_messages::WM_MOUSEMOVE, wparam, lparam)
}
pub fn mouse_button(
button: MouseButton,
pressed: bool,
x: i16,
y: i16,
modifiers: KeyModifiers,
) -> Self {
let msg = match (button, pressed) {
(MouseButton::Left, true) => windows_messages::WM_LBUTTONDOWN,
(MouseButton::Left, false) => windows_messages::WM_LBUTTONUP,
(MouseButton::Right, true) => windows_messages::WM_RBUTTONDOWN,
(MouseButton::Right, false) => windows_messages::WM_RBUTTONUP,
(MouseButton::Middle, true) => windows_messages::WM_MBUTTONDOWN,
(MouseButton::Middle, false) => windows_messages::WM_MBUTTONUP,
_ => windows_messages::WM_MOUSEMOVE,
};
let wparam = Self::make_mouse_wparam(modifiers, Some(button));
let lparam = LParam::from_point(x, y);
Self::new(msg, wparam, lparam)
}
pub fn key_event(msg: u32, virtual_key: u16, scan_code: u16, repeat_count: u16) -> Self {
let wparam = WParam::new(virtual_key as usize);
let lparam_value = (((scan_code as u32) << 16) | (repeat_count as u32)) as isize;
Self::new(msg, wparam, LParam::new(lparam_value))
}
fn make_mouse_wparam(modifiers: KeyModifiers, button: Option<MouseButton>) -> WParam {
let mut wparam = 0u32;
if modifiers.shift {
wparam |= 0x0004; }
if modifiers.ctrl {
wparam |= 0x0008; }
if let Some(button) = button {
match button {
MouseButton::Left => wparam |= 0x0001, MouseButton::Right => wparam |= 0x0002, MouseButton::Middle => wparam |= 0x0010, MouseButton::X1 => wparam |= 0x0020, MouseButton::X2 => wparam |= 0x0040, }
}
WParam::new(wparam as usize)
}
}
pub struct MessageParser;
impl MessageParser {
pub fn parse_mouse_message(message: WindowMessage) -> Option<MouseEvent> {
match message.msg {
windows_messages::WM_MOUSEMOVE
| windows_messages::WM_LBUTTONDOWN
| windows_messages::WM_LBUTTONUP
| windows_messages::WM_RBUTTONDOWN
| windows_messages::WM_RBUTTONUP
| windows_messages::WM_MBUTTONDOWN
| windows_messages::WM_MBUTTONUP => {
let (x, y) = message.lparam.as_point();
let wparam = message.wparam.as_u32();
let modifiers = KeyModifiers {
shift: (wparam & 0x0004) != 0,
ctrl: (wparam & 0x0008) != 0,
alt: false, };
let button = match message.msg {
windows_messages::WM_LBUTTONDOWN | windows_messages::WM_LBUTTONUP => {
Some(MouseButton::Left)
}
windows_messages::WM_RBUTTONDOWN | windows_messages::WM_RBUTTONUP => {
Some(MouseButton::Right)
}
windows_messages::WM_MBUTTONDOWN | windows_messages::WM_MBUTTONUP => {
Some(MouseButton::Middle)
}
_ => None,
};
Some(MouseEvent {
x: x as i32,
y: y as i32,
button,
modifiers,
})
}
_ => None,
}
}
pub fn parse_key_message(message: WindowMessage) -> Option<KeyEvent> {
match message.msg {
windows_messages::WM_KEYDOWN
| windows_messages::WM_KEYUP
| windows_messages::WM_SYSKEYDOWN
| windows_messages::WM_SYSKEYUP => {
let virtual_key = message.wparam.as_u32() as u16;
let lparam = message.lparam.as_u32();
let scan_code = ((lparam >> 16) & 0xFF) as u16;
let repeat_count = (lparam & 0xFFFF) as u16;
let modifiers = KeyModifiers::default();
Some(KeyEvent {
virtual_key,
scan_code,
modifiers,
repeat_count,
})
}
_ => None,
}
}
pub fn parse_size_message(message: WindowMessage) -> Option<(i32, i32)> {
if message.msg == windows_messages::WM_SIZE {
let (width, height) = message.lparam.as_point();
Some((width as i32, height as i32))
} else {
None
}
}
}
pub mod windows_messages {
pub const WM_NULL: u32 = 0x0000;
pub const WM_CREATE: u32 = 0x0001;
pub const WM_DESTROY: u32 = 0x0002;
pub const WM_MOVE: u32 = 0x0003;
pub const WM_SIZE: u32 = 0x0005;
pub const WM_PAINT: u32 = 0x000F;
pub const WM_CLOSE: u32 = 0x0010;
pub const WM_QUIT: u32 = 0x0012;
pub const WM_KEYDOWN: u32 = 0x0100;
pub const WM_KEYUP: u32 = 0x0101;
pub const WM_SYSKEYDOWN: u32 = 0x0104;
pub const WM_SYSKEYUP: u32 = 0x0105;
pub const WM_MOUSEMOVE: u32 = 0x0200;
pub const WM_LBUTTONDOWN: u32 = 0x0201;
pub const WM_LBUTTONUP: u32 = 0x0202;
pub const WM_RBUTTONDOWN: u32 = 0x0204;
pub const WM_RBUTTONUP: u32 = 0x0205;
pub const WM_MBUTTONDOWN: u32 = 0x0207;
pub const WM_MBUTTONUP: u32 = 0x0208;
}
#[cfg(feature = "windows-interop")]
pub mod windows_interop {
use super::*;
impl From<WParam> for windows::Win32::Foundation::WPARAM {
fn from(wparam: WParam) -> Self {
windows::Win32::Foundation::WPARAM(wparam.as_usize())
}
}
impl From<windows::Win32::Foundation::WPARAM> for WParam {
fn from(wparam: windows::Win32::Foundation::WPARAM) -> Self {
WParam::new(wparam.0)
}
}
impl From<LParam> for windows::Win32::Foundation::LPARAM {
fn from(lparam: LParam) -> Self {
windows::Win32::Foundation::LPARAM(lparam.as_isize())
}
}
impl From<windows::Win32::Foundation::LPARAM> for LParam {
fn from(lparam: windows::Win32::Foundation::LPARAM) -> Self {
LParam::new(lparam.0)
}
}
impl From<LRESULT> for windows::Win32::Foundation::LRESULT {
fn from(result: LRESULT) -> Self {
windows::Win32::Foundation::LRESULT(result.0)
}
}
impl From<windows::Win32::Foundation::LRESULT> for LRESULT {
fn from(result: windows::Win32::Foundation::LRESULT) -> Self {
LRESULT(result.0)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_message_creation() {
let msg = WindowMessage::mouse_move(100, 200, KeyModifiers::default());
assert_eq!(msg.msg, windows_messages::WM_MOUSEMOVE);
if let Some(mouse_event) = MessageParser::parse_mouse_message(msg) {
assert_eq!(mouse_event.x, 100);
assert_eq!(mouse_event.y, 200);
}
}
#[test]
fn test_lparam_point_conversion() {
let lparam = LParam::from_point(100, 200);
let (x, y) = lparam.as_point();
assert_eq!(x, 100);
assert_eq!(y, 200);
}
#[test]
fn test_type_compatibility() {
let our_wparam = WParam::new(0x12345678);
let our_lparam = LParam::new(0x87654321);
assert_eq!(our_wparam.as_usize(), 0x12345678);
assert_eq!(our_lparam.as_isize(), 0x87654321);
}
}