mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-06-28 01:19:58 +02:00
159 lines
3.9 KiB
Rust
159 lines
3.9 KiB
Rust
use muda::{Menu, MenuItem, PredefinedMenuItem};
|
|
use std::process::Command;
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
use tray_icon::{Icon, TrayIcon, TrayIconBuilder};
|
|
|
|
static GUI_RUNNING: AtomicBool = AtomicBool::new(false);
|
|
|
|
pub fn load_icon() -> Icon {
|
|
// On Windows, use the full-color icon so it renders well on dark taskbars.
|
|
// On macOS/Linux, use the template icon (black with alpha) for system light/dark handling.
|
|
#[cfg(target_os = "windows")]
|
|
let icon_bytes = include_bytes!("../../icons/tray-icon-win-44.png");
|
|
#[cfg(not(target_os = "windows"))]
|
|
let icon_bytes = include_bytes!("../../icons/tray-icon-44.png");
|
|
|
|
let image = image::load_from_memory(icon_bytes)
|
|
.expect("Failed to load icon")
|
|
.into_rgba8();
|
|
|
|
let (width, height) = image.dimensions();
|
|
let rgba = image.into_raw();
|
|
|
|
Icon::from_rgba(rgba, width, height).expect("Failed to create icon")
|
|
}
|
|
|
|
pub struct TrayMenu {
|
|
pub menu: Menu,
|
|
pub open_item: MenuItem,
|
|
pub quit_item: MenuItem,
|
|
}
|
|
|
|
impl Default for TrayMenu {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl TrayMenu {
|
|
pub fn new() -> Self {
|
|
let menu = Menu::new();
|
|
|
|
let open_item = MenuItem::new("Open Donut Browser", true, None);
|
|
let separator = PredefinedMenuItem::separator();
|
|
let quit_item = MenuItem::new("Quit Donut Browser", true, None);
|
|
|
|
menu.append(&open_item).unwrap();
|
|
menu.append(&separator).unwrap();
|
|
menu.append(&quit_item).unwrap();
|
|
|
|
Self {
|
|
menu,
|
|
open_item,
|
|
quit_item,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn create_tray_icon(icon: Icon, menu: &Menu) -> TrayIcon {
|
|
let builder = TrayIconBuilder::new()
|
|
.with_icon(icon)
|
|
.with_tooltip("Donut Browser")
|
|
.with_menu(Box::new(menu.clone()));
|
|
|
|
// On macOS, template icons are automatically colored by the system for light/dark mode
|
|
#[cfg(target_os = "macos")]
|
|
let builder = builder.with_icon_as_template(true);
|
|
|
|
builder.build().expect("Failed to create tray icon")
|
|
}
|
|
|
|
pub fn open_gui() {
|
|
if GUI_RUNNING.load(Ordering::SeqCst) {
|
|
log::info!("GUI already running, activating...");
|
|
activate_gui();
|
|
return;
|
|
}
|
|
|
|
log::info!("Opening GUI...");
|
|
|
|
#[cfg(target_os = "macos")]
|
|
{
|
|
let _ = Command::new("open").arg("-a").arg("Donut Browser").spawn();
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
use std::path::PathBuf;
|
|
|
|
// In dev mode, find the main exe next to the daemon binary
|
|
if let Ok(current_exe) = std::env::current_exe() {
|
|
if let Some(exe_dir) = current_exe.parent() {
|
|
let app_path = exe_dir.join("donutbrowser.exe");
|
|
if app_path.exists() {
|
|
let _ = Command::new(app_path).spawn();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
let paths = [
|
|
dirs::data_local_dir().map(|p| p.join("Donut Browser").join("Donut Browser.exe")),
|
|
Some(PathBuf::from(
|
|
"C:\\Program Files\\Donut Browser\\Donut Browser.exe",
|
|
)),
|
|
];
|
|
|
|
for path in paths.iter().flatten() {
|
|
if path.exists() {
|
|
let _ = Command::new(path).spawn();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
let _ = Command::new("donutbrowser").spawn();
|
|
}
|
|
}
|
|
|
|
pub fn activate_gui() {
|
|
#[cfg(target_os = "macos")]
|
|
{
|
|
let _ = Command::new("osascript")
|
|
.args(["-e", "tell application \"Donut Browser\" to activate"])
|
|
.spawn();
|
|
}
|
|
}
|
|
|
|
pub fn quit_gui() {
|
|
log::info!("[daemon] Quitting GUI...");
|
|
|
|
#[cfg(target_os = "macos")]
|
|
{
|
|
let _ = Command::new("osascript")
|
|
.args(["-e", "tell application \"Donut Browser\" to quit"])
|
|
.output();
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
let _ = Command::new("taskkill")
|
|
.args(["/IM", "Donut.exe", "/F"])
|
|
.output();
|
|
let _ = Command::new("taskkill")
|
|
.args(["/IM", "donutbrowser.exe", "/F"])
|
|
.output();
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
let _ = Command::new("pkill").args(["-x", "donutbrowser"]).output();
|
|
}
|
|
}
|
|
|
|
pub fn set_gui_running(running: bool) {
|
|
GUI_RUNNING.store(running, Ordering::SeqCst);
|
|
}
|