use std::{
fmt::Debug,
ops::{Deref, DerefMut},
};
use winapi::shared::minwindef::{HIWORD, LOWORD, LPARAM, LPVOID, LRESULT, UINT, WPARAM};
use winapi::shared::windef::{HBRUSH, HICON, HMENU, HWND, POINT};
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser;
use super::wchar::wchar;
use super::{msgs, winnotifyicon::WinNotifyIcon, MenuSys};
use crate::{trayiconsender::TrayIconSender, Error, Icon, MenuBuilder, TrayIconBase};
pub type WinTrayIcon<T> = WindowBox<T>;
#[derive(Debug)]
pub struct WindowBox<T>(*mut WinTrayIconImpl<T>)
where
T: PartialEq + Clone + 'static;
impl<T> Drop for WindowBox<T>
where
T: PartialEq + Clone + 'static,
{
fn drop(&mut self) {
unsafe {
winuser::SendMessageW(self.hwnd, winuser::WM_CLOSE, 0, 0);
}
}
}
impl<T> Deref for WindowBox<T>
where
T: PartialEq + Clone + 'static,
{
type Target = WinTrayIconImpl<T>;
fn deref(&self) -> &WinTrayIconImpl<T> {
unsafe { &mut *(self.0) }
}
}
impl<T> DerefMut for WindowBox<T>
where
T: PartialEq + Clone + 'static,
{
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.0) }
}
}
#[derive(Debug)]
pub struct WinTrayIconImpl<T>
where
T: PartialEq + Clone + 'static,
{
hwnd: HWND,
sender: TrayIconSender<T>,
menu: Option<MenuSys<T>>,
notify_icon: WinNotifyIcon,
on_click: Option<T>,
on_double_click: Option<T>,
on_right_click: Option<T>,
msg_taskbarcreated: Option<UINT>,
}
unsafe impl<T> Send for WinTrayIconImpl<T> where T: PartialEq + Clone {}
unsafe impl<T> Sync for WinTrayIconImpl<T> where T: PartialEq + Clone {}
impl<T> WinTrayIconImpl<T>
where
T: PartialEq + Clone + 'static,
{
#[allow(clippy::new_ret_no_self)]
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
sender: TrayIconSender<T>,
menu: Option<MenuSys<T>>,
notify_icon: WinNotifyIcon,
on_click: Option<T>,
on_double_click: Option<T>,
on_right_click: Option<T>,
) -> Result<WinTrayIcon<T>, Error>
where
T: PartialEq + Clone + 'static,
{
unsafe {
let hinstance = GetModuleHandleW(0 as _);
let wnd_class_name = wchar("TrayIconCls");
let wnd_class = winuser::WNDCLASSW {
style: 0,
lpfnWndProc: Some(WinTrayIconImpl::<T>::winproc),
hInstance: hinstance,
lpszClassName: wnd_class_name.as_ptr() as _,
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: 0 as HICON,
hCursor: 0 as HICON,
hbrBackground: 0 as HBRUSH,
lpszMenuName: 0 as _,
};
winuser::RegisterClassW(&wnd_class);
let window = Box::new(WinTrayIconImpl {
hwnd: 0 as HWND,
notify_icon,
menu,
on_click,
on_right_click,
on_double_click,
sender,
msg_taskbarcreated: None,
});
let ptr = Box::into_raw(window);
let hwnd = winuser::CreateWindowExW(
0,
wnd_class_name.as_ptr() as _,
wchar("TrayIcon").as_ptr() as _,
0, winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
0 as _,
0 as HMENU,
hinstance,
ptr as *mut _ as LPVOID,
) as u32;
if hwnd == 0 {
return Err(Error::OsError);
}
Ok(WindowBox(ptr))
}
}
pub fn wndproc(&mut self, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
match msg {
winuser::WM_CREATE => {
self.notify_icon.add(self.hwnd);
self.msg_taskbarcreated = unsafe {
Some(winuser::RegisterWindowMessageA(
"TaskbarCreated\0".as_ptr() as _
))
};
}
msgs::WM_USER_TRAYICON => {
match lparam as u32 {
winuser::WM_LBUTTONUP => {
if let Some(e) = self.on_click.as_ref() {
self.sender.send(e);
}
}
winuser::WM_RBUTTONUP => {
if let Some(e) = self.on_right_click.as_ref() {
self.sender.send(e);
}
if let Some(menu) = &self.menu {
let mut pos = POINT { x: 0, y: 0 };
unsafe {
winuser::GetCursorPos(&mut pos as _);
winuser::SetForegroundWindow(self.hwnd);
}
menu.menu.track(self.hwnd, pos.x, pos.y);
}
}
winuser::WM_LBUTTONDBLCLK => {
if let Some(e) = self.on_double_click.as_ref() {
self.sender.send(e);
}
}
_ => {}
}
}
winuser::WM_COMMAND => {
let identifier = LOWORD(wparam as u32);
let cmd = HIWORD(wparam as u32);
if cmd == 0 {
if let Some(v) = self.menu.as_ref() {
if let Some(event) = v.ids.get(&(identifier as usize)) {
self.sender.send(event);
}
}
}
}
x if Some(x) == self.msg_taskbarcreated => {
self.notify_icon.add(self.hwnd);
}
_ => {
return unsafe { winuser::DefWindowProcW(self.hwnd, msg, wparam, lparam) };
}
}
0
}
pub unsafe extern "system" fn winproc(
hwnd: HWND,
msg: UINT,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
match msg {
winuser::WM_CREATE => {
let create_struct: &mut winuser::CREATESTRUCTW = &mut *(lparam as *mut _);
let window: &mut WinTrayIconImpl<T> =
&mut *(create_struct.lpCreateParams as *mut _);
window.hwnd = hwnd;
winuser::SetWindowLongPtrW(hwnd, winuser::GWL_USERDATA, window as *mut _ as _);
window.wndproc(msg, wparam, lparam)
}
winuser::WM_NCDESTROY => {
let window_ptr = winuser::SetWindowLongPtrW(hwnd, winuser::GWL_USERDATA, 0);
if window_ptr != 0 {
let ptr = window_ptr as *mut WinTrayIconImpl<T>;
let mut window = Box::from_raw(ptr);
window.wndproc(msg, wparam, lparam)
} else {
winuser::DefWindowProcW(hwnd, msg, wparam, lparam)
}
}
_ => {
let window_ptr = winuser::GetWindowLongPtrW(hwnd, winuser::GWL_USERDATA);
if window_ptr != 0 {
let window: &mut WinTrayIconImpl<T> = &mut *(window_ptr as *mut _);
window.wndproc(msg, wparam, lparam)
} else {
winuser::DefWindowProcW(hwnd, msg, wparam, lparam)
}
}
}
}
}
impl<T> TrayIconBase<T> for WinTrayIconImpl<T>
where
T: PartialEq + Clone + 'static,
{
fn set_tooltip(&mut self, tooltip: &str) -> Result<(), Error> {
if !self.notify_icon.set_tooltip(tooltip) {
return Err(Error::OsError);
}
Ok(())
}
fn set_icon(&mut self, icon: &Icon) -> Result<(), Error> {
if !self.notify_icon.set_icon(&icon.sys) {
return Err(Error::IconLoadingFailed);
}
Ok(())
}
fn set_menu(&mut self, menu: &MenuBuilder<T>) -> Result<(), Error> {
if menu.menu_items.is_empty() {
self.menu = None
} else {
self.menu = Some(menu.build()?);
}
Ok(())
}
}
impl<T> Drop for WinTrayIconImpl<T>
where
T: PartialEq + Clone + 'static,
{
fn drop(&mut self) {
self.notify_icon.remove();
}
}