fix store dir + feat launch option switch in tray
This commit is contained in:
@@ -111,6 +111,21 @@ pub fn start_watch_loginusers(app: tauri::AppHandle, steam_dir: String) -> Resul
|
||||
wrap_err!(steam::watch::start_watch_loginusers(app, steam_dir))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn start_watch_cs2_video(
|
||||
app: tauri::AppHandle,
|
||||
steam_dir: String,
|
||||
steam_id32: u32,
|
||||
) -> Result<(), String> {
|
||||
wrap_err!(steam::watch::start_watch_cs2_video(app, steam_dir, steam_id32))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn stop_watch_cs2_video() -> Result<(), String> {
|
||||
steam::watch::stop_watch_cs2_video();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_cs2_video_config(steam_dir: &str, steam_id32: u32) -> Result<VideoConfig, String> {
|
||||
let p = format!(
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_cli::CliExt;
|
||||
@@ -38,17 +40,22 @@ fn on_button_clicked() -> String {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// 获取应用上下文
|
||||
let ctx = tauri::generate_context!();
|
||||
|
||||
// 手动构建 AppConfig 目录路径
|
||||
let config_dir = dirs::config_dir().expect("无法获取配置目录");
|
||||
let app_name = ctx.config().identifier.as_str();
|
||||
let store_dir = config_dir.join(app_name).join("cstb");
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_single_instance::init(|app, _, _| {
|
||||
let window = app
|
||||
.get_webview_window("main")
|
||||
.expect("no main window");
|
||||
let window = app.get_webview_window("main").expect("no main window");
|
||||
|
||||
window.show().expect("no main window, can't show");
|
||||
window.show().expect("no main window, can't show");
|
||||
window.set_focus().expect("no main window, can't set focus")
|
||||
}))
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(tauri_plugin_valtio::init())
|
||||
.plugin(tauri_plugin_store::Builder::new().build())
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_autostart::init(
|
||||
@@ -65,24 +72,41 @@ fn main() {
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_system_info::init())
|
||||
.plugin(tauri_plugin_cli::init())
|
||||
// .plugin(tauri_plugin_store::Builder::default().build())
|
||||
// .plugin(tauri_plugin_valtio::init())
|
||||
.plugin(tauri_plugin_valtio::Builder::new().path(&store_dir).build())
|
||||
// .plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.setup(|app| {
|
||||
.setup(move |app| {
|
||||
// Get Window
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
|
||||
let store = app.store("cstb.json")?;
|
||||
// 获取boolean类型的hidden值,Err时设置为False
|
||||
let hidden: bool = store.get("hidden").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
let store = app.store("cstb.json")?;
|
||||
// 获取boolean类型的hidden值,Err时设置为False
|
||||
let hidden: bool = store
|
||||
.get("hidden")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
|
||||
// Vibrant Window
|
||||
// Vibrant Window - 使用更优雅的错误处理和延迟应用
|
||||
#[cfg(target_os = "macos")]
|
||||
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, Some(10.0))
|
||||
.expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
|
||||
{
|
||||
if let Err(e) =
|
||||
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, Some(10.0))
|
||||
{
|
||||
eprintln!("Failed to apply vibrancy effect: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
apply_acrylic(&window, None)
|
||||
.expect("Unsupported platform! 'apply_acrylic' is only supported on Windows");
|
||||
{
|
||||
// 延迟应用 acrylic 效果,确保窗口完全初始化
|
||||
let window_handle = window.clone();
|
||||
thread::spawn(move || {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
if let Err(e) = apply_acrylic(&window_handle, None) {
|
||||
eprintln!("Failed to apply acrylic effect: {:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// apply_blur(&window, Some((18, 18, 18, 0)))
|
||||
// .expect("Unsupported platform! 'apply_blur' is only supported on Windows");
|
||||
@@ -105,7 +129,10 @@ fn main() {
|
||||
// `subcommand` is `Option<Box<SubcommandMatches>>` where `SubcommandMatches` is a struct with { name, matches }.
|
||||
Ok(matches) => {
|
||||
println!("{:?}", matches);
|
||||
if matches.args.contains_key("hidden") && matches.args["hidden"].value == true && hidden {
|
||||
if matches.args.contains_key("hidden")
|
||||
&& matches.args["hidden"].value == true
|
||||
&& hidden
|
||||
{
|
||||
window.hide().unwrap();
|
||||
} else {
|
||||
window.show().unwrap();
|
||||
@@ -130,10 +157,12 @@ fn main() {
|
||||
cmds::get_steam_users,
|
||||
cmds::set_auto_login_user,
|
||||
cmds::start_watch_loginusers,
|
||||
cmds::get_cs2_video_config,
|
||||
cmds::set_cs2_video_config,
|
||||
cmds::start_watch_cs2_video,
|
||||
cmds::stop_watch_cs2_video,
|
||||
cmds::get_cs2_video_config,
|
||||
cmds::set_cs2_video_config,
|
||||
cmds::check_path,
|
||||
cmds::analyze_replay,
|
||||
cmds::analyze_replay,
|
||||
cmds::get_console_log_path,
|
||||
cmds::read_vprof_report,
|
||||
cmds::check_app_update,
|
||||
@@ -141,6 +170,6 @@ fn main() {
|
||||
cmds::install_app_update,
|
||||
on_button_clicked
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.run(ctx)
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use tauri::{AppHandle, Emitter};
|
||||
|
||||
// 全局 watcher 存储,用于管理文件监听器的生命周期
|
||||
static WATCHER: OnceLock<Mutex<Option<RecommendedWatcher>>> = OnceLock::new();
|
||||
static CS2_VIDEO_WATCHER: OnceLock<Mutex<Option<RecommendedWatcher>>> = OnceLock::new();
|
||||
|
||||
/// 启动监听 loginusers.vdf 文件的变化
|
||||
pub fn start_watch_loginusers(app: AppHandle, steam_dir: String) -> Result<()> {
|
||||
@@ -74,3 +75,74 @@ pub fn stop_watch_loginusers() {
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动监听 cs2_video.txt 文件的变化
|
||||
pub fn start_watch_cs2_video(app: AppHandle, steam_dir: String, steam_id32: u32) -> Result<()> {
|
||||
// 停止之前的监听
|
||||
stop_watch_cs2_video();
|
||||
|
||||
let cfg_dir = Path::new(&steam_dir)
|
||||
.join("userdata")
|
||||
.join(steam_id32.to_string())
|
||||
.join("730")
|
||||
.join("local")
|
||||
.join("cfg");
|
||||
|
||||
// 如果 cfg 目录不存在,不进行监听
|
||||
if !cfg_dir.exists() {
|
||||
log::warn!("cs2_video.txt 配置目录不存在,跳过监听: {:?}", cfg_dir);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let app_clone = app.clone();
|
||||
|
||||
let mut watcher = RecommendedWatcher::new(
|
||||
move |result: Result<Event, notify::Error>| {
|
||||
match result {
|
||||
Ok(event) => {
|
||||
// 检查是否是 cs2_video.txt 文件的变化
|
||||
if let EventKind::Modify(_) | EventKind::Create(_) = event.kind {
|
||||
for path in &event.paths {
|
||||
if path.ends_with("cs2_video.txt") {
|
||||
log::info!("检测到 cs2_video.txt 文件变化: {:?}", path);
|
||||
// 延迟一小段时间,确保文件写入完成
|
||||
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||
// 发送事件到前端
|
||||
if let Err(e) = app_clone.emit("steam://cs2_video_changed", ()) {
|
||||
log::error!("发送 cs2_video 变化事件失败: {}", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("文件监听错误: {}", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
Config::default(),
|
||||
)?;
|
||||
|
||||
// 监听 cfg 目录(而不是单个文件),因为某些文件系统可能不会直接监听单个文件
|
||||
watcher.watch(&cfg_dir, RecursiveMode::NonRecursive)?;
|
||||
log::info!("开始监听 cs2_video.txt 文件: {:?}", cfg_dir.join("cs2_video.txt"));
|
||||
|
||||
// 保存 watcher 到全局变量
|
||||
let watcher_store = CS2_VIDEO_WATCHER.get_or_init(|| Mutex::new(None));
|
||||
*watcher_store.lock().unwrap() = Some(watcher);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 停止监听 cs2_video.txt 文件
|
||||
pub fn stop_watch_cs2_video() {
|
||||
if let Some(watcher_store) = CS2_VIDEO_WATCHER.get() {
|
||||
if let Ok(mut watcher_guard) = watcher_store.lock() {
|
||||
if let Some(watcher) = watcher_guard.take() {
|
||||
drop(watcher);
|
||||
log::info!("已停止监听 cs2_video.txt 文件");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
use tauri::{
|
||||
menu::{CheckMenuItem, Menu, MenuItem, PredefinedMenuItem},
|
||||
menu::{CheckMenuItem, Menu, MenuItem, PredefinedMenuItem, Submenu},
|
||||
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
||||
Emitter, Listener, Manager, Runtime,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::tool::powerplan::PowerPlanMode;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct LaunchOption {
|
||||
option: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
// 托盘菜单项目
|
||||
let separator = &PredefinedMenuItem::separator(app).unwrap();
|
||||
@@ -47,13 +54,8 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
)?;
|
||||
|
||||
|
||||
let current_launch_option = MenuItem::with_id(
|
||||
app,
|
||||
"current_launch_option",
|
||||
"启动项档位",
|
||||
true,
|
||||
None::<&str>,
|
||||
)?;
|
||||
// 创建启动项子菜单(初始为空,后续会动态更新)
|
||||
let launch_option_submenu = Submenu::with_id(app, "launch_option_submenu", "启动项: 游戏", true)?;
|
||||
|
||||
// 创建托盘菜单
|
||||
let menu = Menu::with_items(
|
||||
@@ -64,7 +66,7 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
&power_plan_balanced,
|
||||
&power_plan_powersave,
|
||||
separator,
|
||||
¤t_launch_option,
|
||||
&launch_option_submenu,
|
||||
launch_ww_i,
|
||||
launch_pw_i,
|
||||
separator,
|
||||
@@ -114,10 +116,58 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
}
|
||||
});
|
||||
|
||||
let _ = app.listen("tray://get_current_launch_option", move |event| {
|
||||
let payload = event.payload();
|
||||
if payload != "" {
|
||||
let _ = current_launch_option.set_text("启动项档位 ".to_string() + payload);
|
||||
// 监听启动项列表更新事件
|
||||
let launch_option_submenu_clone = launch_option_submenu.clone();
|
||||
let _ = app.listen("tray://update_launch_options", move |event| {
|
||||
if let Ok(data) = serde_json::from_str::<serde_json::Value>(event.payload()) {
|
||||
if let (Some(options), Some(current_index)) = (
|
||||
data.get("options").and_then(|v| v.as_array()),
|
||||
data.get("currentIndex").and_then(|v| v.as_u64()),
|
||||
) {
|
||||
let current_index = current_index as usize;
|
||||
// 获取当前启动项名称
|
||||
let current_name = options
|
||||
.get(current_index)
|
||||
.and_then(|opt| opt.get("name"))
|
||||
.and_then(|n| n.as_str())
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| format!("{}", current_index + 1));
|
||||
|
||||
// 更新子菜单标题
|
||||
let _ = launch_option_submenu_clone.set_text(format!("启动项: {}", current_name));
|
||||
|
||||
// 清空现有子菜单项 - 先收集所有项目,然后移除
|
||||
if let Ok(items) = launch_option_submenu_clone.items() {
|
||||
let items_to_remove: Vec<_> = items.iter().collect();
|
||||
for item in items_to_remove {
|
||||
let _ = launch_option_submenu_clone.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新的子菜单项
|
||||
for (index, option) in options.iter().enumerate() {
|
||||
if let Some(name) = option.get("name").and_then(|n| n.as_str()) {
|
||||
let display_name = if name.is_empty() {
|
||||
format!("{}", index + 1)
|
||||
} else {
|
||||
name.to_string()
|
||||
};
|
||||
|
||||
let item_id = format!("launch_option_{}", index);
|
||||
let app_handle = launch_option_submenu_clone.app_handle();
|
||||
if let Ok(item) = CheckMenuItem::with_id(
|
||||
app_handle,
|
||||
&item_id,
|
||||
&display_name,
|
||||
true,
|
||||
index == current_index,
|
||||
None::<&str>,
|
||||
) {
|
||||
let _ = launch_option_submenu_clone.append(&item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -166,6 +216,12 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
let _ = app.emit("tray://set_powerplan", PowerPlanMode::PowerSaving as i32);
|
||||
// let _ = power_plan_powersave.set_checked(true);
|
||||
}
|
||||
id if id.starts_with("launch_option_") => {
|
||||
// 提取索引
|
||||
if let Ok(index) = id.replace("launch_option_", "").parse::<usize>() {
|
||||
let _ = app.emit("tray://set_launch_index", index);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.on_tray_icon_event(|tray, event| {
|
||||
|
||||
Reference in New Issue
Block a user