pub mod get;
pub mod peek;
pub mod wait;
use {
super::{
CreateStruct,
DwmWindowAttribute,
LResult,
LpParam,
PeekMessageFlags,
Window,
WindowProcedure,
dpi_to_scale_factor,
},
crate::{
Handle,
Rect,
high_word,
input::keyboard::{
destructure_key_lparam,
get_kbd_state,
get_location,
key::Key,
layout::{
LAYOUT_CACHE,
WindowsModifiers,
},
new_ex_scancode,
scancode_to_code,
},
low_word,
},
dpi::{
PhysicalPosition,
PhysicalSize,
},
keyboard_types::{
Code,
KeyState,
Location,
Modifiers,
NamedKey,
},
std::ops::{
Deref,
RangeInclusive,
},
windows_result::Error,
windows_sys::Win32::{
Foundation::{
POINT,
RECT,
},
UI::{
Input::KeyboardAndMouse::{
HKL,
MAPVK_VK_TO_VSC_EX,
MapVirtualKeyExW,
VIRTUAL_KEY,
VK_NUMLOCK,
},
WindowsAndMessaging::{
self,
CREATESTRUCTW,
DispatchMessageW,
MSG,
TranslateMessage,
},
},
},
};
pub use {
get::*,
peek::*,
};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WParam(pub usize);
impl Deref for WParam {
type Target = usize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LParam(pub isize);
impl Deref for LParam {
type Target = isize;
fn deref(&self) -> &Self::Target {
&self.0
}
}
const REGISTERED_MESSAGES_LOWER: u32 = 0xC000;
const REGISTERED_MESSAGES_UPPER: u32 = 0xFFFF;
#[derive(win64_macro::Message, Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum MessageId {
#[default]
Null,
#[params(w)]
Quit,
#[fallback]
#[params(w, l)]
Other(u32),
#[id(WindowsAndMessaging::WM_USER..WindowsAndMessaging::WM_APP)]
#[params(w, l)]
User(u32),
#[id(WindowsAndMessaging::WM_APP..REGISTERED_MESSAGES_LOWER)]
#[params(w, l)]
App(u32),
#[id(REGISTERED_MESSAGES_LOWER..=REGISTERED_MESSAGES_UPPER)]
#[params(w, l)]
Registered(u32),
#[params(w, l)]
Activate,
#[params(w, l)]
ActivateApp,
#[params(w, l)]
AppCommand,
#[params(w, l)]
AskCbFormatName,
CancelJournal,
CancelMode,
#[params(l)]
CaptureChanged,
#[params(w, l)]
ChangeCbChain,
#[params(w)]
ChangeUiState,
#[params(w, l)]
Char,
#[params(w, l)]
CharToItem,
ChildActivate,
Clear,
ClipboardUpdate,
Close,
Command,
CommNotify,
#[params(w)]
Compacting,
#[params(w, l)]
CompareItem,
#[params(w, l)]
ContextMenu,
Copy,
#[params(w, l)]
CopyData,
#[params(l)]
Create,
#[params(w, l)]
CtlColorBtn,
#[params(w, l)]
CtlColorDlg,
#[params(w, l)]
CtlColorEdit,
#[params(w, l)]
CtlColorListBox,
#[params(w, l)]
CtlColorMsgBox,
#[params(w, l)]
CtlColorScrollBar,
#[params(w, l)]
CtlColorStatic,
Cut,
#[params(w, l)]
DeadChar,
#[params(w, l)]
DeleteItem,
Destroy,
DestroyClipboard,
#[params(w, l)]
DeviceChange,
#[params(l)]
DevModeChange,
#[params(w, l)]
DisplayChange,
#[params(w, l)]
DpiChanged,
#[id(WindowsAndMessaging::WM_DPICHANGED_AFTERPARENT)]
DpiChangedAfterParent,
#[id(WindowsAndMessaging::WM_DPICHANGED_BEFOREPARENT)]
DpiChangedBeforeParent,
DrawClipboard,
#[params(w, l)]
DrawItem,
#[params(w)]
DropFiles,
#[params(w, l)]
DwmColorizationColorChanged,
DwmCompositionChanged,
#[params(w)]
DwmNcRenderingChanged,
DwmSendIconicLivePreviewBitmap,
#[params(l)]
DwmSendIconicThumbnail,
#[params(w)]
DwmWindowMaximizedChange,
#[params(w)]
Enable,
#[params(w, l)]
EndSession,
#[params(w, l)]
EnterIdle,
#[params(w)]
EnterMenuLoop,
EnterSizeMove,
#[params(w)]
EraseBkgnd,
ExitMenuLoop,
ExitSizeMove,
FontChange,
#[params(w, l)]
Gesture,
#[params(l)]
GestureNotify,
#[params(w, l)]
GetDlgCode,
#[params(w, l)]
GetDpiScaledSize,
GetFont,
#[id(WindowsAndMessaging::MN_GETHMENU)]
GetHMenu,
GetHotKey,
#[params(w, l)]
GetIcon,
#[params(l)]
GetMinMaxInfo,
#[params(w, l)]
GetObject,
#[params(w, l)]
GetText,
GetTextLength,
#[params(l)]
GetTitleBarInfoEx,
#[params(l)]
Help,
#[params(w, l)]
HotKey,
#[params(w, l)]
HScroll,
#[params(w, l)]
HScrollClipboard,
#[params(w)]
IconEraseBkgnd,
#[id(WindowsAndMessaging::WM_IME_CHAR)]
#[params(w, l)]
ImeChar,
#[id(WindowsAndMessaging::WM_IME_COMPOSITION)]
#[params(w, l)]
ImeComposition,
#[id(WindowsAndMessaging::WM_IME_COMPOSITIONFULL)]
ImeCompositionFull,
#[id(WindowsAndMessaging::WM_IME_CONTROL)]
#[params(w, l)]
ImeControl,
#[id(WindowsAndMessaging::WM_IME_ENDCOMPOSITION)]
ImeEndComposition,
#[id(WindowsAndMessaging::WM_IME_KEYDOWN)]
#[params(w, l)]
ImeKeyDown,
#[id(WindowsAndMessaging::WM_IME_KEYUP)]
#[params(w, l)]
ImeKeyUp,
#[id(WindowsAndMessaging::WM_IME_NOTIFY)]
#[params(w, l)]
ImeNotify,
#[id(WindowsAndMessaging::WM_IME_REQUEST)]
#[params(w, l)]
ImeRequest,
#[id(WindowsAndMessaging::WM_IME_SELECT)]
#[params(w, l)]
ImeSelect,
#[id(WindowsAndMessaging::WM_IME_SETCONTEXT)]
#[params(w, l)]
ImeSetContext,
#[id(WindowsAndMessaging::WM_IME_STARTCOMPOSITION)]
ImeStartComposition,
#[params(w, l)]
InitDialog,
#[params(w)]
InitMenu,
InitMenuPopup,
#[params(w, l)]
Input,
#[params(w, l)]
InputLangChange,
#[params(w, l)]
InputLangChangeRequest,
#[id(WindowsAndMessaging::WM_INPUT_DEVICE_CHANGE)]
#[params(w, l)]
InputDeviceChange,
#[params(w, l)]
KeyDown,
#[params(w, l)]
KeyUp,
#[params(w)]
KillFocus,
#[params(w, l)]
LButtonDblClk,
#[params(w, l)]
LButtonDown,
#[params(w, l)]
LButtonUp,
#[params(w, l)]
MButtonDblClk,
#[params(w, l)]
MButtonDown,
#[params(w, l)]
MButtonUp,
#[params(w)]
MdiActivate,
#[params(w)]
MdiCascade,
#[params(l)]
MdiCreate,
#[params(w)]
MdiDestroy,
#[params(l)]
MdiGetActive,
MdiIconArrange,
#[params(w)]
MdiMaximize,
#[params(w, l)]
MdiNext,
MdiRefreshMenu,
#[params(w)]
MdiRestore,
#[params(w, l)]
MdiSetMenu,
#[params(w)]
MdiTile,
#[params(w, l)]
MeasureItem,
#[params(w, l)]
MenuChar,
#[params(w, l)]
MenuCommand,
#[params(w, l)]
MenuDrag,
#[params(l)]
MenuGetObject,
#[params(w, l)]
MenuRButtonUp,
#[params(w, l)]
MenuSelect,
#[params(w, l)]
MouseActivate,
#[params(w, l)]
MouseHWheel,
#[id(0x02A1)]
#[params(w, l)]
MouseHover,
#[id(0x02A3)]
MouseLeave,
#[params(w, l)]
MouseMove,
#[params(w, l)]
MouseWheel,
#[params(l)]
Move,
#[params(l)]
Moving,
#[params(w, l)]
NcActivate,
#[params(w, l)]
NcCalcSize,
#[params(l)]
NcCreate,
NcDestroy,
#[params(l)]
NcHitTest,
#[params(w, l)]
NcLButtonDblClk,
#[params(w, l)]
NcLButtonDown,
#[params(w, l)]
NcLButtonUp,
#[params(w, l)]
NcMButtonDblClk,
#[params(w, l)]
NcMButtonDown,
#[params(w, l)]
NcMButtonUp,
#[params(w, l)]
NcMouseHover,
NcMouseLeave,
#[params(w, l)]
NcMouseMove,
#[params(w)]
NcPaint,
#[params(w, l)]
NcPointerDown,
#[params(w, l)]
NcPointerUp,
#[params(w, l)]
NcPointerUpdate,
#[params(w, l)]
NcRButtonDblClk,
#[params(w, l)]
NcRButtonDown,
#[params(w, l)]
NcRButtonUp,
#[params(w, l)]
NcXButtonDblClk,
#[params(w, l)]
NcXButtonDown,
#[params(w, l)]
NcXButtonUp,
#[params(w, l)]
NextDlgCtl,
#[params(w, l)]
NextMenu,
#[params(w, l)]
Notify,
#[params(w, l)]
NotifyFormat,
Paint,
#[params(w, l)]
PaintClipboard,
PaintIcon,
#[params(w)]
PaletteChanged,
#[params(w)]
PaletteIsChanging,
#[params(w, l)]
ParentNotify,
Paste,
#[params(w, l)]
PointerActivate,
#[params(w, l)]
PointerCaptureChanged,
#[params(w, l)]
PointerDeviceChange,
#[params(w, l)]
PointerDeviceInRange,
#[params(w, l)]
PointerDeviceOutOfRange,
#[params(w, l)]
PointerDown,
#[params(w, l)]
PointerEnter,
#[params(w, l)]
PointerHWheel,
#[params(w, l)]
PointerLeave,
PointerRoutedAway,
PointerRoutedReleased,
PointerRoutedTo,
#[params(w, l)]
PointerUp,
#[params(w, l)]
PointerUpdate,
#[params(w, l)]
PointerWheel,
#[params(w)]
Power,
#[params(w, l)]
PowerBroadcast,
#[params(w, l)]
Print,
#[params(w, l)]
PrintClient,
QueryDragIcon,
#[params(l)]
QueryEndSession,
QueryNewPalette,
QueryOpen,
QueryUiState,
QueueSync,
#[params(w, l)]
RButtonDblClk,
#[params(w, l)]
RButtonDown,
#[params(w, l)]
RButtonUp,
RenderAllFormats,
#[params(w)]
RenderFormat,
#[params(w, l)]
SetCursor,
#[params(w)]
SetFocus,
#[params(w, l)]
SetFont,
#[params(w)]
SetHotKey,
#[params(w, l)]
SetIcon,
#[params(w)]
SetRedraw,
#[params(l)]
SetText,
#[params(w, l)]
SettingChange,
#[params(w, l)]
ShowWindow,
#[params(w, l)]
Size,
#[params(w, l)]
SizeClipboard,
#[params(w, l)]
Sizing,
#[params(w, l)]
SpoolerStatus,
#[params(w, l)]
StyleChanged,
#[params(w, l)]
StyleChanging,
SyncPaint,
#[params(w, l)]
SysChar,
SysColorChange,
#[params(w, l)]
SysCommand,
#[params(w, l)]
SysDeadChar,
#[params(w, l)]
SysKeyDown,
#[params(w, l)]
SysKeyUp,
#[params(w, l)]
TCard,
ThemeChanged,
TimeChange,
#[params(w, l)]
Timer,
#[params(w, l)]
TooltipDismiss,
#[params(w, l)]
Touch,
#[params(l)]
TouchHitTesting,
Undo,
#[params(w, l)]
UniChar,
#[params(w, l)]
UninitMenuPopup,
#[params(w)]
UpdateUiState,
UserChanged,
#[params(w, l)]
VKeyToItem,
#[params(w, l)]
VScroll,
#[params(w, l)]
VScrollClipboard,
#[params(l)]
WindowPosChanged,
#[params(l)]
WindowPosChanging,
#[id(WindowsAndMessaging::WM_WTSSESSION_CHANGE)]
#[params(w, l)]
WtsSessionChange,
#[params(w, l)]
XButtonDblClk,
#[params(w, l)]
XButtonDown,
#[params(w, l)]
XButtonUp,
}
impl Message {
pub const KEY_MESSAGES: RangeInclusive<u32> = WindowsAndMessaging::WM_KEYFIRST..=WindowsAndMessaging::WM_KEYLAST;
pub const MOUSE_MESSAGES: RangeInclusive<u32> =
WindowsAndMessaging::WM_MOUSEFIRST..=WindowsAndMessaging::WM_MOUSELAST;
#[inline]
pub fn is_key(&self) -> bool {
Self::KEY_MESSAGES.contains(&self.id().to_raw())
}
#[inline]
pub fn is_mouse(&self) -> bool {
Self::MOUSE_MESSAGES.contains(&self.id().to_raw())
}
#[inline]
pub const fn quit_requested(&self) -> bool {
matches!(self, Message::Destroy)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Msg {
pub window: Window,
pub message: Message,
pub time: u32,
pub point: PhysicalPosition<i32>,
}
impl Msg {
pub fn to_raw(&self) -> MSG {
MSG {
hwnd: self.window.to_ptr(),
message: self.message.id().to_raw(),
wParam: self.message.w().0,
lParam: self.message.l().0,
time: self.time,
pt: POINT {
x: self.point.x,
y: self.point.y,
},
}
}
pub fn translate(&self) {
let msg = self.to_raw();
unsafe { TranslateMessage(&msg) };
}
pub fn dispatch(&self) {
let msg = self.to_raw();
unsafe { DispatchMessageW(&msg) };
}
}
impl From<MSG> for Msg {
fn from(msg: MSG) -> Self {
let window = unsafe { Window::from_ptr(msg.hwnd) };
let time = msg.time;
let point = PhysicalPosition {
x: msg.pt.x,
y: msg.pt.y,
};
let message = Message::new(msg.message.into(), WParam(msg.wParam), LParam(msg.lParam));
Self {
window,
message,
time,
point,
}
}
}
pub struct QuitCode(pub usize);
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MessageLoopQueue {
#[default]
Thread,
Window(Window),
Posted,
}
impl MessageLoopQueue {
fn unwrap_or_default(self) -> Window {
match self {
Self::Window(hwnd) => hwnd,
Self::Thread => Window::null(),
Self::Posted => unsafe { Window::from_raw(usize::MAX) }, }
}
}
impl Msg {
#[inline]
pub fn get(queue: MessageLoopQueue, filter: Option<RangeInclusive<u32>>) -> impl Iterator<Item = Result<Msg, Error>> {
GetMessageIterator::Iterating { queue, filter }
}
#[inline]
pub fn peek(
queue: MessageLoopQueue,
filter: Option<RangeInclusive<u32>>,
flags: PeekMessageFlags,
) -> impl Iterator<Item = PeekResult> {
PeekMessageIterator::Iterating { queue, filter, flags }
}
}
pub trait MessageHandler {
type In<'a>;
type Out;
fn handle<'a>(&'a self, f: impl Fn(Self::In<'a>) -> Self::Out) -> Option<LResult>
where
Self: Sized;
}
impl MessageHandler for NcCreateMessage {
type In<'a> = Box<dyn WindowProcedure>;
type Out = bool;
fn handle<'a>(&'a self, f: impl Fn(Self::In<'a>) -> Self::Out) -> Option<LResult> {
let create_struct = unsafe { (self.l.0 as *mut CREATESTRUCTW).as_ref() }.unwrap();
let lp_param = unsafe { (create_struct.lpCreateParams as *mut LpParam).as_mut() }.unwrap();
Some(match f(lp_param.wnd_proc.take().unwrap()) {
true => LResult::TRUE,
false => LResult::FALSE,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CreateMessageResult {
Create,
Destroy,
}
impl MessageHandler for CreateMessage {
type In<'a> = CreateStruct;
type Out = CreateMessageResult;
fn handle<'a>(&'a self, f: impl FnOnce(Self::In<'a>) -> Self::Out) -> Option<LResult> {
let create_struct = unsafe { (self.l.0 as *mut CREATESTRUCTW).as_ref() }.unwrap();
let lp_param = unsafe { (create_struct.lpCreateParams as *mut LpParam).as_mut() }.unwrap();
let boxed = unsafe { Box::from_raw(lp_param) };
Some(LResult(match f(boxed.create_struct) {
CreateMessageResult::Create => 0,
CreateMessageResult::Destroy => -1,
}))
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeyEvent {
pub state: KeyState,
pub key: Key,
pub code: Code,
pub location: Location,
pub modifiers: Modifiers,
pub repeat: bool,
pub key_without_modifiers: Key,
}
impl KeyEvent {
fn new(w: WParam, l: LParam, is_pressed: bool) -> Self {
let mut layouts = LAYOUT_CACHE.lock().unwrap();
const NO_MODS: WindowsModifiers = WindowsModifiers::empty();
let modifiers = layouts.get_agnostic_mods();
let (_, layout) = layouts.get_current_layout();
let lparam_struct = destructure_key_lparam(l);
let vkey = w.0 as VIRTUAL_KEY;
let scancode = if lparam_struct.scancode == 0 {
unsafe { MapVirtualKeyExW(vkey as u32, MAPVK_VK_TO_VSC_EX, layout.hkl as HKL) as u16 }
} else {
new_ex_scancode(lparam_struct.scancode, lparam_struct.extended)
};
let code = scancode_to_code(scancode as u32);
let location = get_location(scancode, layout.hkl as HKL);
let kbd_state = get_kbd_state();
let mods = WindowsModifiers::active_modifiers(&kbd_state);
let mods_without_ctrl = mods.remove_only_ctrl();
let num_lock_on = kbd_state[VK_NUMLOCK as usize] & 1 != 0;
let code_as_key = if mods.contains(WindowsModifiers::CONTROL) {
match code {
Code::NumLock => Some(Key::Named(NamedKey::NumLock)),
Code::Pause => Some(Key::Named(NamedKey::Pause)),
_ => None,
}
} else {
None
};
let preliminary_logical_key = layout.get_key(mods_without_ctrl, num_lock_on, vkey, &code);
let key_is_char = matches!(&preliminary_logical_key, Key::Character(chars) if chars != " ");
let key = if let Some(key) = code_as_key.clone() {
key
} else if is_pressed && key_is_char && !mods.contains(WindowsModifiers::CONTROL) {
preliminary_logical_key
} else {
preliminary_logical_key
};
let key_without_modifiers = if let Some(key) = code_as_key {
key
} else {
match layout.get_key(NO_MODS, false, vkey, &code) {
Key::Dead(k) => {
if let Some(ch) = k {
let mut utf8 = [0; 4];
let s = ch.encode_utf8(&mut utf8);
Key::Character(s.to_string())
} else {
Key::Named(NamedKey::Unidentified)
}
},
key => key,
}
};
Self {
state: if is_pressed { KeyState::Down } else { KeyState::Up },
key,
code,
location,
modifiers,
repeat: lparam_struct.is_repeat,
key_without_modifiers,
}
}
}
impl KeyDownMessage {
pub fn event(&self) -> KeyEvent {
KeyEvent::new(self.w, self.l, true)
}
}
impl KeyUpMessage {
pub fn event(&self) -> KeyEvent {
KeyEvent::new(self.w, self.l, false)
}
}
impl CharMessage {
pub fn char_string(&self) -> String {
char::from_u32(self.w.0 as u32).unwrap_or_default().to_string()
}
}
impl MoveMessage {
pub fn physical_position(&self) -> PhysicalPosition<i32> {
let x = low_word(self.l.0 as u32) as i32;
let y = high_word(self.l.0 as u32) as i32;
(x, y).into()
}
}
impl SizeMessage {
pub fn physical_size(&self) -> PhysicalSize<u32> {
let width = low_word(self.l.0 as u32) as u32;
let height = high_word(self.l.0 as u32) as u32;
(width, height).into()
}
}
impl DpiChangedMessage {
pub fn dpi(&self) -> u32 {
low_word(self.w.0 as u32) as u32
}
pub fn scale_factor(&self) -> f64 {
dpi_to_scale_factor(self.dpi())
}
pub fn suggested_rect(&self) -> Rect {
unsafe { *(self.l.0 as *const RECT) }.into()
}
pub fn update_window_size(&self, window: &Window) {
let suggested_rect = self.suggested_rect();
window
.set_position(
(suggested_rect.left, suggested_rect.top),
(suggested_rect.right - suggested_rect.left, suggested_rect.bottom - suggested_rect.top),
)
.unwrap();
}
}
impl CreateMessage {
pub fn use_dark_mode(&self, window: &Window, enable: bool) {
window.dwm_set_window_attribute(DwmWindowAttribute::UseImmersiveDarkMode(enable));
}
}
impl SettingChangeMessage {
pub fn use_dark_mode(&self, window: &Window, enable: bool) {
window.dwm_set_window_attribute(DwmWindowAttribute::UseImmersiveDarkMode(enable));
}
}