]> code.octet-stream.net Git - netwatcher/blob - src/watch_mac.rs
Mac watching working
[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 struct nw_path_monitor;
12 type nw_path_monitor_t = *mut nw_path_monitor;
13 struct nw_path;
14 struct dispatch_queue;
15 type dispatch_queue_t = *mut dispatch_queue;
16 const QOS_CLASS_BACKGROUND: usize = 0x09;
17
18 unsafe impl objc2::Encode for nw_path {
19 const ENCODING: Encoding = usize::ENCODING;
20 }
21
22 #[link(name = "Network", kind = "framework")]
23 extern "C" {
24 fn nw_path_monitor_create() -> nw_path_monitor_t;
25 fn nw_path_monitor_set_update_handler(
26 monitor: nw_path_monitor_t,
27 update_handler: &Block<dyn Fn(nw_path)>,
28 );
29 fn nw_path_monitor_set_queue(monitor: nw_path_monitor_t, queue: dispatch_queue_t);
30 fn nw_path_monitor_start(monitor: nw_path_monitor_t);
31 fn nw_path_monitor_cancel(monitor: nw_path_monitor_t);
32
33 fn dispatch_get_global_queue(identifier: usize, flag: usize) -> dispatch_queue_t;
34 }
35
36 pub(crate) struct WatchHandle {
37 path_monitor: nw_path_monitor_t,
38 }
39
40 impl Drop for WatchHandle {
41 fn drop(&mut self) {
42 unsafe { nw_path_monitor_cancel(self.path_monitor); }
43 }
44 }
45
46 struct CallbackState {
47 prev_list: List,
48 callback: Box<dyn FnMut(Update) + Send + 'static>,
49 }
50
51 pub(crate) fn watch_interfaces<F: FnMut(Update) + Send + 'static>(
52 callback: F,
53 ) -> Result<WatchHandle, Error> {
54 let state = CallbackState {
55 prev_list: List::default(),
56 callback: Box::new(callback),
57 };
58 // Blocks are Fn, not FnMut
59 let state = Mutex::new(state);
60 let block = RcBlock::new(move |_: nw_path| {
61 let mut state = state.lock().unwrap();
62 let Ok(new_list) = crate::list::list_interfaces() else {
63 return;
64 };
65 if new_list == state.prev_list {
66 return;
67 }
68 let update = Update {
69 interfaces: new_list.0.clone(),
70 diff: new_list.diff_from(&state.prev_list),
71 };
72 (state.callback)(update);
73 state.prev_list = new_list;
74 });
75 let path_monitor: nw_path_monitor_t;
76 unsafe {
77 let queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
78 path_monitor = nw_path_monitor_create();
79 nw_path_monitor_set_update_handler(path_monitor, &block);
80 nw_path_monitor_set_queue(path_monitor, queue);
81 nw_path_monitor_start(path_monitor);
82 }
83 Ok(WatchHandle {
84 path_monitor,
85 })
86 }