use std::{ffi::c_void, io::Error};
use crate::types::*;
use crate::utils;
use crate::key_translation;
use windows::{
core::PCWSTR,
Win32::{
Foundation::{CloseHandle, GENERIC_WRITE, HANDLE},
Storage::FileSystem::{
CreateFileW, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_READ, FILE_SHARE_WRITE,
OPEN_EXISTING,
},
System::IO::DeviceIoControl,
},
};
const RAZER_IOCTL_CODE: u32 = 0x88883020;
#[derive(Debug)]
pub struct RazerDevice {
handle: HANDLE,
}
unsafe impl Send for RazerDevice {}
unsafe impl Sync for RazerDevice {}
impl RazerDevice {
pub fn new() -> Result<Self, Error> {
let handle = Self::open_device_handle()?;
Ok(RazerDevice { handle })
}
fn open_device_handle() -> Result<HANDLE, Error> {
let name = utils::find_sym_link("\\GLOBAL??", "RZCONTROL").map_err(|e| {
Error::new(
e.kind(),
format!("Error while trying to find symbolic link: {}", e),
)
})?;
let sym_link = format!("\\\\?\\{}", name);
let wide_name: Vec<u16> = sym_link.encode_utf16().chain(Some(0)).collect();
unsafe {
let handle = CreateFileW(
PCWSTR(wide_name.as_ptr()),
GENERIC_WRITE.0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
None,
OPEN_EXISTING,
FILE_FLAGS_AND_ATTRIBUTES(0),
None,
);
match handle {
Ok(h) if !h.is_invalid() => Ok(h),
_ => Err(Error::last_os_error()),
}
}
}
fn reconnect(&mut self) -> Result<(), Error> {
if !self.handle.is_invalid() {
unsafe { CloseHandle(self.handle).ok() };
}
self.handle = Self::open_device_handle()?;
Ok(())
}
pub fn mouse_move(&mut self, x: i32, y: i32) -> Result<(), Error> {
let mut control = RzControl::new(Type::Mouse);
unsafe {
let mouse_data = control.mouse_data_mut();
mouse_data.x = x;
mouse_data.y = y;
mouse_data.absolute_coord = 0;
mouse_data.movement = 1;
mouse_data.button_flags = MouseButtons::new();
}
self.send_control(&control)
}
pub fn mouse_click(&mut self, button: MouseButton, down: bool) -> Result<(), Error> {
let mut control = RzControl::new(Type::Mouse);
unsafe {
let mouse_data = control.mouse_data_mut();
mouse_data.button_flags = MouseButtons::new();
match (button, down) {
(MouseButton::Left, true) => mouse_data.button_flags.set_flag(L_BUTTON_DOWN, true),
(MouseButton::Left, false) => mouse_data.button_flags.set_flag(L_BUTTON_UP, true),
(MouseButton::Right, true) => mouse_data.button_flags.set_flag(R_BUTTON_DOWN, true),
(MouseButton::Right, false) => mouse_data.button_flags.set_flag(R_BUTTON_UP, true),
(MouseButton::Middle, true) => mouse_data.button_flags.set_flag(M_BUTTON_DOWN, true),
(MouseButton::Middle, false) => mouse_data.button_flags.set_flag(M_BUTTON_UP, true),
(MouseButton::X1, true) => mouse_data.button_flags.set_flag(X_BUTTON1_DOWN, true),
(MouseButton::X1, false) => mouse_data.button_flags.set_flag(X_BUTTON1_UP, true),
(MouseButton::X2, true) => mouse_data.button_flags.set_flag(X_BUTTON2_DOWN, true),
(MouseButton::X2, false) => mouse_data.button_flags.set_flag(X_BUTTON2_UP, true),
}
}
self.send_control(&control)
}
pub fn key_down(&mut self, vk: u8) -> Result<(), Error> {
self.send_keyboard_input(vk, true)
}
pub fn key_up(&mut self, vk: u8) -> Result<(), Error> {
self.send_keyboard_input(vk, false)
}
fn send_keyboard_input(&mut self, vk: u8, is_down: bool) -> Result<(), Error> {
let usage_id = key_translation::vk_to_usage_id(vk);
if usage_id == 0 {
return Err(Error::new(std::io::ErrorKind::InvalidInput, "Unknown virtual key code"));
}
let make_code = key_translation::usage_id_to_make_code(usage_id);
if make_code < 0 {
return Err(Error::new(std::io::ErrorKind::InvalidInput, "Failed to map key to scan code"));
}
let mut control = RzControl::new(Type::Keyboard);
unsafe {
let keyboard_data = control.keyboard_data_mut();
keyboard_data.make_code = make_code as u16;
keyboard_data.flags = if is_down { KEY_MAKE } else { KEY_BREAK };
if key_translation::is_extended_key(vk) {
keyboard_data.flags |= KEY_E0;
}
keyboard_data.unit_id = 0;
keyboard_data.reserved = 0;
keyboard_data.extra_information = 0;
}
self.send_control(&control)
}
fn send_control(&mut self, data: &RzControl) -> Result<(), Error> {
let mut bytes_returned = 0u32;
let result = unsafe {
DeviceIoControl(
self.handle,
RAZER_IOCTL_CODE,
Some(data as *const _ as *const c_void),
std::mem::size_of::<RzControl>() as u32,
None,
0,
Some(&mut bytes_returned),
None,
)
};
if result.is_err() {
self.reconnect().map_err(|e| Error::new(std::io::ErrorKind::Other, format!("Reconnection failed: {}", e)))?;
unsafe {
DeviceIoControl(
self.handle,
RAZER_IOCTL_CODE,
Some(data as *const _ as *const c_void),
std::mem::size_of::<RzControl>() as u32,
None,
0,
Some(&mut bytes_returned),
None,
).map_err(Error::from)?;
}
}
Ok(())
}
}
impl Drop for RazerDevice {
fn drop(&mut self) {
if !self.handle.is_invalid() {
unsafe {
CloseHandle(self.handle).ok();
}
}
}
}
pub enum MouseButton {
Left,
Right,
Middle,
X1,
X2,
}