sharing\natives/
common.rs

1/*
2 * Authors: Jorge.A Duran & Mario Gónzalez
3 * Company: pispas Technologies SL
4 * Date: April 23, 2023
5 * Description: commons definition for singleton.
6 */
7use thiserror::Error;
8
9
10#[derive(Error, Debug)]
11pub enum NativeError {
12    #[error("singleton was owned from another pid")]
13    SingletonLocked,
14    #[error("Unable to get singleton handle")]
15    SingletonUnavailable
16}
17
18pub type NativeResult<T> = crate::PisPasResult<T>;
19
20pub trait Lock {
21    fn lock(&self) -> NativeResult<()>;
22
23    fn unlock(&self)-> NativeResult<()> ;
24}
25use std::process::{Command, Stdio};
26
27pub fn show_message_box_non_blocking(message: &str) {
28    let title = "UNPISPAS SERVICIO IMPRESION";
29
30    #[cfg(target_os = "windows")]
31    show_message_box_windows(message, title);
32
33    #[cfg(target_os = "macos")]
34    show_message_box_macos(message, title);
35
36    #[cfg(target_os = "linux")]
37    show_message_box_linux(message, title);
38
39    #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
40    {
41        tracing::warn!("Message box not supported on this platform: {}", message);
42        println!("{}: {}", title, message);
43    }
44}
45
46#[cfg(target_os = "windows")]
47fn show_message_box_windows(message: &str, title: &str) {
48    let command = format!(
49        "[reflection.assembly]::LoadWithPartialName('System.Windows.Forms')|out-null;[windows.forms.messagebox]::Show('{}', '{}')",
50        message, title
51    );
52
53    let result = Command::new("powershell")
54        .arg("-WindowStyle")
55        .arg("Hidden")
56        .arg("-Command")
57        .arg(command)
58        .stdout(Stdio::null())
59        .stderr(Stdio::piped())
60        .spawn();
61
62    if let Err(e) = result {
63        tracing::error!("Error al ejecutar el comando PowerShell: {}", e);
64    }
65}
66
67#[cfg(target_os = "macos")]
68fn show_message_box_macos(message: &str, title: &str) {
69    // Opción 1: osascript (AppleScript) - más nativo
70    let script = format!(
71        "display dialog \"{}\" with title \"{}\" buttons {{\"OK\"}} default button \"OK\"",
72        message.replace("\"", "\\\""),
73        title.replace("\"", "\\\"")
74    );
75
76    let result = Command::new("osascript")
77        .arg("-e")
78        .arg(script)
79        .stdout(Stdio::null())
80        .stderr(Stdio::piped())
81        .spawn();
82
83    if let Err(e) = result {
84        tracing::error!("Error al ejecutar osascript: {}", e);
85
86        // Fallback: terminal-notifier si está instalado
87        fallback_macos_notification(message, title);
88    }
89}
90
91#[cfg(target_os = "macos")]
92fn fallback_macos_notification(message: &str, title: &str) {
93    // Alternativa usando terminal-notifier (si está instalado)
94    let result = Command::new("terminal-notifier")
95        .arg("-message")
96        .arg(message)
97        .arg("-title")
98        .arg(title)
99        .arg("-sound")
100        .arg("default")
101        .stdout(Stdio::null())
102        .stderr(Stdio::piped())
103        .spawn();
104
105    if let Err(e) = result {
106        tracing::error!("Error con terminal-notifier: {}", e);
107        // Como último recurso, usar say para notificación por voz
108        let _ = Command::new("say")
109            .arg(format!("{}: {}", title, message))
110            .spawn();
111    }
112}
113
114#[cfg(target_os = "linux")]
115fn show_message_box_linux(message: &str, title: &str) {
116    // Intentar diferentes opciones en orden de preferencia
117
118    // Opción 1: zenity (GNOME)
119    if try_zenity(message, title) {
120        return;
121    }
122
123    // Opción 2: kdialog (KDE)
124    if try_kdialog(message, title) {
125        return;
126    }
127
128    // Opción 3: notify-send (notificaciones del sistema)
129    if try_notify_send(message, title) {
130        return;
131    }
132
133    // Opción 4: xmessage (X11 básico)
134    if try_xmessage(message, title) {
135        return;
136    }
137
138    // Fallback: log y print
139    tracing::warn!("No se pudo mostrar message box en Linux: {}", message);
140    println!("{}: {}", title, message);
141}
142
143#[cfg(target_os = "linux")]
144fn try_zenity(message: &str, title: &str) -> bool {
145    Command::new("zenity")
146        .arg("--info")
147        .arg("--title")
148        .arg(title)
149        .arg("--text")
150        .arg(message)
151        .arg("--no-wrap")
152        .stdout(Stdio::null())
153        .stderr(Stdio::piped())
154        .spawn()
155        .is_ok()
156}
157
158#[cfg(target_os = "linux")]
159fn try_kdialog(message: &str, title: &str) -> bool {
160    Command::new("kdialog")
161        .arg("--msgbox")
162        .arg(message)
163        .arg("--title")
164        .arg(title)
165        .stdout(Stdio::null())
166        .stderr(Stdio::piped())
167        .spawn()
168        .is_ok()
169}
170
171#[cfg(target_os = "linux")]
172fn try_notify_send(message: &str, title: &str) -> bool {
173    Command::new("notify-send")
174        .arg(title)
175        .arg(message)
176        .arg("--urgency=normal")
177        .arg("--expire-time=5000") // 5 segundos
178        .stdout(Stdio::null())
179        .stderr(Stdio::piped())
180        .spawn()
181        .is_ok()
182}
183
184#[cfg(target_os = "linux")]
185fn try_xmessage(message: &str, title: &str) -> bool {
186    let full_message = format!("{}\n\n{}", title, message);
187    Command::new("xmessage")
188        .arg("-center")
189        .arg(full_message)
190        .stdout(Stdio::null())
191        .stderr(Stdio::piped())
192        .spawn()
193        .is_ok()
194}
195
196// Función adicional para message boxes síncronos (con respuesta)
197pub fn show_message_box_blocking(message: &str) -> bool {
198    let title = "UNPISPAS SERVICIO IMPRESION";
199
200    #[cfg(target_os = "windows")]
201    return show_message_box_blocking_windows(message, title);
202
203    #[cfg(target_os = "macos")]
204    return show_message_box_blocking_macos(message, title);
205
206    #[cfg(target_os = "linux")]
207    return show_message_box_blocking_linux(message, title);
208
209    #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
210    {
211        println!("{}: {}", title, message);
212        false
213    }
214}
215
216#[cfg(target_os = "windows")]
217fn show_message_box_blocking_windows(message: &str, title: &str) -> bool {
218    let command = format!(
219        "[reflection.assembly]::LoadWithPartialName('System.Windows.Forms')|out-null;[windows.forms.messagebox]::Show('{}', '{}', 'YesNo')",
220        message, title
221    );
222
223    let result = Command::new("powershell")
224        .arg("-WindowStyle")
225        .arg("Hidden")
226        .arg("-Command")
227        .arg(command)
228        .output();
229
230    match result {
231        Ok(output) => {
232            let stdout = String::from_utf8_lossy(&output.stdout);
233            stdout.trim() == "Yes"
234        }
235        Err(e) => {
236            tracing::error!("Error al ejecutar PowerShell: {}", e);
237            false
238        }
239    }
240}
241
242#[cfg(target_os = "macos")]
243fn show_message_box_blocking_macos(message: &str, title: &str) -> bool {
244    let script = format!(
245        "display dialog \"{}\" with title \"{}\" buttons {{\"No\", \"Yes\"}} default button \"Yes\"",
246        message.replace("\"", "\\\""),
247        title.replace("\"", "\\\"")
248    );
249
250    let result = Command::new("osascript")
251        .arg("-e")
252        .arg(script)
253        .output();
254
255    match result {
256        Ok(output) => {
257            let stdout = String::from_utf8_lossy(&output.stdout);
258            stdout.contains("Yes")
259        }
260        Err(e) => {
261            tracing::error!("Error al ejecutar osascript: {}", e);
262            false
263        }
264    }
265}
266
267#[cfg(target_os = "linux")]
268fn show_message_box_blocking_linux(message: &str, title: &str) -> bool {
269    // Intentar zenity primero para Yes/No dialog
270    if let Ok(output) = Command::new("zenity")
271        .arg("--question")
272        .arg("--title")
273        .arg(title)
274        .arg("--text")
275        .arg(message)
276        .output()
277    {
278        return output.status.success();
279    }
280
281    // Fallback a kdialog
282    if let Ok(output) = Command::new("kdialog")
283        .arg("--yesno")
284        .arg(message)
285        .arg("--title")
286        .arg(title)
287        .output()
288    {
289        return output.status.success();
290    }
291
292    // Si nada funciona, usar la versión no-blocking
293    show_message_box_linux(message, title);
294    false
295}