Skip to main content

fast_down_gui/
ipc.rs

1use crate::{
2    core::{App, start_new_entry},
3    os::wakeup_window,
4    ui::EntryData,
5    utils::LogErr,
6};
7use crossfire::mpsc;
8use interprocess::local_socket::{
9    GenericNamespaced, ListenerOptions,
10    tokio::{Stream, prelude::*},
11};
12use serde::{Deserialize, Serialize};
13use slint::VecModel;
14use std::{process::exit, rc::Rc};
15use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
16use url::Url;
17
18pub const NS_NAME: &str = "top.s121.fd.sock";
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21#[serde(rename_all = "camelCase")]
22pub struct DownloadOptions {
23    pub url: Url,
24    pub headers: Option<String>,
25    pub save_dir: Option<String>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29#[serde(tag = "type")]
30pub enum IpcMessage {
31    WakeUp,
32    Download(DownloadOptions),
33}
34
35/// 这是用户正常双击运行软件时,检查是否已经有在运行的实例
36pub async fn check_ipc_and_wake() -> color_eyre::Result<()> {
37    let ns_name = NS_NAME.to_ns_name::<GenericNamespaced>()?;
38    if let Ok(mut stream) = Stream::connect(ns_name).await {
39        tracing::info!("发现已有实例,正在发送唤醒信号...");
40        let msg = IpcMessage::WakeUp;
41        if let Ok(json) = serde_json::to_string(&msg) {
42            let _ = stream.write_all(format!("{json}\n").as_bytes()).await;
43        }
44        exit(0);
45    }
46    Ok(())
47}
48
49/// 监听其他实例(或浏览器代理进程)发来的 IPC 请求
50pub async fn init_ipc(app: App, list_model: Rc<VecModel<EntryData>>) -> color_eyre::Result<()> {
51    let ns_name = NS_NAME.to_ns_name::<GenericNamespaced>()?;
52    let listener = ListenerOptions::new()
53        .name(ns_name)
54        .try_overwrite(true)
55        .create_tokio()?;
56
57    let (tx, rx) = mpsc::unbounded_async::<IpcMessage>();
58
59    let ui_weak = app.ui.clone();
60    slint::spawn_local(async move {
61        while let Ok(msg) = rx.recv().await {
62            match msg {
63                IpcMessage::WakeUp => {
64                    tracing::info!("收到唤醒信号");
65                    let _ = ui_weak.upgrade_in_event_loop(|ui| wakeup_window(&ui));
66                }
67                IpcMessage::Download(e) => {
68                    tracing::info!("收到外部下载请求: {}", e.url);
69                    let mut config = app.db.get_ui_download_config();
70                    if let Some(s) = e.headers {
71                        config.headers = s.into();
72                    }
73                    if let Some(s) = e.save_dir {
74                        config.save_dir = s.into();
75                    }
76                    start_new_entry(&app, e.url, &config, &list_model);
77                }
78            }
79        }
80    })
81    .log_err("IPC 消息处理任务失败")?;
82
83    tokio::spawn(async move {
84        loop {
85            match listener.accept().await {
86                Ok(conn) => {
87                    let tx = tx.clone();
88                    tokio::spawn(async move {
89                        let mut reader = BufReader::new(conn);
90                        let mut buffer = String::new();
91                        if reader.read_line(&mut buffer).await.is_ok()
92                            && let Ok(msg) = serde_json::from_str::<IpcMessage>(&buffer)
93                        {
94                            let _ = tx.send(msg);
95                        }
96                    });
97                }
98                Err(e) => tracing::error!(err = ?e, "监听连接出错"),
99            }
100        }
101    });
102    Ok(())
103}