x_win/
lib.rs

1#![deny(unsafe_op_in_unsafe_fn)]
2
3#[cfg(target_os = "macos")]
4#[macro_use]
5extern crate objc2;
6
7#[cfg(target_os = "macos")]
8#[macro_use]
9extern crate core;
10
11mod common;
12
13#[cfg(target_os = "windows")]
14mod win32;
15
16#[cfg(target_os = "linux")]
17mod linux;
18
19#[cfg(target_os = "macos")]
20mod macos;
21
22#[cfg(target_os = "windows")]
23use win32::init_platform_api;
24
25#[cfg(target_os = "linux")]
26use linux::init_platform_api;
27
28#[cfg(target_os = "macos")]
29use macos::init_platform_api;
30
31#[cfg(all(feature = "macos_permission", target_os = "macos"))]
32/// Handle screen record permission
33/// <div class="warning">important Work only with macOS 10.15+</div>
34/// To use this function you need to add `macos_permission` feature
35pub use macos::permission;
36
37pub use common::{
38  api::{empty_entity, os_name, Api},
39  result::Result,
40  x_win_struct::{
41    icon_info::IconInfo, process_info::ProcessInfo, usage_info::UsageInfo, window_info::WindowInfo,
42    window_position::WindowPosition,
43  },
44};
45
46/**
47 * Recover icon of window.
48 * Return `IconInfo`
49 */
50pub fn get_window_icon(window_info: &WindowInfo) -> Result<IconInfo> {
51  let api = init_platform_api();
52  let icon_info = api.get_app_icon(window_info)?;
53  Ok(icon_info)
54}
55
56/**
57 * Recover browser url of window.
58 * Return `String`
59 */
60pub fn get_browser_url(window_info: &WindowInfo) -> Result<String> {
61  let api = init_platform_api();
62  let browser_url = api.get_browser_url(window_info)?;
63  Ok(browser_url)
64}
65
66/**
67 * Retrieve information the about currently active window.
68 * Return `WindowInfo` containing details about a specific active window.
69 */
70pub fn get_active_window() -> Result<WindowInfo> {
71  let api = init_platform_api();
72  let active_window = api.get_active_window()?;
73  Ok(active_window)
74}
75
76/**
77 * Retrieve information about the currently open windows.
78 * Return `Vec<WindowInfo>` each containing details about a specific open window.
79 */
80pub fn get_open_windows() -> Result<Vec<WindowInfo>> {
81  let api = init_platform_api();
82  let open_windows = api.get_open_windows()?;
83  Ok(open_windows)
84}
85
86/**
87 * Install "@mininben90/x-win" Gnome extension required for Linux using Gnome > 41.
88 * This function will write extension files needed to correctly detect working windows with Wayland desktop environment.
89 * **Restart session will be require to install the gnome extension.**
90 */
91pub fn install_extension() -> Result<bool> {
92  #[cfg(not(target_os = "linux"))]
93  {
94    Ok(false)
95  }
96  #[cfg(target_os = "linux")]
97  {
98    linux::gnome_install_extension()
99  }
100}
101
102/**
103 * Uninstall "@mininben90/x-win" Gnome extension.
104 * This function will disable and remove extension files.
105 * **Restart session will be require to remove the gnome extension.**
106 */
107pub fn uninstall_extension() -> Result<bool> {
108  #[cfg(not(target_os = "linux"))]
109  {
110    Ok(false)
111  }
112  #[cfg(target_os = "linux")]
113  {
114    linux::gnome_uninstall_extension()
115  }
116}
117
118/**
119 * Enable Gnome extension required for Linux using Gnome > 41.
120 * This function will enable extension needed to correctly detect working windows with Wayland desktop environment.
121 */
122pub fn enable_extension() -> Result<bool> {
123  #[cfg(not(target_os = "linux"))]
124  {
125    Ok(false)
126  }
127  #[cfg(target_os = "linux")]
128  {
129    linux::gnome_enable_extension()
130  }
131}
132
133/**
134 * Disable Gnome extension required for Linux using Gnome > 41.
135 * This function will disable extension needed to correctly detect working windows with Wayland desktop environment.
136 */
137pub fn disable_extension() -> Result<bool> {
138  #[cfg(not(target_os = "linux"))]
139  {
140    Ok(false)
141  }
142  #[cfg(target_os = "linux")]
143  {
144    linux::gnome_disable_extension()
145  }
146}
147
148/**
149 * Return true of false if gnome extension is enabled for Linux using Gnome > 41.
150 * This function will return true or false if the extension is set to enabled on extension info. Working only with Wayland windows manager.
151 */
152pub fn is_enabled_extension() -> Result<bool> {
153  #[cfg(not(target_os = "linux"))]
154  {
155    Ok(false)
156  }
157  #[cfg(target_os = "linux")]
158  {
159    linux::gnome_is_enabled_extension()
160  }
161}
162
163/**
164 * Return true of false the extensions is installed for Linux using Gnome > 41.
165 * This function will return true or false if the extension is correctly installed. Working only with Wayland windows manager.
166 */
167pub fn is_installed_extension() -> Result<bool> {
168  #[cfg(not(target_os = "linux"))]
169  {
170    Ok(false)
171  }
172  #[cfg(target_os = "linux")]
173  {
174    linux::gnome_is_installed_extension()
175  }
176}
177
178#[cfg(test)]
179mod tests {
180  use super::*;
181  #[cfg(not(target_os = "linux"))]
182  use std::process::Command;
183  #[cfg(not(target_os = "linux"))]
184  use std::{thread, time};
185
186  #[cfg(not(target_os = "linux"))]
187  struct TestContext;
188
189  #[cfg(not(target_os = "linux"))]
190  impl TestContext {
191    fn setup() -> Self {
192      let output = if cfg!(target_os = "windows") {
193        Command::new("cmd")
194          .args([
195            "/C",
196            "start",
197            "microsoft-edge:https://github.com",
198            "--no-first-run",
199            "--restore-last-session",
200          ])
201          .output()
202          .expect("failed to execute process")
203      } else {
204        Command::new("open")
205          .args(["-a", "Safari", "https://github.com"])
206          .output()
207          .expect("failed to execute process")
208      };
209      println!(
210        "[START] Command Status: {:?}; Command stdout: {:?}; Command stderr: {:?}",
211        output.status,
212        std::str::from_utf8(&output.stdout).unwrap_or("Error when convert output"),
213        std::str::from_utf8(&output.stderr).unwrap_or("Error when convert stderr")
214      );
215      thread::sleep(time::Duration::from_secs(3));
216      TestContext
217    }
218  }
219
220  #[cfg(not(target_os = "linux"))]
221  impl Drop for TestContext {
222    fn drop(&mut self) {
223      let output = if cfg!(target_os = "windows") {
224        Command::new("cmd")
225          .args(["/C", "taskkill", "/f", "/im", "msedge.exe"])
226          .output()
227          .expect("failed to execute process")
228      } else {
229        Command::new("killall")
230          .args(["Safari"])
231          .output()
232          .expect("failed to execute process")
233      };
234      println!(
235        "[DONE] Command Status: {:?}; Command stdout: {:?}; Command stderr: {:?}",
236        output.status,
237        std::str::from_utf8(&output.stdout).unwrap_or("Error when convert output"),
238        std::str::from_utf8(&output.stderr).unwrap_or("Error when convert stderr")
239      );
240      thread::sleep(time::Duration::from_secs(3));
241    }
242  }
243
244  fn test_osname() -> String {
245    #[cfg(target_os = "linux")]
246    {
247      r#"linux"#.to_owned()
248    }
249    #[cfg(target_os = "macos")]
250    {
251      r#"darwin"#.to_owned()
252    }
253    #[cfg(target_os = "windows")]
254    {
255      r#"win32"#.to_owned()
256    }
257  }
258
259  fn test_struct(window_info: WindowInfo) -> Result<()> {
260    assert_ne!(window_info.id, 0);
261    assert_ne!(window_info.title, String::from(""));
262    #[cfg(target_os = "linux")]
263    assert_eq!(window_info.os, r#"linux"#);
264    #[cfg(target_os = "macos")]
265    assert_eq!(window_info.os, r#"darwin"#);
266    #[cfg(target_os = "windows")]
267    assert_eq!(window_info.os, r#"win32"#);
268    Ok(())
269  }
270
271  #[test]
272  fn test_get_active_window() -> Result<()> {
273    let window_info = get_active_window().unwrap();
274    test_struct(window_info)
275  }
276
277  #[test]
278  fn test_get_open_windows() -> Result<()> {
279    let open_windows = get_open_windows().unwrap();
280    assert_ne!(open_windows.len(), 0);
281    let window_info = open_windows.first().unwrap().to_owned();
282    test_struct(window_info)
283  }
284
285  #[test]
286  fn test_os_name() -> Result<()> {
287    let os_name = os_name();
288    assert_eq!(os_name, test_osname());
289    Ok(())
290  }
291
292  #[test]
293  fn test_empty_entity() -> Result<()> {
294    let window_info = empty_entity();
295    assert_eq!(window_info.id, 0);
296    assert_eq!(window_info.title, String::from(""));
297    assert_eq!(window_info.os, test_osname());
298    Ok(())
299  }
300
301  #[test]
302  fn test_get_window_icon() -> Result<()> {
303    let window_info: &WindowInfo = &get_active_window().unwrap();
304    let icon_info = get_window_icon(window_info).unwrap();
305    assert_ne!(icon_info.data, "");
306    assert_ne!(icon_info.height, 0);
307    assert_ne!(icon_info.width, 0);
308    let open_windows = &get_open_windows().unwrap();
309    assert_ne!(open_windows.len(), 0);
310    let window_info = open_windows.first().unwrap().to_owned();
311    let icon_info = get_window_icon(&window_info).unwrap();
312    assert_ne!(icon_info.data, "");
313    assert_ne!(icon_info.height, 0);
314    assert_ne!(icon_info.width, 0);
315    Ok(())
316  }
317
318  #[cfg(not(target_os = "linux"))]
319  #[test]
320  #[ignore = "Not working on ci/cd"]
321  fn test_get_brower_url() -> Result<()> {
322    #[allow(unused)]
323    let _context = TestContext::setup();
324    let open_windows = &get_open_windows().unwrap();
325    assert_ne!(open_windows.len(), 0);
326    let window_info = open_windows.first().unwrap().to_owned();
327    let url = get_browser_url(&window_info).unwrap();
328    println!("URL: {:?}; process: {:?}", url, window_info.info.name);
329    assert!(url.starts_with("http"));
330    let window_info = &get_active_window().unwrap().to_owned();
331    let url = get_browser_url(window_info).unwrap();
332    println!("URL: {:?}; process: {:?}", url, window_info.info.name);
333    assert!(url.starts_with("http"));
334    Ok(())
335  }
336
337  #[cfg(target_os = "linux")]
338  #[test]
339  fn test_get_brower_url() -> Result<()> {
340    let open_windows = &get_open_windows().unwrap();
341    assert_ne!(open_windows.len(), 0);
342    let window_info = open_windows.first().unwrap().to_owned();
343    let url = get_browser_url(&window_info).unwrap();
344    assert!(url.eq("URL recovery not supported on Linux distribution!"));
345    let window_info = &get_active_window().unwrap().to_owned();
346    let url = get_browser_url(&window_info).unwrap();
347    assert!(url.eq("URL recovery not supported on Linux distribution!"));
348    Ok(())
349  }
350
351  #[cfg(all(feature = "macos_permission", target_os = "macos"))]
352  #[test]
353  #[ignore = "Not working on ci/cd"]
354  fn test_check_screen_record_permission() -> Result<()> {
355    use macos::permission::check_screen_record_permission;
356    let value = check_screen_record_permission();
357    assert_eq!(value, true);
358    Ok(())
359  }
360
361  #[cfg(all(feature = "macos_permission", target_os = "macos"))]
362  #[test]
363  #[ignore = "Not working on ci/cd"]
364  fn test_request_screen_record_permission() -> Result<()> {
365    use macos::permission::request_screen_record_permission;
366    let value = request_screen_record_permission();
367    assert_eq!(value, true);
368    Ok(())
369  }
370
371  #[cfg(target_os = "linux")]
372  #[test]
373  #[ignore = "Not working on ci/cd"]
374  fn test_install_extension() -> Result<()> {
375    let value = install_extension()?;
376    assert_eq!(value, true);
377    Ok(())
378  }
379
380  #[cfg(target_os = "linux")]
381  #[test]
382  #[ignore = "Not working on ci/cd"]
383  fn test_uninstall_extension() -> Result<()> {
384    let value = uninstall_extension()?;
385    assert_eq!(value, true);
386    Ok(())
387  }
388
389  #[cfg(target_os = "linux")]
390  #[test]
391  #[ignore = "Not working on ci/cd"]
392  fn test_enable_extension() -> Result<()> {
393    let value = enable_extension()?;
394    assert_eq!(value, true);
395    Ok(())
396  }
397
398  #[cfg(target_os = "linux")]
399  #[test]
400  #[ignore = "Not working on ci/cd"]
401  fn test_disable_extension() -> Result<()> {
402    let value = disable_extension()?;
403    assert_eq!(value, true);
404    Ok(())
405  }
406
407  #[cfg(target_os = "linux")]
408  #[test]
409  #[ignore = "Not working on ci/cd"]
410  fn test_is_enabled_extension() -> Result<()> {
411    let value = is_enabled_extension()?;
412    assert_eq!(value, true);
413    Ok(())
414  }
415
416  #[cfg(target_os = "linux")]
417  #[test]
418  #[ignore = "Not working on ci/cd"]
419  fn test_is_installed_extension() -> Result<()> {
420    let value = is_installed_extension()?;
421    assert_eq!(value, true);
422    Ok(())
423  }
424}