#![allow(clippy::not_unsafe_ptr_arg_deref)]
use crate::mlink::{
CMumbleContext, CMumbleLink, MumbleLink, C_MUMBLE_LINK_SIZE, USEFUL_C_MUMBLE_LINK_SIZE,
};
use color_eyre::eyre::*;
use tracing::*;
use windows::{
core::PCSTR,
Win32::{
Foundation::*,
System::{Memory::*, Threading::*},
UI::WindowsAndMessaging::*,
},
};
pub struct MumbleBackend {
pub link_ptr: *const CMumbleLink,
mumble_handle: HANDLE,
}
impl Drop for MumbleBackend {
fn drop(&mut self) {
unsafe {
UnmapViewOfFile(self.link_ptr as *const std::ffi::c_void);
CloseHandle(self.mumble_handle);
}
}
}
#[derive(Debug)]
pub struct MumbleSource {
pub link_ptr: *const CMumbleLink,
mumble_handle: HANDLE,
pub gw2_window_handle: isize,
pub gw2_pid: u32,
pub gw2_process_handle: HANDLE,
pub gw2_pos: [i32; 2],
pub gw2_size: [u32; 2],
pub link: MumbleLink,
pub last_uitick_update: f64,
pub last_pos_size_update: f64,
}
impl Drop for MumbleSource {
fn drop(&mut self) {
unsafe {
UnmapViewOfFile(self.link_ptr as *const std::ffi::c_void);
CloseHandle(self.mumble_handle);
}
}
}
impl MumbleSource {
pub fn new(key: &str, latest_time: f64, _ow_window_id: u32) -> Result<Self> {
let (handle, link_ptr) =
create_link_shared_mem(key).wrap_err("failed to create mumblelink shm ")?;
let mut link = MumbleLink::default();
let link = if link.update(link_ptr).is_ok() {
link
} else {
MumbleLink::default()
};
Ok(Self {
link_ptr,
mumble_handle: handle,
gw2_window_handle: 0,
gw2_pid: 0,
gw2_process_handle: HANDLE::default(),
gw2_pos: [0, 0],
gw2_size: [0, 0],
link,
last_uitick_update: latest_time,
last_pos_size_update: latest_time,
})
}
pub fn tick(&mut self, latest_time: f64, _sys: &mut sysinfo::System) -> Result<()> {
let mut present_link = MumbleLink::default();
present_link.update(self.link_ptr)?;
let present_tick = present_link.ui_tick;
let previous_tick = self.link.ui_tick;
if present_tick == 0 || previous_tick == present_tick {
return Ok(());
}
if previous_tick > present_tick {
warn!("previous tick: {previous_tick} is greater than present_tick: {present_tick}. ");
self.gw2_window_handle = 0;
self.gw2_pid = 0;
if !self.gw2_process_handle.is_invalid() {
close_process_handle(self.gw2_process_handle);
}
}
self.last_uitick_update = latest_time;
self.link = present_link;
if latest_time - self.last_pos_size_update > 1.0 {
self.gw2_pid = get_gw2_pid(self.link_ptr);
if self.gw2_pid != 0 {
if self.gw2_process_handle.is_invalid() {
self.gw2_process_handle = get_process_handle(self.gw2_pid)
.wrap_err("failed to get gw2 process handle, when pid is not zero")?;
}
assert!(!self.gw2_process_handle.is_invalid());
if check_process_alive(self.gw2_process_handle)? {
if self.gw2_window_handle == 0 {
self.gw2_window_handle = get_gw2_window_handle(self.link_ptr).wrap_err(
"failed to get window handle when tick is greater than zero",
)?;
}
assert_ne!(self.gw2_window_handle, 0);
let (x, y, w, h) = get_win_pos_dim(self.gw2_window_handle)?;
self.gw2_pos = [x, y];
self.gw2_size = [w, h];
self.last_pos_size_update = latest_time;
}
}
}
Ok(())
}
pub fn get_link(&self) -> &MumbleLink {
&self.link
}
}
pub struct MumbleOnly {
pub link_ptr: *const CMumbleLink,
mumble_handle: HANDLE,
pub last_ui_tick_update: f64,
pub last_ui_tick: u32,
}
impl Drop for MumbleOnly {
fn drop(&mut self) {
unsafe {
UnmapViewOfFile(self.link_ptr as *const std::ffi::c_void);
CloseHandle(self.mumble_handle);
}
}
}
impl MumbleOnly {
pub fn new(key: &str, glfw_time: f64) -> Result<Self> {
let (handle, link_ptr) =
create_link_shared_mem(key).wrap_err("failed to create mumblelink shm ")?;
let last_ui_tick = unsafe { (*link_ptr).ui_tick };
Ok(Self {
mumble_handle: handle,
link_ptr,
last_ui_tick_update: glfw_time,
last_ui_tick,
})
}
pub fn tick(&mut self, latest_time: f64) -> Result<Option<(u32, MumbleLink)>> {
let previous_tick = self.last_ui_tick;
let mut present_link = MumbleLink::default();
let buffer = get_link_buffer(self.link_ptr);
present_link.update_from_slice(&buffer)?;
let present_tick = present_link.ui_tick;
if present_tick == 0 || previous_tick == present_tick {
return Ok(None);
}
self.last_ui_tick_update = latest_time;
self.last_ui_tick = present_tick;
Ok(Some((present_link.context.process_id, present_link)))
}
}
unsafe extern "system" fn get_handle_by_pid(window_handle: HWND, gw2_pid: LPARAM) -> BOOL {
let mut handle_pid: u32 = 0;
GetWindowThreadProcessId(window_handle, (&mut handle_pid) as *mut u32);
if handle_pid == 0 {
return BOOL(1);
}
let gw2_pid = gw2_pid.0 as *mut isize;
if *gw2_pid == handle_pid as isize {
*gw2_pid = window_handle.0;
return BOOL(0);
}
BOOL(1)
}
pub fn get_win_pos_dim(window_handle: isize) -> Result<(i32, i32, u32, u32)> {
unsafe {
let mut rect: RECT = RECT {
left: 0,
top: 0,
right: 0,
bottom: 0,
};
let status = GetWindowRect(HWND(window_handle), &mut rect as *mut RECT);
if !status.as_bool() {
bail!("could not get gw2 window size");
}
Ok((
rect.left,
rect.top,
(rect.right - rect.left)
.try_into()
.wrap_err("gw2 window width could not be cast into u32")?,
(rect.bottom - rect.top)
.try_into()
.wrap_err("gw2 height could not be cast into u32")?,
))
}
}
pub fn get_link_buffer(
link_ptr: *const CMumbleLink,
) -> [u8; USEFUL_C_MUMBLE_LINK_SIZE + std::mem::size_of::<isize>()] {
{
let mut buffer = [0u8; USEFUL_C_MUMBLE_LINK_SIZE + std::mem::size_of::<isize>()];
buffer.copy_from_slice(unsafe {
std::slice::from_raw_parts(link_ptr as *const u8, C_MUMBLE_LINK_SIZE)
});
buffer
}
}
pub fn get_gw2_window_handle(link_ptr: *const CMumbleLink) -> Result<isize> {
unsafe {
let mut pid = get_gw2_pid(link_ptr) as isize;
let result: BOOL = EnumWindows(
Some(get_handle_by_pid),
LPARAM(((&mut pid) as *mut isize) as isize),
);
if result.as_bool() {
bail!(
"couldn't find gw2 window. error code: {:#?}",
GetLastError()
);
}
Ok(pid as isize)
}
}
pub fn get_gw2_pid(link_ptr: *const CMumbleLink) -> u32 {
unsafe { (*((*link_ptr).context.as_ptr() as *const CMumbleContext)).process_id }
}
pub fn get_xid(link_ptr: *const CMumbleLink) -> Result<u32> {
assert!(CMumbleLink::is_valid(link_ptr));
unsafe {
let window_handle = get_gw2_window_handle(link_ptr)?;
let atom_string = std::ffi::CString::new("__wine_x11_whole_window")?;
let xid = GetPropA(HWND(window_handle), PCSTR(atom_string.as_ptr() as _));
if xid.is_invalid() {
bail!("xid is NULL");
}
xid.0
.try_into()
.wrap_err("failed to put xid into u32 from isize")
}
}
pub fn get_process_handle(pid: u32) -> Result<HANDLE> {
unsafe {
OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SYNCHRONIZE,
false,
pid,
)
.wrap_err_with(|| {
format!(
"failed to get handle for process id: {} due to error {:?}",
pid,
GetLastError()
)
})
}
}
pub fn check_process_alive(process_handle: HANDLE) -> Result<bool> {
let result = unsafe { WaitForSingleObject(process_handle, 0) };
if result == WAIT_ABANDONED || result == WAIT_OBJECT_0 {
Ok(false)
} else if result == WAIT_TIMEOUT.0 {
Ok(true)
} else {
bail!("WaitForSingleObject returned code: {:#?}", result)
}
}
pub fn close_process_handle(process_handle: HANDLE) {
unsafe {
CloseHandle(process_handle);
}
}
pub fn is_pid_alive(pid: u32) -> Result<bool> {
let process_handle = get_process_handle(pid)?;
let alive = check_process_alive(process_handle);
close_process_handle(process_handle);
alive
}
pub fn create_link_shared_mem(key: &str) -> Result<(HANDLE, *const CMumbleLink)> {
let key_cstr = std::ffi::CString::new(key)?;
unsafe {
let file_handle = CreateFileMappingA(
INVALID_HANDLE_VALUE,
std::ptr::null_mut(),
PAGE_READWRITE,
0,
C_MUMBLE_LINK_SIZE as u32,
PCSTR(key_cstr.as_ptr() as _),
)
.wrap_err_with(|| {
format!(
"could not create file map handle, error code: {:#?}",
GetLastError()
)
})?;
let cml_ptr = MapViewOfFile(file_handle, FILE_MAP_ALL_ACCESS, 0, 0, C_MUMBLE_LINK_SIZE);
if cml_ptr.is_null() {
bail!(
"could not map view of file, error code: {:#?}",
GetLastError()
);
}
Ok((file_handle, cml_ptr as *const CMumbleLink))
}
}