diff --git a/Cargo.toml b/Cargo.toml index 8e68873..24ade03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ ctrlc = "3.2.1" serde_json = "1.0" crossbeam-channel = "0.5" inputbot = {path = "D:\\Inputbot"} +winit = "0.25" +winapi = { version = "0.3.9", features = ["winuser", "windef", "minwindef", "shellapi", "libloaderapi", "commctrl", "basetsd"] } +trayicon = { version="0.1.3", features = ["crossbeam-channel"] } [features] default = [] diff --git a/src/icon1.ico b/src/icon1.ico new file mode 100644 index 0000000..31ff90f Binary files /dev/null and b/src/icon1.ico differ diff --git a/src/icon2.ico b/src/icon2.ico new file mode 100644 index 0000000..9a35618 Binary files /dev/null and b/src/icon2.ico differ diff --git a/src/main.rs b/src/main.rs index e0bb349..838341d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,9 @@ use workctl::sync_flag; use crate::modules::stream_states::state_update::StateUpdate; +#[cfg(target_os = "windows")] +use modules::tray_icon; + #[cfg(test)] mod tests; mod modules; @@ -21,6 +24,10 @@ const SERVER_ADDRESS: &str = "10.0.0.209:5000"; const SERVER_ADDRESS: &str = "10.0.0.168:5000"; fn main() { + + let icon = include_bytes!("icon1.ico"); + let mut tray_icon = tray_icon::TrayIcon::setup(icon); + let settings_json = load_json(); let hotkeys = Hotkeys::new(settings_json); @@ -53,8 +60,11 @@ fn main() { }, Err(_) => {}, } + let tick_update = state.tick(); handle_instructions(tick_update, &mut state, &socket, &hotkeys); + + tray_icon.process_tray_messages(); } println!("closing main thread"); @@ -109,4 +119,3 @@ fn update_all(state: &StreamState, socket: &Socket) { socket.send(StateUpdate::Scene(state.current_scene).to_json().to_string()); socket.send(StateUpdate::PauseTimer(state.timer_paused_length.is_some()).to_json().to_string()); } - diff --git a/src/modules/mod.rs b/src/modules/mod.rs index bfe706d..fda7886 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -2,6 +2,7 @@ pub mod message_handler; pub mod socket_handler; pub mod external_interface; +pub mod tray_icon; pub mod stream_states { pub mod stream_state; diff --git a/src/modules/tray_icon.rs b/src/modules/tray_icon.rs new file mode 100644 index 0000000..2b7d479 --- /dev/null +++ b/src/modules/tray_icon.rs @@ -0,0 +1,130 @@ +use core::mem::MaybeUninit; +use std::time::Duration; +use trayicon::*; +use winapi::um::winuser; + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Events { + ClickTrayIcon, + DoubleClickTrayIcon, + Exit, + Item1, + Item2, + Item3, + Item4, + CheckItem1, + SubItem1, + SubItem2, + SubItem3, +} + +pub struct TrayIcon { + tray_icon: trayicon::TrayIcon, + message_channel: crossbeam_channel::Receiver, +} + +impl TrayIcon { + + pub fn setup(icon: &[u8]) -> TrayIcon { + + let (s, r) = crossbeam_channel::unbounded(); + let icon1 = include_bytes!("./../icon1.ico"); + let icon2 = include_bytes!("./../icon2.ico"); + + let second_icon = Icon::from_buffer(icon2, None, None).unwrap(); + let first_icon = Icon::from_buffer(icon1, None, None).unwrap(); + + // Needlessly complicated tray icon with all the whistles and bells + let mut tray_icon = TrayIconBuilder::new() + .sender_crossbeam(s) + .icon_from_buffer(icon1) + .tooltip("Cool Tray 👀 Icon") + .on_click(Events::ClickTrayIcon) + .on_double_click(Events::DoubleClickTrayIcon) + .menu( + MenuBuilder::new() + .item("Item 3 Replace Menu 👍", Events::Item3) + .item("Item 2 Change Icon Green", Events::Item2) + .item("Item 1 Change Icon Red", Events::Item1) + .separator() + .checkable("This is checkable", true, Events::CheckItem1) + .submenu( + "Sub Menu", + MenuBuilder::new() + .item("Sub item 1", Events::SubItem1) + .item("Sub Item 2", Events::SubItem2) + .item("Sub Item 3", Events::SubItem3), + ) + .with(MenuItem::Item { + name: "Item Disabled".into(), + disabled: true, // Disabled entry example + id: Events::Item4, + icon: None, + }) + .separator() + .item("E&xit", Events::Exit), + ) + .build() + .unwrap(); + + TrayIcon { + tray_icon, + message_channel: r, + } + } + + pub fn process_tray_messages(&self) { + unsafe { + let mut msg = MaybeUninit::uninit(); + let bret = winuser::PeekMessageW(msg.as_mut_ptr(), 0 as _, 0, 0, 1); + + if bret > 0 { + winuser::TranslateMessage(msg.as_ptr()); + winuser::DispatchMessageA(msg.as_ptr()); + } else { + return; + } + } + + let message = self.message_channel.recv_timeout(Duration::from_millis(10)); + match message { + Err(_) => return, + Ok(message) => { + match message { + Events::DoubleClickTrayIcon => { + println!("Double click"); + } + Events::ClickTrayIcon => { + println!("Single click"); + } + Events::Exit => { + println!("Please exit"); + todo!() + } + Events::Item1 => { + println!("item1"); + //tray_icon.set_icon(&second_icon).unwrap(); + } + Events::Item2 => { + println!("item2"); + //tray_icon.set_icon(&first_icon).unwrap(); + } + Events::Item3 => { + println!("item3"); + /*tray_icon + .set_menu( + &MenuBuilder::new() + .item("New menu item", Events::Item1) + .item("Exit", Events::Exit), + ) + .unwrap();*/ + } + e => { + println!("{:?}", e); + } + } + } + } + } + +} \ No newline at end of file