x_win/
lib.rs

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