Skip to content

Commit c45ca8a

Browse files
committed
add InterprocessTokioStream
1 parent 9d5e665 commit c45ca8a

File tree

4 files changed

+118
-2
lines changed

4 files changed

+118
-2
lines changed

RustApp/src/main.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extern crate log;
1818

1919
mod audio;
2020
mod config;
21+
mod single_instance;
2122
mod start_at_login;
2223
mod streamer;
2324
mod ui;
@@ -85,10 +86,14 @@ fn main() {
8586
};
8687
let mut app_lock = LockFile::open(&instance_lock_path).expect("Failed to open app lock file");
8788
if !app_lock.try_lock_with_pid().unwrap_or(false) {
88-
error!(
89+
info!(
8990
"Another instance is already running. PID can be found in {:?}",
9091
instance_lock_path
9192
);
93+
94+
if let Err(e) = single_instance::send_event(single_instance::IpcEvent::Show) {
95+
error!("can't send ipc event {e}");
96+
}
9297
return;
9398
}
9499

RustApp/src/single_instance.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use byteorder::WriteBytesExt;
2+
use futures::stream::Stream;
3+
use interprocess::local_socket::tokio::Stream as InterprocessTokioStream;
4+
use interprocess::local_socket::traits::Stream as InterprocessStreamTrait;
5+
use interprocess::local_socket::traits::tokio::Listener as TokioListener;
6+
use interprocess::local_socket::{GenericNamespaced, ListenerOptions, ToNsName};
7+
use interprocess::local_socket::{Name, Stream as InterprocessStream};
8+
9+
use tokio::io::AsyncReadExt;
10+
11+
#[derive(Debug, Clone)]
12+
pub enum IpcEvent {
13+
Show,
14+
}
15+
16+
impl TryFrom<u8> for IpcEvent {
17+
type Error = ();
18+
19+
fn try_from(value: u8) -> Result<Self, Self::Error> {
20+
match value {
21+
0 => Ok(IpcEvent::Show),
22+
_ => Err(()),
23+
}
24+
}
25+
}
26+
27+
impl From<IpcEvent> for u8 {
28+
fn from(value: IpcEvent) -> Self {
29+
match value {
30+
IpcEvent::Show => 0,
31+
}
32+
}
33+
}
34+
35+
fn get_name() -> anyhow::Result<Name<'static>> {
36+
let printname = "android-mic.sock";
37+
let name = printname.to_ns_name::<GenericNamespaced>()?;
38+
Ok(name)
39+
}
40+
41+
pub async fn create_stream() -> anyhow::Result<InterprocessTokioStream> {
42+
let name = get_name()?;
43+
44+
let opts = ListenerOptions::new().name(name);
45+
46+
let listener = opts.create_tokio()?;
47+
48+
let stream = listener.accept().await?;
49+
Ok(stream)
50+
}
51+
52+
pub fn parse_stream(stream: InterprocessTokioStream) -> impl Stream<Item = IpcEvent> {
53+
futures::stream::unfold(stream, |mut stream| async {
54+
match stream.read_u8().await {
55+
Ok(value) => match value.try_into() {
56+
Ok(event) => Some((event, stream)),
57+
Err(()) => {
58+
error!("can't parse ipc event");
59+
None
60+
}
61+
},
62+
Err(e) => {
63+
error!("{e}");
64+
None
65+
}
66+
}
67+
})
68+
}
69+
70+
pub fn send_event(event: IpcEvent) -> anyhow::Result<()> {
71+
let name = get_name()?;
72+
73+
let mut stream = InterprocessStream::connect(name)?;
74+
75+
stream.write_u8(event.into())?;
76+
77+
Ok(())
78+
}

RustApp/src/ui/app.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::{
3333
config::{
3434
AppTheme, AudioFormat, ChannelCount, Config, ConnectionMode, NetworkAdapter, SampleRate,
3535
},
36-
fl,
36+
fl, single_instance,
3737
streamer::{self, ConnectOption, StreamerCommand, StreamerMsg},
3838
ui::view::{SCROLLABLE_ID, about_window},
3939
utils::APP_ID,
@@ -407,6 +407,24 @@ impl Application for AppState {
407407
commands.push(app.open_main_window());
408408
}
409409

410+
commands.push(
411+
cosmic::iced::task::Task::future(async { single_instance::create_stream().await })
412+
.then(|stream| match stream {
413+
Ok(stream) => cosmic::iced::task::Task::run(
414+
single_instance::parse_stream(stream),
415+
|event| match event {
416+
single_instance::IpcEvent::Show => {
417+
cosmic::Action::App(AppMsg::ShowWindow)
418+
}
419+
},
420+
),
421+
Err(e) => {
422+
error!("{e}");
423+
Task::none()
424+
}
425+
}),
426+
);
427+
410428
(app, Task::batch(commands))
411429
}
412430

@@ -763,6 +781,20 @@ impl Application for AppState {
763781
SystemTrayMsg::Connect => return self.connect(),
764782
SystemTrayMsg::Disconnect => return self.disconnect(),
765783
},
784+
AppMsg::ShowWindow => {
785+
if let Some(main_window) = &self.main_window {
786+
// avoid duplicate window
787+
return cosmic::iced_runtime::task::effect(
788+
cosmic::iced::runtime::Action::Window(window::Action::GainFocus(
789+
main_window.window_id,
790+
)),
791+
);
792+
} else {
793+
let command = self.open_main_window();
794+
795+
return Task::batch(vec![command, self.update_audio_stream()]);
796+
}
797+
}
766798
}
767799

768800
Task::none()

RustApp/src/ui/message.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub enum AppMsg {
2121
RefreshAudioDevices,
2222
RefreshNetworkAdapters,
2323
HideWindow,
24+
ShowWindow,
2425
Menu(MenuMsg),
2526
LinkClicked(String),
2627
SystemTray(SystemTrayMsg),

0 commit comments

Comments
 (0)