#![cfg(target_os = "windows")]
const INVALID_ACCEL: usize = 0xffffffff;
use crate::error::Error;
use crate::icon::Icon;
use crate::key_handler::KeyHandler;
use crate::rate::UpdateRate;
use crate::Result;
use crate::{CursorStyle, MenuHandle, MenuItem, MenuItemHandle};
use crate::{
InputCallback, Key, KeyRepeat, MouseButton, MouseMode, Scale, ScaleMode, WindowOptions,
};
use crate::{MENU_KEY_ALT, MENU_KEY_CTRL, MENU_KEY_SHIFT, MENU_KEY_WIN};
use crate::buffer_helper;
use crate::mouse_handler;
use std::ffi::OsStr;
use std::mem;
use std::os::raw;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use winapi::shared::basetsd;
use winapi::shared::minwindef::{self, LPARAM, WPARAM};
use winapi::shared::ntdef;
use winapi::shared::windef;
use winapi::um::errhandlingapi;
use winapi::um::fileapi::GetFullPathNameW;
use winapi::um::libloaderapi;
use winapi::um::wingdi;
use winapi::um::winuser::{
self, ICON_BIG, ICON_SMALL, IMAGE_ICON, LR_DEFAULTSIZE, LR_LOADFROMFILE, WM_SETICON,
};
#[repr(C)]
struct BitmapInfo {
pub bmi_header: wingdi::BITMAPINFOHEADER,
pub bmi_colors: [wingdi::RGBQUAD; 3],
}
fn update_key_state(window: &mut Window, wparam: u32, state: bool) {
match wparam & 0x1ff {
0x00B => window.key_handler.set_key_state(Key::Key0, state),
0x002 => window.key_handler.set_key_state(Key::Key1, state),
0x003 => window.key_handler.set_key_state(Key::Key2, state),
0x004 => window.key_handler.set_key_state(Key::Key3, state),
0x005 => window.key_handler.set_key_state(Key::Key4, state),
0x006 => window.key_handler.set_key_state(Key::Key5, state),
0x007 => window.key_handler.set_key_state(Key::Key6, state),
0x008 => window.key_handler.set_key_state(Key::Key7, state),
0x009 => window.key_handler.set_key_state(Key::Key8, state),
0x00A => window.key_handler.set_key_state(Key::Key9, state),
0x01E => window.key_handler.set_key_state(Key::A, state),
0x030 => window.key_handler.set_key_state(Key::B, state),
0x02E => window.key_handler.set_key_state(Key::C, state),
0x020 => window.key_handler.set_key_state(Key::D, state),
0x012 => window.key_handler.set_key_state(Key::E, state),
0x021 => window.key_handler.set_key_state(Key::F, state),
0x022 => window.key_handler.set_key_state(Key::G, state),
0x023 => window.key_handler.set_key_state(Key::H, state),
0x017 => window.key_handler.set_key_state(Key::I, state),
0x024 => window.key_handler.set_key_state(Key::J, state),
0x025 => window.key_handler.set_key_state(Key::K, state),
0x026 => window.key_handler.set_key_state(Key::L, state),
0x032 => window.key_handler.set_key_state(Key::M, state),
0x031 => window.key_handler.set_key_state(Key::N, state),
0x018 => window.key_handler.set_key_state(Key::O, state),
0x019 => window.key_handler.set_key_state(Key::P, state),
0x010 => window.key_handler.set_key_state(Key::Q, state),
0x013 => window.key_handler.set_key_state(Key::R, state),
0x01F => window.key_handler.set_key_state(Key::S, state),
0x014 => window.key_handler.set_key_state(Key::T, state),
0x016 => window.key_handler.set_key_state(Key::U, state),
0x02F => window.key_handler.set_key_state(Key::V, state),
0x011 => window.key_handler.set_key_state(Key::W, state),
0x02D => window.key_handler.set_key_state(Key::X, state),
0x015 => window.key_handler.set_key_state(Key::Y, state),
0x02C => window.key_handler.set_key_state(Key::Z, state),
0x03B => window.key_handler.set_key_state(Key::F1, state),
0x03C => window.key_handler.set_key_state(Key::F2, state),
0x03D => window.key_handler.set_key_state(Key::F3, state),
0x03E => window.key_handler.set_key_state(Key::F4, state),
0x03F => window.key_handler.set_key_state(Key::F5, state),
0x040 => window.key_handler.set_key_state(Key::F6, state),
0x041 => window.key_handler.set_key_state(Key::F7, state),
0x042 => window.key_handler.set_key_state(Key::F8, state),
0x043 => window.key_handler.set_key_state(Key::F9, state),
0x044 => window.key_handler.set_key_state(Key::F10, state),
0x057 => window.key_handler.set_key_state(Key::F11, state),
0x058 => window.key_handler.set_key_state(Key::F12, state),
0x150 => window.key_handler.set_key_state(Key::Down, state),
0x14B => window.key_handler.set_key_state(Key::Left, state),
0x14D => window.key_handler.set_key_state(Key::Right, state),
0x148 => window.key_handler.set_key_state(Key::Up, state),
0x028 => window.key_handler.set_key_state(Key::Apostrophe, state),
0x029 => window.key_handler.set_key_state(Key::Backquote, state),
0x02B => window.key_handler.set_key_state(Key::Backslash, state),
0x033 => window.key_handler.set_key_state(Key::Comma, state),
0x00D => window.key_handler.set_key_state(Key::Equal, state),
0x01A => window.key_handler.set_key_state(Key::LeftBracket, state),
0x00C => window.key_handler.set_key_state(Key::Minus, state),
0x034 => window.key_handler.set_key_state(Key::Period, state),
0x01B => window.key_handler.set_key_state(Key::RightBracket, state),
0x027 => window.key_handler.set_key_state(Key::Semicolon, state),
0x035 => window.key_handler.set_key_state(Key::Slash, state),
0x00E => window.key_handler.set_key_state(Key::Backspace, state),
0x153 => window.key_handler.set_key_state(Key::Delete, state),
0x14F => window.key_handler.set_key_state(Key::End, state),
0x01C => window.key_handler.set_key_state(Key::Enter, state),
0x001 => window.key_handler.set_key_state(Key::Escape, state),
0x147 => window.key_handler.set_key_state(Key::Home, state),
0x152 => window.key_handler.set_key_state(Key::Insert, state),
0x15D => window.key_handler.set_key_state(Key::Menu, state),
0x151 => window.key_handler.set_key_state(Key::PageDown, state),
0x149 => window.key_handler.set_key_state(Key::PageUp, state),
0x045 => window.key_handler.set_key_state(Key::Pause, state),
0x039 => window.key_handler.set_key_state(Key::Space, state),
0x00F => window.key_handler.set_key_state(Key::Tab, state),
0x145 => window.key_handler.set_key_state(Key::NumLock, state),
0x03A => window.key_handler.set_key_state(Key::CapsLock, state),
0x046 => window.key_handler.set_key_state(Key::ScrollLock, state),
0x02A => window.key_handler.set_key_state(Key::LeftShift, state),
0x036 => window.key_handler.set_key_state(Key::RightShift, state),
0x01D => window.key_handler.set_key_state(Key::LeftCtrl, state),
0x11D => window.key_handler.set_key_state(Key::RightCtrl, state),
0x052 => window.key_handler.set_key_state(Key::NumPad0, state),
0x04F => window.key_handler.set_key_state(Key::NumPad1, state),
0x050 => window.key_handler.set_key_state(Key::NumPad2, state),
0x051 => window.key_handler.set_key_state(Key::NumPad3, state),
0x04B => window.key_handler.set_key_state(Key::NumPad4, state),
0x04C => window.key_handler.set_key_state(Key::NumPad5, state),
0x04D => window.key_handler.set_key_state(Key::NumPad6, state),
0x047 => window.key_handler.set_key_state(Key::NumPad7, state),
0x048 => window.key_handler.set_key_state(Key::NumPad8, state),
0x049 => window.key_handler.set_key_state(Key::NumPad9, state),
0x053 => window.key_handler.set_key_state(Key::NumPadDot, state),
0x135 => window.key_handler.set_key_state(Key::NumPadSlash, state),
0x037 => window.key_handler.set_key_state(Key::NumPadAsterisk, state),
0x04A => window.key_handler.set_key_state(Key::NumPadMinus, state),
0x04E => window.key_handler.set_key_state(Key::NumPadPlus, state),
0x11C => window.key_handler.set_key_state(Key::NumPadEnter, state),
_ => (),
}
}
fn char_down(window: &mut Window, code_point: u32) {
if let Some(ref mut callback) = window.key_handler.key_callback {
callback.add_char(code_point);
}
}
#[cfg(target_arch = "x86_64")]
unsafe fn set_window_long(window: windef::HWND, data: basetsd::LONG_PTR) -> basetsd::LONG_PTR {
winuser::SetWindowLongPtrW(window, winuser::GWLP_USERDATA, data)
}
#[cfg(target_arch = "x86_64")]
unsafe fn get_window_long(window: windef::HWND) -> basetsd::LONG_PTR {
winuser::GetWindowLongPtrW(window, winuser::GWLP_USERDATA)
}
#[cfg(target_arch = "x86")]
unsafe fn set_window_long(window: windef::HWND, data: ntdef::LONG) -> ntdef::LONG {
winuser::SetWindowLongW(window, winuser::GWLP_USERDATA, data)
}
#[cfg(target_arch = "x86")]
unsafe fn get_window_long(window: windef::HWND) -> ntdef::LONG {
winuser::GetWindowLongW(window, winuser::GWLP_USERDATA)
}
unsafe extern "system" fn wnd_proc(
window: windef::HWND,
msg: minwindef::UINT,
wparam: minwindef::WPARAM,
lparam: minwindef::LPARAM,
) -> minwindef::LRESULT {
let user_data = get_window_long(window);
if user_data == 0 {
return winuser::DefWindowProcW(window, msg, wparam, lparam);
}
let mut wnd: &mut Window = mem::transmute(user_data);
match msg {
winuser::WM_MOUSEWHEEL => {
let scroll = ((((wparam as u32) >> 16) & 0xffff) as i16) as f32 * 0.1;
wnd.mouse.scroll = scroll;
}
winuser::WM_SETCURSOR => {
if winapi::shared::minwindef::LOWORD(lparam as u32) == winuser::HTCLIENT as u16 {
winuser::SetCursor(wnd.cursors[wnd.cursor as usize]);
return 1;
}
}
winuser::WM_KEYDOWN => {
update_key_state(wnd, (lparam as u32) >> 16, true);
return 0;
}
winuser::WM_CHAR => {
char_down(wnd, wparam as u32);
}
winuser::WM_SYSCHAR => {
char_down(wnd, wparam as u32);
}
winuser::WM_LBUTTONDOWN => wnd.mouse.state[0] = true,
winuser::WM_LBUTTONUP => wnd.mouse.state[0] = false,
winuser::WM_MOUSEMOVE => {
let button_checks = [
winuser::MK_LBUTTON,
winuser::MK_MBUTTON,
winuser::MK_RBUTTON,
];
for i in 0..3 {
if (wparam & button_checks[i]) == button_checks[i] {
wnd.mouse.state[i] = true;
} else {
wnd.mouse.state[i] = false;
}
}
}
winuser::WM_MBUTTONDOWN => wnd.mouse.state[1] = true,
winuser::WM_MBUTTONUP => wnd.mouse.state[1] = false,
winuser::WM_RBUTTONDOWN => wnd.mouse.state[2] = true,
winuser::WM_RBUTTONUP => wnd.mouse.state[2] = false,
winuser::WM_CLOSE => {
wnd.is_open = false;
}
winuser::WM_KEYUP => {
update_key_state(wnd, (lparam as u32) >> 16, false);
return 0;
}
winuser::WM_COMMAND => {
if lparam == 0 {
wnd.accel_key = (wparam & 0xffff) as usize;
}
}
winuser::WM_SIZE => {
let width = (lparam as u32) & 0xffff;
let height = ((lparam as u32) >> 16) & 0xffff;
wnd.width = width as i32;
wnd.height = height as i32;
}
winuser::WM_PAINT => {
if wnd.draw_params.buffer.is_null() {
return winuser::DefWindowProcW(window, msg, wparam, lparam);
}
let mut bitmap_info: BitmapInfo = mem::zeroed();
bitmap_info.bmi_header.biSize = mem::size_of::<wingdi::BITMAPINFOHEADER>() as u32;
bitmap_info.bmi_header.biPlanes = 1;
bitmap_info.bmi_header.biBitCount = 32;
bitmap_info.bmi_header.biCompression = wingdi::BI_BITFIELDS;
bitmap_info.bmi_header.biWidth = wnd.draw_params.buffer_width as i32;
bitmap_info.bmi_header.biHeight = -(wnd.draw_params.buffer_height as i32);
bitmap_info.bmi_colors[0].rgbRed = 0xff;
bitmap_info.bmi_colors[1].rgbGreen = 0xff;
bitmap_info.bmi_colors[2].rgbBlue = 0xff;
let buffer_width = wnd.draw_params.buffer_width as i32;
let buffer_height = wnd.draw_params.buffer_height as i32;
let window_width = wnd.width as i32;
let window_height = wnd.height as i32;
let mut new_height = window_height;
let mut new_width = window_width;
let mut x_offset = 0;
let mut y_offset = 0;
let dc = wnd.dc.unwrap();
wingdi::SelectObject(dc, wnd.clear_brush as *mut winapi::ctypes::c_void);
match wnd.draw_params.scale_mode {
ScaleMode::AspectRatioStretch => {
let buffer_aspect = buffer_width as f32 / buffer_height as f32;
let win_aspect = window_width as f32 / window_height as f32;
if buffer_aspect > win_aspect {
new_height = (window_width as f32 / buffer_aspect) as i32;
y_offset = (new_height - window_height) / -2;
if y_offset != 0 {
wingdi::Rectangle(dc, 0, 0, window_width, y_offset);
wingdi::Rectangle(
dc,
0,
y_offset + new_height,
window_width,
window_height,
);
}
} else {
new_width = (window_height as f32 * buffer_aspect) as i32;
x_offset = (new_width - window_width) / -2;
if x_offset != 0 {
wingdi::Rectangle(dc, 0, 0, x_offset, window_height);
wingdi::Rectangle(
dc,
x_offset + new_width,
0,
window_width,
window_height,
);
}
}
}
ScaleMode::Center => {
new_width = buffer_width;
new_height = buffer_height;
if buffer_height > window_height {
y_offset = -(buffer_height - window_height) / 2;
} else {
y_offset = (window_height - buffer_height) / 2;
}
if buffer_width > window_width {
x_offset = -(buffer_width - window_width) / 2;
} else {
x_offset = (window_width - buffer_width) / 2;
}
if y_offset > 0 {
wingdi::Rectangle(dc, 0, 0, window_width, y_offset);
wingdi::Rectangle(
dc,
0,
y_offset + new_height,
window_width,
window_height,
);
}
if x_offset > 0 {
wingdi::Rectangle(dc, 0, y_offset, x_offset, buffer_height + y_offset);
wingdi::Rectangle(
dc,
x_offset + buffer_width,
y_offset,
window_width,
buffer_height + y_offset,
);
}
}
ScaleMode::UpperLeft => {
new_width = buffer_width;
new_height = buffer_height;
if buffer_width < window_width {
wingdi::Rectangle(dc, buffer_width, 0, window_width, window_height);
}
if buffer_height < window_height {
wingdi::Rectangle(dc, 0, buffer_height, window_width, window_height);
}
}
_ => (),
}
wingdi::StretchDIBits(
dc,
x_offset,
y_offset,
new_width,
new_height,
0,
0,
wnd.draw_params.buffer_width as i32,
wnd.draw_params.buffer_height as i32,
mem::transmute(wnd.draw_params.buffer),
mem::transmute(&bitmap_info),
wingdi::DIB_RGB_COLORS,
wingdi::SRCCOPY,
);
winuser::ValidateRect(window, ptr::null_mut());
return 0;
}
_ => (),
}
winuser::DefWindowProcW(window, msg, wparam, lparam)
}
fn to_wstring(str: &str) -> Vec<u16> {
let v: Vec<u16> = OsStr::new(str)
.encode_wide()
.chain(Some(0).into_iter())
.collect();
v
}
#[derive(Default)]
struct MouseData {
pub x: f32,
pub y: f32,
pub state: [bool; 8],
pub scroll: f32,
}
struct DrawParameters {
buffer: *const u32,
buffer_width: u32,
buffer_height: u32,
scale_mode: ScaleMode,
}
impl Default for DrawParameters {
fn default() -> Self {
DrawParameters {
buffer: std::ptr::null(),
buffer_width: 0,
buffer_height: 0,
scale_mode: ScaleMode::Stretch,
}
}
}
pub struct Window {
mouse: MouseData,
dc: Option<windef::HDC>,
window: Option<windef::HWND>,
clear_brush: windef::HBRUSH,
is_open: bool,
scale_factor: i32,
width: i32,
height: i32,
menus: Vec<Menu>,
key_handler: KeyHandler,
update_rate: UpdateRate,
accel_table: windef::HACCEL,
accel_key: usize,
cursor: CursorStyle,
cursors: [windef::HCURSOR; 8],
draw_params: DrawParameters,
}
unsafe impl raw_window_handle::HasRawWindowHandle for Window {
fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
let mut handle = raw_window_handle::Win32Handle::empty();
handle.hwnd = self.window.unwrap() as *mut raw::c_void;
handle.hinstance =
unsafe { libloaderapi::GetModuleHandleA(ptr::null()) } as *mut raw::c_void;
raw_window_handle::RawWindowHandle::Win32(handle)
}
}
impl Window {
fn open_window(
name: &str,
width: usize,
height: usize,
opts: WindowOptions,
scale_factor: i32,
) -> Option<windef::HWND> {
unsafe {
let class_name = to_wstring("minifb_window");
let class = winuser::WNDCLASSW {
style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC,
lpfnWndProc: Some(wnd_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: libloaderapi::GetModuleHandleA(ptr::null()),
hIcon: ptr::null_mut(),
hCursor: winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_ARROW),
hbrBackground: ptr::null_mut(),
lpszMenuName: ptr::null(),
lpszClassName: class_name.as_ptr(),
};
if winuser::RegisterClassW(&class) == 0 {
if errhandlingapi::GetLastError() as u32 != 1410 {
println!(
"Unable to register class, error {}",
errhandlingapi::GetLastError() as u32
);
return None;
}
}
let window_name = to_wstring(name);
let mut flags = 0;
if opts.title {
flags |= winuser::WS_OVERLAPPEDWINDOW as u32;
}
if opts.resize {
flags |= winuser::WS_THICKFRAME as u32 | winuser::WS_MAXIMIZEBOX as u32;
} else {
flags &= !winuser::WS_MAXIMIZEBOX;
flags &= !winuser::WS_THICKFRAME;
}
if opts.borderless {
flags &= !winuser::WS_THICKFRAME;
}
if opts.transparency {
flags &= winuser::WS_EX_LAYERED;
}
if opts.none {
flags = winuser::WS_VISIBLE | winuser::WS_POPUP;
}
let new_width = width * scale_factor as usize;
let new_height = height * scale_factor as usize;
let mut rect = windef::RECT {
left: 0,
right: new_width as ntdef::LONG,
top: 0,
bottom: new_height as ntdef::LONG,
};
winuser::AdjustWindowRect(&mut rect, flags, 0);
rect.right -= rect.left;
rect.bottom -= rect.top;
let handle = winuser::CreateWindowExW(
0,
class_name.as_ptr(),
window_name.as_ptr(),
flags,
winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
rect.right,
rect.bottom,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
);
if handle.is_null() {
println!(
"Unable to create window, error {}",
errhandlingapi::GetLastError() as u32
);
return None;
}
winuser::ShowWindow(handle, winuser::SW_NORMAL);
Some(handle)
}
}
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window> {
unsafe {
let scale_factor = Self::get_scale_factor(width, height, opts.scale);
let handle = Self::open_window(name, width, height, opts, scale_factor);
if handle.is_none() {
return Err(Error::WindowCreate("Unable to create Window".to_owned()));
}
let window = Window {
mouse: MouseData::default(),
dc: Some(winuser::GetDC(handle.unwrap())),
window: Some(handle.unwrap()),
key_handler: KeyHandler::new(),
update_rate: UpdateRate::new(),
is_open: true,
scale_factor,
width: (width * scale_factor as usize) as i32,
height: (height * scale_factor as usize) as i32,
menus: Vec::new(),
accel_table: ptr::null_mut(),
accel_key: INVALID_ACCEL,
cursor: CursorStyle::Arrow,
clear_brush: wingdi::CreateSolidBrush(0),
cursors: [
winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_ARROW),
winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_IBEAM),
winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_CROSS),
winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_HAND),
winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_HAND),
winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_SIZEWE),
winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_SIZENS),
winuser::LoadCursorW(ptr::null_mut(), winuser::IDC_SIZEALL),
],
draw_params: DrawParameters {
scale_mode: opts.scale_mode,
..DrawParameters::default()
},
};
if opts.topmost {
window.topmost(true)
}
Ok(window)
}
}
#[inline]
pub fn set_title(&mut self, title: &str) {
unsafe {
let title_name = to_wstring(title);
winuser::SetWindowTextW(self.window.unwrap(), title_name.as_ptr());
}
}
#[inline]
pub fn set_icon(&mut self, icon: Icon) {
unsafe {
if let Icon::Path(s_pointer) = icon {
let mut buffer: Vec<u16> = Vec::new();
let return_value =
GetFullPathNameW(s_pointer, 0, buffer.as_mut_ptr(), std::ptr::null_mut());
buffer.reserve(return_value as usize);
let _ = GetFullPathNameW(
s_pointer,
return_value,
buffer.as_mut_ptr(),
std::ptr::null_mut(),
);
let path = buffer.as_ptr();
let icon = winapi::um::winuser::LoadImageW(
std::ptr::null_mut(),
path,
IMAGE_ICON,
0,
0,
LR_DEFAULTSIZE | LR_LOADFROMFILE,
);
if let Some(handle) = self.window {
winapi::um::winuser::SendMessageW(
handle,
WM_SETICON,
ICON_SMALL as WPARAM,
icon as LPARAM,
);
winapi::um::winuser::SendMessageW(
handle,
WM_SETICON,
ICON_BIG as WPARAM,
icon as LPARAM,
);
}
}
}
}
#[inline]
pub fn get_window_handle(&self) -> *mut raw::c_void {
self.window.unwrap() as *mut raw::c_void
}
#[inline]
pub fn set_position(&mut self, x: isize, y: isize) {
unsafe {
winuser::SetWindowPos(
self.window.unwrap(),
ptr::null_mut(),
x as i32,
y as i32,
0,
0,
winuser::SWP_SHOWWINDOW | winuser::SWP_NOSIZE,
);
}
}
#[inline]
pub fn get_position(&self) -> (isize, isize) {
let (mut x, mut y) = (0, 0);
unsafe {
let mut rect = windef::RECT {
left: 0,
right: 0,
top: 0,
bottom: 0,
};
if winuser::GetWindowRect(self.window.unwrap(), &mut rect) != 0 {
x = rect.left;
y = rect.top;
}
}
(x as isize, y as isize)
}
#[inline]
pub fn topmost(&self, topmost: bool) {
unsafe {
winuser::SetWindowPos(
self.window.unwrap(),
if topmost {
winuser::HWND_TOPMOST
} else {
winuser::HWND_TOP
},
0,
0,
0,
0,
winuser::SWP_SHOWWINDOW | winuser::SWP_NOSIZE | winuser::SWP_NOMOVE,
)
};
}
#[inline]
pub fn get_size(&self) -> (usize, usize) {
(self.width as usize, self.height as usize)
}
pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> {
let s = self.scale_factor as f32;
let w = self.width as f32;
let h = self.height as f32;
mouse_handler::get_pos(mode, self.mouse.x, self.mouse.y, s, w, h)
}
pub fn get_unscaled_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> {
let w = self.width as f32;
let h = self.height as f32;
mouse_handler::get_pos(mode, self.mouse.x, self.mouse.y, 1.0, w, h)
}
pub fn get_mouse_down(&self, button: MouseButton) -> bool {
match button {
MouseButton::Left => self.mouse.state[0],
MouseButton::Middle => self.mouse.state[1],
MouseButton::Right => self.mouse.state[2],
}
}
pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> {
if self.mouse.scroll.abs() > 0.0 {
Some((0.0, self.mouse.scroll))
} else {
None
}
}
#[inline]
pub fn set_cursor_style(&mut self, cursor: CursorStyle) {
self.cursor = cursor;
}
#[inline]
pub fn set_rate(&mut self, rate: Option<std::time::Duration>) {
self.update_rate.set_rate(rate);
}
#[inline]
pub fn update_rate(&mut self) {
self.update_rate.update();
}
#[inline]
pub fn get_keys(&self) -> Vec<Key> {
self.key_handler.get_keys()
}
#[inline]
pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Vec<Key> {
self.key_handler.get_keys_pressed(repeat)
}
#[inline]
pub fn get_keys_released(&self) -> Vec<Key> {
self.key_handler.get_keys_released()
}
#[inline]
pub fn is_key_down(&self, key: Key) -> bool {
self.key_handler.is_key_down(key)
}
#[inline]
pub fn set_input_callback(&mut self, callback: Box<dyn InputCallback>) {
self.key_handler.set_input_callback(callback)
}
#[inline]
pub fn set_key_repeat_delay(&mut self, delay: f32) {
self.key_handler.set_key_repeat_delay(delay)
}
#[inline]
pub fn set_key_repeat_rate(&mut self, rate: f32) {
self.key_handler.set_key_repeat_rate(rate)
}
#[inline]
pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool {
self.key_handler.is_key_pressed(key, repeat)
}
#[inline]
pub fn is_key_released(&self, key: Key) -> bool {
self.key_handler.is_key_released(key)
}
#[inline]
pub fn is_open(&self) -> bool {
self.is_open
}
fn generic_update(&mut self, window: windef::HWND) {
unsafe {
let mut point: windef::POINT = mem::zeroed();
winuser::GetCursorPos(&mut point);
winuser::ScreenToClient(window, &mut point);
self.mouse.x = point.x as f32;
self.mouse.y = point.y as f32;
self.mouse.scroll = 0.0;
self.key_handler.update();
set_window_long(window, mem::transmute(self));
}
}
fn message_loop(&self, _window: windef::HWND) {
unsafe {
let mut msg = mem::zeroed();
while winuser::PeekMessageW(&mut msg, std::ptr::null_mut(), 0, 0, winuser::PM_REMOVE)
!= 0
{
let acc_condition = winuser::TranslateAcceleratorW(
msg.hwnd,
mem::transmute(self.accel_table),
&mut msg,
) == 0;
if self.accel_table.is_null() || acc_condition {
winuser::TranslateMessage(&msg);
winuser::DispatchMessageW(&msg);
}
}
}
}
#[allow(clippy::identity_op)]
pub fn set_background_color(&mut self, color: u32) {
unsafe {
wingdi::DeleteObject(self.clear_brush as *mut winapi::ctypes::c_void);
let r = (color >> 16) & 0xff;
let g = (color >> 8) & 0xff;
let b = (color >> 0) & 0xff;
self.clear_brush = wingdi::CreateSolidBrush((b << 16) | (g << 8) | r);
}
}
pub fn set_cursor_visibility(&mut self, visibility: bool) {
unsafe {
winuser::ShowCursor(visibility as i32);
}
}
pub fn update_with_buffer_stride(
&mut self,
buffer: &[u32],
buf_width: usize,
buf_height: usize,
buf_stride: usize,
) -> Result<()> {
let window = self.window.unwrap();
Self::generic_update(self, window);
buffer_helper::check_buffer_size(buf_width, buf_height, buf_stride, buffer)?;
self.draw_params.buffer = buffer.as_ptr();
self.draw_params.buffer_width = buf_width as u32;
self.draw_params.buffer_height = buf_height as u32;
unsafe {
winuser::InvalidateRect(window, ptr::null_mut(), minwindef::TRUE);
}
Self::message_loop(self, window);
Ok(())
}
pub fn update(&mut self) {
let window = self.window.unwrap();
Self::generic_update(self, window);
Self::message_loop(self, window);
}
#[inline]
pub fn is_active(&mut self) -> bool {
match self.window {
Some(hwnd) => {
let active = unsafe { winapi::um::winuser::GetActiveWindow() };
!active.is_null() && active == hwnd
}
None => false,
}
}
unsafe fn get_scale_factor(width: usize, height: usize, scale: Scale) -> i32 {
let factor: i32 = match scale {
Scale::X1 => 1,
Scale::X2 => 2,
Scale::X4 => 4,
Scale::X8 => 8,
Scale::X16 => 16,
Scale::X32 => 32,
Scale::FitScreen => {
let screen_x = winuser::GetSystemMetrics(winuser::SM_CXSCREEN) as i32;
let screen_y = winuser::GetSystemMetrics(winuser::SM_CYSCREEN) as i32;
let mut scale = 1i32;
loop {
let w = width as i32 * (scale + 1);
let h = height as i32 * (scale + 1);
if w > screen_x || h > screen_y {
break;
}
scale *= 2;
}
scale
}
};
factor
}
unsafe fn adjust_window_size_for_menu(handle: windef::HWND) {
let mut rect: windef::RECT = mem::zeroed();
let menu_height = winuser::GetSystemMetrics(winuser::SM_CYMENU);
winuser::GetWindowRect(handle, &mut rect);
winuser::MoveWindow(
handle,
rect.left,
rect.top,
rect.right - rect.left,
(rect.bottom - rect.top) + menu_height,
1,
);
}
unsafe fn set_accel_table(&mut self) {
let mut temp_accel_table = Vec::<winuser::ACCEL>::new();
for menu in self.menus.iter() {
for item in menu.accel_table.iter() {
temp_accel_table.push(*item);
}
}
if !self.accel_table.is_null() {
winuser::DestroyAcceleratorTable(self.accel_table);
}
self.accel_table = winuser::CreateAcceleratorTableW(
temp_accel_table.as_mut_ptr(),
temp_accel_table.len() as i32,
);
}
pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle {
unsafe {
let window = self.window.unwrap();
let mut main_menu = winuser::GetMenu(window);
if main_menu.is_null() {
main_menu = winuser::CreateMenu();
winuser::SetMenu(window, main_menu);
Self::adjust_window_size_for_menu(window);
}
winuser::AppendMenuW(
main_menu,
0x10,
menu.menu_handle as basetsd::UINT_PTR,
menu.name.as_ptr(),
);
self.menus.push(menu.clone());
self.set_accel_table();
winuser::DrawMenuBar(window);
}
MenuHandle(menu.menu_handle as u64)
}
pub fn remove_menu(&mut self, handle: MenuHandle) {
let window = self.window.unwrap();
let main_menu = unsafe { winuser::GetMenu(window) };
for i in 0..self.menus.len() {
if self.menus[i].menu_handle == handle.0 as windef::HMENU {
unsafe {
let _t = winuser::RemoveMenu(main_menu, i as minwindef::UINT, 0);
winuser::DrawMenuBar(self.window.unwrap());
}
self.menus.swap_remove(i);
return;
}
}
}
pub fn is_menu_pressed(&mut self) -> Option<usize> {
if self.accel_key == INVALID_ACCEL {
None
} else {
let t = self.accel_key;
self.accel_key = INVALID_ACCEL;
Some(t)
}
}
}
#[derive(Clone)]
pub struct Menu {
menu_handle: windef::HMENU,
name: Vec<u16>,
accel_table: Vec<winuser::ACCEL>,
}
impl Menu {
pub fn new(name: &str) -> Result<Menu> {
unsafe {
Ok(Menu {
menu_handle: winuser::CreatePopupMenu(),
name: to_wstring(name),
accel_table: Vec::new(),
})
}
}
fn map_key_to_vk_accel(key: Key) -> (raw::c_int, &'static str) {
match key {
Key::Key0 => (0x30, "0"),
Key::Key1 => (0x31, "1"),
Key::Key2 => (0x32, "2"),
Key::Key3 => (0x33, "3"),
Key::Key4 => (0x34, "4"),
Key::Key5 => (0x35, "5"),
Key::Key6 => (0x36, "6"),
Key::Key7 => (0x37, "7"),
Key::Key8 => (0x38, "8"),
Key::Key9 => (0x39, "9"),
Key::A => (0x41, "a"),
Key::B => (0x42, "b"),
Key::C => (0x43, "c"),
Key::D => (0x44, "d"),
Key::E => (0x45, "e"),
Key::F => (0x46, "f"),
Key::G => (0x47, "g"),
Key::H => (0x48, "h"),
Key::I => (0x49, "i"),
Key::J => (0x4a, "j"),
Key::K => (0x4b, "k"),
Key::L => (0x4c, "l"),
Key::M => (0x4d, "m"),
Key::N => (0x4e, "n"),
Key::O => (0x4f, "o"),
Key::P => (0x50, "p"),
Key::Q => (0x51, "q"),
Key::R => (0x52, "r"),
Key::S => (0x53, "s"),
Key::T => (0x54, "t"),
Key::U => (0x55, "u"),
Key::V => (0x56, "v"),
Key::W => (0x57, "w"),
Key::X => (0x58, "x"),
Key::Y => (0x59, "y"),
Key::Z => (0x5a, "z"),
Key::F1 => (winuser::VK_F1, "F1"),
Key::F2 => (winuser::VK_F2, "F2"),
Key::F3 => (winuser::VK_F3, "F3"),
Key::F4 => (winuser::VK_F4, "F4"),
Key::F5 => (winuser::VK_F5, "F5"),
Key::F6 => (winuser::VK_F6, "F6"),
Key::F7 => (winuser::VK_F7, "F7"),
Key::F8 => (winuser::VK_F8, "F8"),
Key::F9 => (winuser::VK_F9, "F9"),
Key::F10 => (winuser::VK_F10, "F10"),
Key::F11 => (winuser::VK_F11, "F11"),
Key::F12 => (winuser::VK_F12, "F12"),
Key::F13 => (winuser::VK_F13, "F14"),
Key::F14 => (winuser::VK_F14, "F14"),
Key::F15 => (winuser::VK_F15, "F15"),
Key::Down => (winuser::VK_DOWN, "Down"),
Key::Left => (winuser::VK_LEFT, "Left"),
Key::Right => (winuser::VK_RIGHT, "Right"),
Key::Up => (winuser::VK_UP, "Up"),
Key::Backslash => (winuser::VK_OEM_102, "Backslash"),
Key::Comma => (winuser::VK_OEM_COMMA, ","),
Key::Minus => (winuser::VK_OEM_MINUS, "-"),
Key::Period => (winuser::VK_OEM_PERIOD, "."),
Key::Backspace => (winuser::VK_BACK, "Back"),
Key::Delete => (winuser::VK_DELETE, "Delete"),
Key::End => (winuser::VK_END, "End"),
Key::Enter => (winuser::VK_RETURN, "Enter"),
Key::Escape => (winuser::VK_ESCAPE, "Esc"),
Key::Home => (winuser::VK_HOME, "Home"),
Key::Insert => (winuser::VK_INSERT, "Insert"),
Key::Menu => (winuser::VK_MENU, "Menu"),
Key::PageDown => (winuser::VK_NEXT, "PageDown"),
Key::PageUp => (winuser::VK_PRIOR, "PageUp"),
Key::Pause => (winuser::VK_PAUSE, "Pause"),
Key::Space => (winuser::VK_SPACE, "Space"),
Key::Tab => (winuser::VK_TAB, "Tab"),
Key::NumLock => (winuser::VK_NUMLOCK, "NumLock"),
Key::CapsLock => (winuser::VK_CAPITAL, "CapsLock"),
Key::ScrollLock => (winuser::VK_SCROLL, "Scroll"),
Key::LeftShift => (winuser::VK_LSHIFT, "LeftShift"),
Key::RightShift => (winuser::VK_RSHIFT, "RightShift"),
Key::LeftCtrl => (winuser::VK_CONTROL, "Ctrl"),
Key::RightCtrl => (winuser::VK_CONTROL, "Ctrl"),
Key::NumPad0 => (winuser::VK_NUMPAD0, "NumPad0"),
Key::NumPad1 => (winuser::VK_NUMPAD1, "NumPad1"),
Key::NumPad2 => (winuser::VK_NUMPAD2, "NumPad2"),
Key::NumPad3 => (winuser::VK_NUMPAD3, "NumPad3"),
Key::NumPad4 => (winuser::VK_NUMPAD4, "NumPad4"),
Key::NumPad5 => (winuser::VK_NUMPAD5, "NumPad5"),
Key::NumPad6 => (winuser::VK_NUMPAD6, "NumPad6"),
Key::NumPad7 => (winuser::VK_NUMPAD7, "NumPad7"),
Key::NumPad8 => (winuser::VK_NUMPAD8, "NumPad8"),
Key::NumPad9 => (winuser::VK_NUMPAD9, "NumPad9"),
Key::LeftAlt => (winuser::VK_MENU, "Alt"),
Key::RightAlt => (winuser::VK_MENU, "Alt"),
Key::LeftSuper => (winuser::VK_LWIN, "LeftWin"),
Key::RightSuper => (winuser::VK_RWIN, "RightWin"),
_ => (0, "Unsupported"),
}
}
pub fn add_sub_menu(&mut self, name: &str, menu: &Menu) {
unsafe {
let menu_name = to_wstring(name);
winuser::AppendMenuW(
self.menu_handle,
0x10,
menu.menu_handle as basetsd::UINT_PTR,
menu_name.as_ptr(),
);
self.accel_table
.extend_from_slice(menu.accel_table.as_slice());
}
}
fn format_name(menu_item: &MenuItem, key_name: &'static str) -> String {
let mut name = menu_item.label.clone();
name.push('\t');
if (menu_item.modifier & MENU_KEY_WIN) == MENU_KEY_WIN {
name.push_str("Win-");
}
if (menu_item.modifier & MENU_KEY_SHIFT) == MENU_KEY_SHIFT {
name.push_str("Shift-");
}
if (menu_item.modifier & MENU_KEY_CTRL) == MENU_KEY_CTRL {
name.push_str("Ctrl-");
}
if (menu_item.modifier & MENU_KEY_ALT) == MENU_KEY_ALT {
name.push_str("Alt-");
}
name.push_str(key_name);
name
}
fn is_key_virtual_range(_key: raw::c_int) -> u32 {
1
}
fn get_virt_key(menu_item: &MenuItem, key: raw::c_int) -> u32 {
let mut virt = Self::is_key_virtual_range(key);
if (menu_item.modifier & MENU_KEY_ALT) == MENU_KEY_ALT {
virt |= 0x10;
}
if (menu_item.modifier & MENU_KEY_CTRL) == MENU_KEY_CTRL {
virt |= 0x8;
}
if (menu_item.modifier & MENU_KEY_SHIFT) == MENU_KEY_SHIFT {
virt |= 0x4;
}
virt
}
fn add_accel(&mut self, vk: raw::c_int, menu_item: &MenuItem) {
let vk_accel = Self::map_key_to_vk_accel(menu_item.key);
let virt = Self::get_virt_key(menu_item, vk);
let accel = winuser::ACCEL {
fVirt: virt as minwindef::BYTE,
cmd: menu_item.id as minwindef::WORD,
key: vk_accel.0 as minwindef::WORD,
};
self.accel_table.push(accel);
}
pub fn add_menu_item(&mut self, menu_item: &MenuItem) -> MenuItemHandle {
let vk_accel = Self::map_key_to_vk_accel(menu_item.key);
unsafe {
match vk_accel.0 {
0 => {
let item_name = to_wstring(&menu_item.label);
winuser::AppendMenuW(
self.menu_handle,
0x10,
menu_item.id as basetsd::UINT_PTR,
item_name.as_ptr(),
);
}
_ => {
let menu_name = Self::format_name(menu_item, vk_accel.1);
let w_name = to_wstring(&menu_name);
winuser::AppendMenuW(
self.menu_handle,
0x10,
menu_item.id as basetsd::UINT_PTR,
w_name.as_ptr(),
);
self.add_accel(vk_accel.0, menu_item);
}
}
}
MenuItemHandle(0)
}
pub fn remove_item(&mut self, _item: &MenuItemHandle) {
panic!("remove item hasn't been implemented");
}
}
impl Drop for Window {
fn drop(&mut self) {
unsafe {
if self.dc.is_some() {
winuser::ReleaseDC(self.window.unwrap(), self.dc.unwrap());
}
if self.window.is_some() {
winuser::DestroyWindow(self.window.unwrap());
}
}
}
}