Release 0.1.1: hide client in system tray
This commit is contained in:
Generated
+2
-1
@@ -1804,9 +1804,10 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "minivless"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dbus",
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "minivless"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
description = "A small personal VLESS client for Linux"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
@@ -8,13 +8,14 @@ rust-version = "1.85"
|
||||
|
||||
[features]
|
||||
default = ["desktop"]
|
||||
desktop = ["dep:tauri", "dep:tauri-build"]
|
||||
desktop = ["dep:tauri", "dep:tauri-build", "dep:dbus"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", optional = true , features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", optional = true , features = [] }
|
||||
tauri = { version = "2", optional = true , features = ["tray-icon"] }
|
||||
dbus = { version = "0.9", optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
url = "2"
|
||||
|
||||
+81
-2
@@ -10,7 +10,83 @@ use std::{path::PathBuf, sync::Arc};
|
||||
mod desktop {
|
||||
use super::*;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use tauri::{Manager, State};
|
||||
use tauri::{
|
||||
menu::{Menu, MenuItem},
|
||||
tray::TrayIconBuilder,
|
||||
Manager, State,
|
||||
};
|
||||
|
||||
struct TrayReady(bool);
|
||||
|
||||
fn tray_host_available() -> bool {
|
||||
use std::time::Duration;
|
||||
let Ok(connection) = dbus::blocking::Connection::new_session() else {
|
||||
return false;
|
||||
};
|
||||
let proxy = connection.with_proxy(
|
||||
"org.freedesktop.DBus",
|
||||
"/org/freedesktop/DBus",
|
||||
Duration::from_millis(300),
|
||||
);
|
||||
let reply: Result<(bool,), _> = proxy.method_call(
|
||||
"org.freedesktop.DBus",
|
||||
"NameHasOwner",
|
||||
("org.kde.StatusNotifierWatcher",),
|
||||
);
|
||||
matches!(reply, Ok((true,)))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn hide_window(
|
||||
app: tauri::AppHandle,
|
||||
window: tauri::WebviewWindow,
|
||||
) -> Result<(), String> {
|
||||
let tray_ready = app.state::<TrayReady>().0;
|
||||
let has_host = tray_ready
|
||||
&& tauri::async_runtime::spawn_blocking(tray_host_available)
|
||||
.await
|
||||
.unwrap_or(false);
|
||||
// A tray icon can be created even when GNOME has no indicator extension.
|
||||
// Keep the window recoverable from the taskbar in that case.
|
||||
if has_host {
|
||||
window.hide()
|
||||
} else {
|
||||
window.minimize()
|
||||
}
|
||||
.map_err(|_| "Cannot hide the window".into())
|
||||
}
|
||||
|
||||
fn create_tray(app: &tauri::App) -> tauri::Result<()> {
|
||||
let show = MenuItem::with_id(app, "show", "Show MiniVLESS", true, None::<&str>)?;
|
||||
let disconnect = MenuItem::with_id(app, "disconnect", "Disconnect", true, None::<&str>)?;
|
||||
let quit = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
|
||||
let menu = Menu::with_items(app, &[&show, &disconnect, &quit])?;
|
||||
let mut tray = TrayIconBuilder::with_id("minivless-tray")
|
||||
.menu(&menu)
|
||||
.tooltip("MiniVLESS");
|
||||
if let Some(icon) = app.default_window_icon() {
|
||||
tray = tray.icon(icon.clone());
|
||||
}
|
||||
tray.on_menu_event(|app, event| match event.id.as_ref() {
|
||||
"show" => {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.unminimize();
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
}
|
||||
"disconnect" => app.state::<Arc<AppState>>().disconnect(),
|
||||
// Reuse the existing CloseRequested handler, which awaits core shutdown.
|
||||
"quit" => {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.close();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.build(app)?;
|
||||
Ok(())
|
||||
}
|
||||
#[tauri::command]
|
||||
async fn list_applications() -> Result<Vec<ApplicationInfo>, String> {
|
||||
tauri::async_runtime::spawn_blocking(processes::list_running)
|
||||
@@ -79,7 +155,8 @@ mod desktop {
|
||||
get_status,
|
||||
get_logs,
|
||||
load_preferences,
|
||||
save_preferences
|
||||
save_preferences,
|
||||
hide_window
|
||||
])
|
||||
.setup(move |app| {
|
||||
let resources = app.path().resource_dir().ok();
|
||||
@@ -89,6 +166,8 @@ mod desktop {
|
||||
std::env::current_exe()?,
|
||||
);
|
||||
app.manage(state.clone());
|
||||
// Lack of tray support must never prevent the VPN client from starting.
|
||||
app.manage(TrayReady(create_tray(app).is_ok()));
|
||||
let signals = Arc::new(AtomicBool::new(false));
|
||||
for sig in [libc::SIGINT, libc::SIGTERM, libc::SIGHUP] {
|
||||
signal_hook::flag::register(sig, signals.clone())?;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "MiniVLESS",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"identifier": "dev.minivless.desktop",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
|
||||
Reference in New Issue
Block a user