source code

This commit is contained in:
rustdesk
2021-03-29 15:59:14 +08:00
parent 002fce136c
commit d1013487e2
175 changed files with 35074 additions and 2 deletions

View File

@@ -0,0 +1,28 @@
use crate::Error;
use std;
pub struct Window {}
impl Window {
pub fn new() -> Result<Window, Error> {
Err(Error::NotImplementedError)
}
pub fn quit(&self) {
unimplemented!()
}
pub fn set_tooltip(&self, _: &str) -> Result<(), Error> {
unimplemented!()
}
pub fn add_menu_item<F>(&self, _: &str, _: F) -> Result<u32, Error>
where
F: std::ops::Fn(&Window) -> () + 'static,
{
unimplemented!()
}
pub fn wait_for_message(&mut self) {
unimplemented!()
}
pub fn set_icon_from_buffer(&self, _: &[u8], _: u32, _: u32) -> Result<(), Error> {
unimplemented!()
}
}

View File

@@ -0,0 +1,182 @@
use crate::{Error, SystrayEvent};
use glib;
use gtk::{
self, MenuShellExt, GtkMenuItemExt, WidgetExt
};
use libappindicator::{AppIndicator, AppIndicatorStatus};
use std::{
self,
cell::RefCell,
collections::HashMap,
sync::mpsc::{channel, Sender},
thread,
};
// Gtk specific struct that will live only in the Gtk thread, since a lot of the
// base types involved don't implement Send (for good reason).
pub struct GtkSystrayApp {
menu: gtk::Menu,
ai: RefCell<AppIndicator>,
menu_items: RefCell<HashMap<u32, gtk::MenuItem>>,
event_tx: Sender<SystrayEvent>,
}
thread_local!(static GTK_STASH: RefCell<Option<GtkSystrayApp>> = RefCell::new(None));
pub struct MenuItemInfo {
mid: u32,
title: String,
tooltip: String,
disabled: bool,
checked: bool,
}
type Callback = Box<(Fn(&GtkSystrayApp) -> () + 'static)>;
// Convenience function to clean up thread local unwrapping
fn run_on_gtk_thread<F>(f: F)
where
F: std::ops::Fn(&GtkSystrayApp) -> () + Send + 'static,
{
// Note this is glib, not gtk. Calling gtk::idle_add will panic us due to
// being on different threads. glib::idle_add can run across threads.
glib::idle_add(move || {
GTK_STASH.with(|stash| {
let stash = stash.borrow();
let stash = stash.as_ref();
if let Some(stash) = stash {
f(stash);
}
});
gtk::prelude::Continue(false)
});
}
impl GtkSystrayApp {
pub fn new(event_tx: Sender<SystrayEvent>) -> Result<GtkSystrayApp, Error> {
if let Err(e) = gtk::init() {
return Err(Error::OsError(format!("{}", "Gtk init error!")));
}
let mut m = gtk::Menu::new();
let mut ai = AppIndicator::new("", "");
ai.set_status(AppIndicatorStatus::Active);
ai.set_menu(&mut m);
Ok(GtkSystrayApp {
menu: m,
ai: RefCell::new(ai),
menu_items: RefCell::new(HashMap::new()),
event_tx: event_tx,
})
}
pub fn systray_menu_selected(&self, menu_id: u32) {
self.event_tx
.send(SystrayEvent {
menu_index: menu_id as u32,
})
.ok();
}
pub fn add_menu_separator(&self, item_idx: u32) {
//let mut menu_items = self.menu_items.borrow_mut();
let m = gtk::SeparatorMenuItem::new();
self.menu.append(&m);
//menu_items.insert(item_idx, m);
self.menu.show_all();
}
pub fn add_menu_entry(&self, item_idx: u32, item_name: &str) {
let mut menu_items = self.menu_items.borrow_mut();
if menu_items.contains_key(&item_idx) {
let m: &gtk::MenuItem = menu_items.get(&item_idx).unwrap();
m.set_label(item_name);
self.menu.show_all();
return;
}
let m = gtk::MenuItem::new_with_label(item_name);
self.menu.append(&m);
m.connect_activate(move |_| {
run_on_gtk_thread(move |stash: &GtkSystrayApp| {
stash.systray_menu_selected(item_idx);
});
});
menu_items.insert(item_idx, m);
self.menu.show_all();
}
pub fn set_icon_from_file(&self, file: &str) {
let mut ai = self.ai.borrow_mut();
ai.set_icon_full(file, "icon");
}
}
pub struct Window {
gtk_loop: Option<thread::JoinHandle<()>>,
}
impl Window {
pub fn new(event_tx: Sender<SystrayEvent>) -> Result<Window, Error> {
let (tx, rx) = channel();
let gtk_loop = thread::spawn(move || {
GTK_STASH.with(|stash| match GtkSystrayApp::new(event_tx) {
Ok(data) => {
(*stash.borrow_mut()) = Some(data);
tx.send(Ok(()));
}
Err(e) => {
tx.send(Err(e));
return;
}
});
gtk::main();
});
match rx.recv().unwrap() {
Ok(()) => Ok(Window {
gtk_loop: Some(gtk_loop),
}),
Err(e) => Err(e),
}
}
pub fn add_menu_entry(&self, item_idx: u32, item_name: &str) -> Result<(), Error> {
let n = item_name.to_owned().clone();
run_on_gtk_thread(move |stash: &GtkSystrayApp| {
stash.add_menu_entry(item_idx, &n);
});
Ok(())
}
pub fn add_menu_separator(&self, item_idx: u32) -> Result<(), Error> {
run_on_gtk_thread(move |stash: &GtkSystrayApp| {
stash.add_menu_separator(item_idx);
});
Ok(())
}
pub fn set_icon_from_file(&self, file: &str) -> Result<(), Error> {
let n = file.to_owned().clone();
run_on_gtk_thread(move |stash: &GtkSystrayApp| {
stash.set_icon_from_file(&n);
});
Ok(())
}
pub fn set_icon_from_resource(&self, resource: &str) -> Result<(), Error> {
panic!("Not implemented on this platform!");
}
pub fn shutdown(&self) -> Result<(), Error> {
Ok(())
}
pub fn set_tooltip(&self, tooltip: &str) -> Result<(), Error> {
panic!("Not implemented on this platform!");
}
pub fn quit(&self) {
glib::idle_add(|| {
gtk::main_quit();
glib::Continue(false)
});
}
}

View File

@@ -0,0 +1,11 @@
#[cfg(target_os = "windows")]
#[path = "win32/mod.rs"]
pub mod api;
#[cfg(target_os = "linux")]
#[path = "linux/mod.rs"]
pub mod api;
#[cfg(target_os = "macos")]
#[path = "cocoa/mod.rs"]
pub mod api;

View File

@@ -0,0 +1,460 @@
use crate::{Error, SystrayEvent};
use std;
use std::cell::RefCell;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use std::sync::mpsc::{channel, Sender};
use std::thread;
use winapi::{
ctypes::{c_ulong, c_ushort},
shared::{
basetsd::ULONG_PTR,
guiddef::GUID,
minwindef::{DWORD, HINSTANCE, LPARAM, LRESULT, PBYTE, TRUE, UINT, WPARAM},
ntdef::LPCWSTR,
windef::{HBITMAP, HBRUSH, HICON, HMENU, HWND, POINT},
},
um::{
errhandlingapi, libloaderapi,
shellapi::{
self, NIF_ICON, NIF_MESSAGE, NIF_TIP, NIM_ADD, NIM_DELETE, NIM_MODIFY, NOTIFYICONDATAW,
},
winuser::{
self, CW_USEDEFAULT, IMAGE_ICON, LR_DEFAULTCOLOR, LR_LOADFROMFILE, MENUINFO,
MENUITEMINFOW, MFT_SEPARATOR, MFT_STRING, MIIM_FTYPE, MIIM_ID, MIIM_STATE, MIIM_STRING,
MIM_APPLYTOSUBMENUS, MIM_STYLE, MNS_NOTIFYBYPOS, WM_DESTROY, WM_USER, WNDCLASSW,
WS_OVERLAPPEDWINDOW,
},
},
};
// Got this idea from glutin. Yay open source! Boo stupid winproc! Even more boo
// doing SetLongPtr tho.
thread_local!(static WININFO_STASH: RefCell<Option<WindowsLoopData>> = RefCell::new(None));
fn to_wstring(str: &str) -> Vec<u16> {
OsStr::new(str)
.encode_wide()
.chain(Some(0).into_iter())
.collect::<Vec<_>>()
}
#[derive(Clone)]
struct WindowInfo {
pub hwnd: HWND,
pub hinstance: HINSTANCE,
pub hmenu: HMENU,
}
unsafe impl Send for WindowInfo {}
unsafe impl Sync for WindowInfo {}
#[derive(Clone)]
struct WindowsLoopData {
pub info: WindowInfo,
pub tx: Sender<SystrayEvent>,
}
unsafe fn get_win_os_error(msg: &str) -> Error {
Error::OsError(format!("{}: {}", &msg, errhandlingapi::GetLastError()))
}
unsafe extern "system" fn window_proc(
h_wnd: HWND,
msg: UINT,
w_param: WPARAM,
l_param: LPARAM,
) -> LRESULT {
if msg == winuser::WM_MENUCOMMAND {
WININFO_STASH.with(|stash| {
let stash = stash.borrow();
let stash = stash.as_ref();
if let Some(stash) = stash {
let menu_id = winuser::GetMenuItemID(stash.info.hmenu, w_param as i32) as i32;
if menu_id != -1 {
stash
.tx
.send(SystrayEvent {
menu_index: menu_id as u32,
})
.ok();
}
}
});
}
if msg == WM_USER + 1 {
if l_param as UINT == winuser::WM_LBUTTONUP || l_param as UINT == winuser::WM_RBUTTONUP {
let mut p = POINT { x: 0, y: 0 };
if winuser::GetCursorPos(&mut p as *mut POINT) == 0 {
return 1;
}
winuser::SetForegroundWindow(h_wnd);
WININFO_STASH.with(|stash| {
let stash = stash.borrow();
let stash = stash.as_ref();
if let Some(stash) = stash {
winuser::TrackPopupMenu(
stash.info.hmenu,
0,
p.x,
p.y,
(winuser::TPM_BOTTOMALIGN | winuser::TPM_LEFTALIGN) as i32,
h_wnd,
std::ptr::null_mut(),
);
}
});
}
}
if msg == winuser::WM_DESTROY {
winuser::PostQuitMessage(0);
}
return winuser::DefWindowProcW(h_wnd, msg, w_param, l_param);
}
fn get_nid_struct(hwnd: &HWND) -> NOTIFYICONDATAW {
NOTIFYICONDATAW {
cbSize: std::mem::size_of::<NOTIFYICONDATAW>() as DWORD,
hWnd: *hwnd,
uID: 0x1 as UINT,
uFlags: 0 as UINT,
uCallbackMessage: 0 as UINT,
hIcon: 0 as HICON,
szTip: [0 as u16; 128],
dwState: 0 as DWORD,
dwStateMask: 0 as DWORD,
szInfo: [0 as u16; 256],
u: Default::default(),
szInfoTitle: [0 as u16; 64],
dwInfoFlags: 0 as UINT,
guidItem: GUID {
Data1: 0 as c_ulong,
Data2: 0 as c_ushort,
Data3: 0 as c_ushort,
Data4: [0; 8],
},
hBalloonIcon: 0 as HICON,
}
}
fn get_menu_item_struct() -> MENUITEMINFOW {
MENUITEMINFOW {
cbSize: std::mem::size_of::<MENUITEMINFOW>() as UINT,
fMask: 0 as UINT,
fType: 0 as UINT,
fState: 0 as UINT,
wID: 0 as UINT,
hSubMenu: 0 as HMENU,
hbmpChecked: 0 as HBITMAP,
hbmpUnchecked: 0 as HBITMAP,
dwItemData: 0 as ULONG_PTR,
dwTypeData: std::ptr::null_mut(),
cch: 0 as u32,
hbmpItem: 0 as HBITMAP,
}
}
unsafe fn init_window() -> Result<WindowInfo, Error> {
let class_name = to_wstring("my_window");
let hinstance: HINSTANCE = libloaderapi::GetModuleHandleA(std::ptr::null_mut());
let wnd = WNDCLASSW {
style: 0,
lpfnWndProc: Some(window_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: 0 as HINSTANCE,
hIcon: winuser::LoadIconW(0 as HINSTANCE, winuser::IDI_APPLICATION),
hCursor: winuser::LoadCursorW(0 as HINSTANCE, winuser::IDI_APPLICATION),
hbrBackground: 16 as HBRUSH,
lpszMenuName: 0 as LPCWSTR,
lpszClassName: class_name.as_ptr(),
};
if winuser::RegisterClassW(&wnd) == 0 {
return Err(get_win_os_error("Error creating window class"));
}
let hwnd = winuser::CreateWindowExW(
0,
class_name.as_ptr(),
to_wstring("rust_systray_window").as_ptr(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
0,
CW_USEDEFAULT,
0,
0 as HWND,
0 as HMENU,
0 as HINSTANCE,
std::ptr::null_mut(),
);
if hwnd == std::ptr::null_mut() {
return Err(get_win_os_error("Error creating window"));
}
let mut nid = get_nid_struct(&hwnd);
nid.uID = 0x1;
nid.uFlags = NIF_MESSAGE;
nid.uCallbackMessage = WM_USER + 1;
if shellapi::Shell_NotifyIconW(NIM_ADD, &mut nid as *mut NOTIFYICONDATAW) == 0 {
return Err(get_win_os_error("Error adding menu icon"));
}
// Setup menu
let hmenu = winuser::CreatePopupMenu();
let m = MENUINFO {
cbSize: std::mem::size_of::<MENUINFO>() as DWORD,
fMask: MIM_APPLYTOSUBMENUS | MIM_STYLE,
dwStyle: MNS_NOTIFYBYPOS,
cyMax: 0 as UINT,
hbrBack: 0 as HBRUSH,
dwContextHelpID: 0 as DWORD,
dwMenuData: 0 as ULONG_PTR,
};
if winuser::SetMenuInfo(hmenu, &m as *const MENUINFO) == 0 {
return Err(get_win_os_error("Error setting up menu"));
}
Ok(WindowInfo {
hwnd: hwnd,
hmenu: hmenu,
hinstance: hinstance,
})
}
unsafe fn run_loop() {
log::debug!("Running windows loop");
// Run message loop
let mut msg = winuser::MSG {
hwnd: 0 as HWND,
message: 0 as UINT,
wParam: 0 as WPARAM,
lParam: 0 as LPARAM,
time: 0 as DWORD,
pt: POINT { x: 0, y: 0 },
};
loop {
winuser::GetMessageW(&mut msg, 0 as HWND, 0, 0);
if msg.message == winuser::WM_QUIT {
break;
}
winuser::TranslateMessage(&mut msg);
winuser::DispatchMessageW(&mut msg);
}
log::debug!("Leaving windows run loop");
}
pub struct Window {
info: WindowInfo,
windows_loop: Option<thread::JoinHandle<()>>,
}
impl Window {
pub fn new(event_tx: Sender<SystrayEvent>) -> Result<Window, Error> {
let (tx, rx) = channel();
let windows_loop = thread::spawn(move || {
unsafe {
let i = init_window();
let k;
match i {
Ok(j) => {
tx.send(Ok(j.clone())).ok();
k = j;
}
Err(e) => {
// If creation didn't work, return out of the thread.
tx.send(Err(e)).ok();
return;
}
};
WININFO_STASH.with(|stash| {
let data = WindowsLoopData {
info: k,
tx: event_tx,
};
(*stash.borrow_mut()) = Some(data);
});
run_loop();
}
});
let info = match rx.recv().unwrap() {
Ok(i) => i,
Err(e) => {
return Err(e);
}
};
let w = Window {
info: info,
windows_loop: Some(windows_loop),
};
Ok(w)
}
pub fn quit(&mut self) {
unsafe {
winuser::PostMessageW(self.info.hwnd, WM_DESTROY, 0 as WPARAM, 0 as LPARAM);
}
if let Some(t) = self.windows_loop.take() {
t.join().ok();
}
}
pub fn set_tooltip(&self, tooltip: &str) -> Result<(), Error> {
// Add Tooltip
log::debug!("Setting tooltip to {}", tooltip);
// Gross way to convert String to [i8; 128]
// TODO: Clean up conversion, test for length so we don't panic at runtime
let tt = tooltip.as_bytes().clone();
let mut nid = get_nid_struct(&self.info.hwnd);
for i in 0..tt.len() {
nid.szTip[i] = tt[i] as u16;
}
nid.uFlags = NIF_TIP;
unsafe {
if shellapi::Shell_NotifyIconW(NIM_MODIFY, &mut nid as *mut NOTIFYICONDATAW) == 0 {
return Err(get_win_os_error("Error setting tooltip"));
}
}
Ok(())
}
pub fn add_menu_entry(&self, item_idx: u32, item_name: &str) -> Result<(), Error> {
let mut st = to_wstring(item_name);
let mut item = get_menu_item_struct();
item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID | MIIM_STATE;
item.fType = MFT_STRING;
item.wID = item_idx;
item.dwTypeData = st.as_mut_ptr();
item.cch = (item_name.len() * 2) as u32;
unsafe {
if winuser::InsertMenuItemW(self.info.hmenu, item_idx, 1, &item as *const MENUITEMINFOW)
== 0
{
return Err(get_win_os_error("Error inserting menu item"));
}
}
Ok(())
}
pub fn remove_menu_entry(&self, item_idx: u32) {
unsafe { winuser::RemoveMenu(self.info.hmenu, item_idx, 1 as _); }
}
pub fn add_menu_separator(&self, item_idx: u32) -> Result<(), Error> {
let mut item = get_menu_item_struct();
item.fMask = MIIM_FTYPE;
item.fType = MFT_SEPARATOR;
item.wID = item_idx;
unsafe {
if winuser::InsertMenuItemW(self.info.hmenu, item_idx, 1, &item as *const MENUITEMINFOW)
== 0
{
return Err(get_win_os_error("Error inserting separator"));
}
}
Ok(())
}
fn set_icon(&self, icon: HICON) -> Result<(), Error> {
unsafe {
let mut nid = get_nid_struct(&self.info.hwnd);
nid.uFlags = NIF_ICON;
nid.hIcon = icon;
if shellapi::Shell_NotifyIconW(NIM_MODIFY, &mut nid as *mut NOTIFYICONDATAW) == 0 {
return Err(get_win_os_error("Error setting icon"));
}
}
Ok(())
}
pub fn set_icon_from_resource(&self, resource_name: &str) -> Result<(), Error> {
let icon;
unsafe {
icon = winuser::LoadImageW(
self.info.hinstance,
to_wstring(&resource_name).as_ptr(),
IMAGE_ICON,
64,
64,
0,
) as HICON;
if icon == std::ptr::null_mut() as HICON {
return Err(get_win_os_error("Error setting icon from resource"));
}
}
self.set_icon(icon)
}
pub fn set_icon_from_file(&self, icon_file: &str) -> Result<(), Error> {
let wstr_icon_file = to_wstring(&icon_file);
let hicon;
unsafe {
hicon = winuser::LoadImageW(
std::ptr::null_mut() as HINSTANCE,
wstr_icon_file.as_ptr(),
IMAGE_ICON,
64,
64,
LR_LOADFROMFILE,
) as HICON;
if hicon == std::ptr::null_mut() as HICON {
return Err(get_win_os_error("Error setting icon from file"));
}
}
self.set_icon(hicon)
}
pub fn set_icon_from_buffer(
&self,
buffer: &[u8],
width: u32,
height: u32,
) -> Result<(), Error> {
let offset = unsafe {
winuser::LookupIconIdFromDirectoryEx(
buffer.as_ptr() as PBYTE,
TRUE,
width as i32,
height as i32,
LR_DEFAULTCOLOR,
)
};
if offset != 0 {
let icon_data = &buffer[offset as usize..];
let hicon = unsafe {
winuser::CreateIconFromResourceEx(
icon_data.as_ptr() as PBYTE,
icon_data.len() as u32,
TRUE,
0x30000,
width as i32,
height as i32,
LR_DEFAULTCOLOR,
)
};
if hicon == std::ptr::null_mut() as HICON {
return Err(unsafe { get_win_os_error("Cannot load icon from the buffer") });
}
self.set_icon(hicon)
} else {
Err(unsafe { get_win_os_error("Error setting icon from buffer") })
}
}
pub fn shutdown(&self) -> Result<(), Error> {
unsafe {
let mut nid = get_nid_struct(&self.info.hwnd);
nid.uFlags = NIF_ICON;
if shellapi::Shell_NotifyIconW(NIM_DELETE, &mut nid as *mut NOTIFYICONDATAW) == 0 {
return Err(get_win_os_error("Error deleting icon from menu"));
}
}
Ok(())
}
}
impl Drop for Window {
fn drop(&mut self) {
self.shutdown().ok();
}
}

192
libs/systray-rs/src/lib.rs Normal file
View File

@@ -0,0 +1,192 @@
// Systray Lib
pub mod api;
use std::{
collections::HashMap,
error, fmt,
sync::mpsc::{channel, Receiver},
};
type BoxedError = Box<dyn error::Error + Send + Sync + 'static>;
#[derive(Debug)]
pub enum Error {
OsError(String),
NotImplementedError,
UnknownError,
Error(BoxedError),
}
impl From<BoxedError> for Error {
fn from(value: BoxedError) -> Self {
Error::Error(value)
}
}
pub struct SystrayEvent {
menu_index: u32,
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use self::Error::*;
match *self {
OsError(ref err_str) => write!(f, "OsError: {}", err_str),
NotImplementedError => write!(f, "Functionality is not implemented yet"),
UnknownError => write!(f, "Unknown error occurrred"),
Error(ref e) => write!(f, "Error: {}", e),
}
}
}
pub struct Application {
window: api::api::Window,
menu_idx: u32,
callback: HashMap<u32, Callback>,
// Each platform-specific window module will set up its own thread for
// dealing with the OS main loop. Use this channel for receiving events from
// that thread.
rx: Receiver<SystrayEvent>,
timer: Option<(std::time::Duration, Callback)>,
}
type Callback =
Box<(dyn FnMut(&mut Application) -> Result<(), BoxedError> + Send + Sync + 'static)>;
fn make_callback<F, E>(mut f: F) -> Callback
where
F: FnMut(&mut Application) -> Result<(), E> + Send + Sync + 'static,
E: error::Error + Send + Sync + 'static,
{
Box::new(move |a: &mut Application| match f(a) {
Ok(()) => Ok(()),
Err(e) => Err(Box::new(e) as BoxedError),
}) as Callback
}
impl Application {
pub fn new() -> Result<Application, Error> {
let (event_tx, event_rx) = channel();
match api::api::Window::new(event_tx) {
Ok(w) => Ok(Application {
window: w,
menu_idx: 0,
callback: HashMap::new(),
rx: event_rx,
timer: None,
}),
Err(e) => Err(e),
}
}
pub fn set_timer<F, E>(
&mut self,
interval: std::time::Duration,
callback: F,
) -> Result<(), Error>
where
F: FnMut(&mut Application) -> Result<(), E> + Send + Sync + 'static,
E: error::Error + Send + Sync + 'static,
{
self.timer = Some((interval, make_callback(callback)));
Ok(())
}
pub fn add_menu_item<F, E>(&mut self, item_name: &str, f: F) -> Result<u32, Error>
where
F: FnMut(&mut Application) -> Result<(), E> + Send + Sync + 'static,
E: error::Error + Send + Sync + 'static,
{
let idx = self.menu_idx;
if let Err(e) = self.window.add_menu_entry(idx, item_name) {
return Err(e);
}
self.callback.insert(idx, make_callback(f));
self.menu_idx += 1;
Ok(idx)
}
pub fn remove_menu_item(&mut self, pos: u32) {
self.window.remove_menu_entry(pos);
self.callback.remove(&pos);
}
pub fn add_menu_separator(&mut self) -> Result<u32, Error> {
let idx = self.menu_idx;
if let Err(e) = self.window.add_menu_separator(idx) {
return Err(e);
}
self.menu_idx += 1;
Ok(idx)
}
pub fn set_icon_from_file(&self, file: &str) -> Result<(), Error> {
self.window.set_icon_from_file(file)
}
pub fn set_icon_from_resource(&self, resource: &str) -> Result<(), Error> {
self.window.set_icon_from_resource(resource)
}
#[cfg(target_os = "windows")]
pub fn set_icon_from_buffer(
&self,
buffer: &[u8],
width: u32,
height: u32,
) -> Result<(), Error> {
self.window.set_icon_from_buffer(buffer, width, height)
}
pub fn shutdown(&self) -> Result<(), Error> {
self.window.shutdown()
}
pub fn set_tooltip(&self, tooltip: &str) -> Result<(), Error> {
self.window.set_tooltip(tooltip)
}
pub fn quit(&mut self) {
self.window.quit()
}
pub fn wait_for_message(&mut self) -> Result<(), Error> {
loop {
let mut msg = None;
if let Some((interval, _)) = self.timer.as_ref() {
match self.rx.recv_timeout(interval.clone()) {
Ok(m) => msg = Some(m),
Err(_) => {}
}
} else {
match self.rx.recv() {
Ok(m) => msg = Some(m),
Err(_) => {
self.quit();
break;
}
}
}
if let Some(msg) = msg {
if let Some(mut f) = self.callback.remove(&msg.menu_index) {
f(self)?;
self.callback.insert(msg.menu_index, f);
}
} else if let Some((interval, mut callback)) = self.timer.take() {
callback(self)?;
self.timer = Some((interval, callback));
}
}
Ok(())
}
}
impl Drop for Application {
fn drop(&mut self) {
self.shutdown().ok();
}
}