]> code.octet-stream.net Git - netwatcher/blob - src/watch_mac.rs
42e7217ad46a09f33c1ce9277da588ce6ac283e9
[netwatcher] / src / watch_mac.rs
1 use std::sync::Mutex;
2
3 use block2::{Block, RcBlock};
4 use objc2::Encoding;
5
6 use crate::{Error, List, Update};
7
8 // The "objc2" project aims to provide bindings for all frameworks but Network.framework
9 // isn't ready yet so let's kick it old-school
10
11 #[repr(C)]
12 struct NwPathMonitor([u8; 0]);
13 type NwPathMonitorT = *mut NwPathMonitor;
14 #[repr(C)]
15 struct NwPath([u8; 0]);
16 #[repr(C)]
17 struct DispatchQueue([u8; 0]);
18 type DispatchQueueT = *mut DispatchQueue;
19 const QOS_CLASS_BACKGROUND: usize = 0x09;
20
21 unsafe impl objc2::Encode for NwPath {
22 const ENCODING: Encoding = usize::ENCODING;
23 }
24
25 #[link(name = "Network", kind = "framework")]
26 extern "C" {
27 fn nw_path_monitor_create() -> NwPathMonitorT;
28 fn nw_path_monitor_set_update_handler(
29 monitor: NwPathMonitorT,
30 update_handler: &Block<dyn Fn(NwPath)>,
31 );
32 fn nw_path_monitor_set_queue(monitor: NwPathMonitorT, queue: DispatchQueueT);
33 fn nw_path_monitor_start(monitor: NwPathMonitorT);
34 fn nw_path_monitor_cancel(monitor: NwPathMonitorT);
35
36 fn dispatch_get_global_queue(identifier: usize, flag: usize) -> DispatchQueueT;
37 }
38
39 pub(crate) struct WatchHandle {
40 path_monitor: NwPathMonitorT,
41 }
42
43 impl Drop for WatchHandle {
44 fn drop(&mut self) {
45 unsafe {
46 nw_path_monitor_cancel(self.path_monitor);
47 }
48 }
49 }
50
51 struct CallbackState {
52 prev_list: List,
53 callback: Box<dyn FnMut(Update) + Send + 'static>,
54 }
55
56 pub(crate) fn watch_interfaces<F: FnMut(Update) + Send + 'static>(
57 callback: F,
58 ) -> Result<WatchHandle, Error> {
59 let state = CallbackState {
60 prev_list: List::default(),
61 callback: Box::new(callback),
62 };
63 // Blocks are Fn, not FnMut
64 let state = Mutex::new(state);
65 let block = RcBlock::new(move |_: NwPath| {
66 let mut state = state.lock().unwrap();
67 let Ok(new_list) = crate::list::list_interfaces() else {
68 return;
69 };
70 if new_list == state.prev_list {
71 return;
72 }
73 let update = Update {
74 interfaces: new_list.0.clone(),
75 diff: new_list.diff_from(&state.prev_list),
76 };
77 (state.callback)(update);
78 state.prev_list = new_list;
79 });
80 let path_monitor: NwPathMonitorT;
81 unsafe {
82 let queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
83 path_monitor = nw_path_monitor_create();
84 nw_path_monitor_set_update_handler(path_monitor, &block);
85 nw_path_monitor_set_queue(path_monitor, queue);
86 nw_path_monitor_start(path_monitor);
87 }
88 Ok(WatchHandle { path_monitor })
89 }