termux_notification/
callbacks.rs1mod callback_key;
44mod map;
45
46use std::{
47 env,
48 io::{self, Read},
49 os::unix::net::UnixListener,
50 path::PathBuf,
51 process,
52 str::FromStr,
53 sync::{Mutex, Once},
54 thread,
55};
56
57use crate::{options, TermuxNotification};
58
59use self::{callback_key::CallbackKey, map::Map};
60
61static CB_MAP: Mutex<Map<CallbackKey, Box<dyn Fn() + Send>>> =
62 Mutex::new(Map::new());
63
64pub fn init_socket() {
71 static INIT: Once = Once::new();
72 INIT.call_once(|| {
73 let socket = UnixListener::bind(socket_path()).unwrap();
74 thread::spawn(move || loop {
75 let msg = recv_message(&socket).unwrap();
76 let key = CallbackKey::from_str(&msg);
77 let Ok(key) = key else { continue };
78 let mut cb_map = CB_MAP.lock().unwrap();
79 let Some(f) = cb_map.get(&key) else { continue };
80 f();
81 if key.is_finish_trigger() {
82 cb_map.retain(|(k, _)| k.id() != key.id());
83 }
84 });
85 });
86}
87
88fn socket_path() -> PathBuf {
89 let pid = process::id();
90 env::temp_dir().join(format!("termux_notification.{pid}.socket"))
91}
92
93fn recv_message(socket: &UnixListener) -> io::Result<String> {
94 let (mut connection, _) = socket.accept()?;
95 let buf = &mut String::new();
96 connection.read_to_string(buf)?;
97 Ok(buf.trim().to_owned())
98}
99
100impl TermuxNotification {
101 pub fn action_fn<F>(&mut self, f: F) -> &mut Self
107 where
108 F: Fn() + Send + 'static,
109 {
110 let cmd = self.on(options::ACTION, f);
111 self.action(cmd)
112 }
113
114 pub fn on_delete_fn<F>(&mut self, f: F) -> &mut Self
120 where
121 F: Fn() + Send + 'static,
122 {
123 let cmd = self.on(options::ON_DELETE, f);
124 self.on_delete(cmd)
125 }
126
127 pub fn button1_fn<L, F>(&mut self, label: L, f: F) -> &mut Self
133 where
134 L: Into<String>,
135 F: Fn() + Send + 'static,
136 {
137 let cmd = self.on(options::BUTTON1, f);
138 self.button1(label, cmd)
139 }
140
141 pub fn button2_fn<L, F>(&mut self, label: L, f: F) -> &mut Self
147 where
148 L: Into<String>,
149 F: Fn() + Send + 'static,
150 {
151 let cmd = self.on(options::BUTTON2, f);
152 self.button2(label, cmd)
153 }
154
155 pub fn button3_fn<L, F>(&mut self, label: L, f: F) -> &mut Self
161 where
162 L: Into<String>,
163 F: Fn() + Send + 'static,
164 {
165 let cmd = self.on(options::BUTTON3, f);
166 self.button3(label, cmd)
167 }
168
169 fn on<F>(&mut self, trigger: &str, f: F) -> String
170 where
171 F: Fn() + Send + 'static,
172 {
173 let id = self.get_id_unchecked();
174 let key = CallbackKey::new(id, trigger.to_owned());
175 let socket = socket_path().to_string_lossy().to_string();
176 let cmd = format!(r#"echo "{key}" | nc -UN {socket}"#);
177 CB_MAP.lock().unwrap().insert(key, Box::new(f));
178 cmd
179 }
180
181 fn get_id_unchecked(&self) -> String {
182 self
183 .args
184 .get(options::ID)
185 .cloned()
186 .flatten()
187 .expect("id not provided")
188 }
189}