From: Thomas Karpiniec Date: Sat, 15 Jun 2024 22:48:14 +0000 (+1000) Subject: Mac watching working X-Git-Tag: v0.1.0~12 X-Git-Url: https://code.octet-stream.net/netwatcher/commitdiff_plain/e29a1fae66084be615adffec6b32f477c8c16277?ds=sidebyside;hp=ddd2c284f693954108371767a183b430b93a7b72 Mac watching working --- diff --git a/Cargo.lock b/Cargo.lock index 632fe83..b24c7bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,7 @@ version = "0.1.0" dependencies = [ "block2", "nix", + "objc2", "windows", ] diff --git a/Cargo.toml b/Cargo.toml index 45cff20..78a17e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ nix = { version = "0.29.0", features = ["net"] } [target.'cfg(target_vendor = "apple")'.dependencies] block2 = "0.5.1" +objc2 = "0.5.2" [target.'cfg(windows)'.dependencies.windows] version = "0.56.0" diff --git a/src/list_win.rs b/src/list_win.rs index 2ae79a6..8f0b783 100644 --- a/src/list_win.rs +++ b/src/list_win.rs @@ -105,14 +105,3 @@ pub(crate) fn list_interfaces() -> Result { Ok(List(ifs)) } - -#[cfg(test)] -mod test { - use super::list_interfaces; - - #[test] - fn list() { - let ifaces = list_interfaces().unwrap().0; - println!("{:?}", ifaces); - } -} diff --git a/src/watch_mac.rs b/src/watch_mac.rs index ed2879c..7a984e5 100644 --- a/src/watch_mac.rs +++ b/src/watch_mac.rs @@ -1,4 +1,9 @@ -use crate::Update; +use std::sync::Mutex; + +use block2::{Block, RcBlock}; +use objc2::Encoding; + +use crate::{Error, List, Update}; // The "objc2" project aims to provide bindings for all frameworks but Network.framework // isn't ready yet so let's kick it old-school @@ -6,17 +11,20 @@ use crate::Update; struct nw_path_monitor; type nw_path_monitor_t = *mut nw_path_monitor; struct nw_path; -type nw_path_t = *mut nw_path; struct dispatch_queue; type dispatch_queue_t = *mut dispatch_queue; const QOS_CLASS_BACKGROUND: usize = 0x09; +unsafe impl objc2::Encode for nw_path { + const ENCODING: Encoding = usize::ENCODING; +} + #[link(name = "Network", kind = "framework")] extern "C" { fn nw_path_monitor_create() -> nw_path_monitor_t; fn nw_path_monitor_set_update_handler( monitor: nw_path_monitor_t, - update_handler: &Block, + update_handler: &Block, ); fn nw_path_monitor_set_queue(monitor: nw_path_monitor_t, queue: dispatch_queue_t); fn nw_path_monitor_start(monitor: nw_path_monitor_t); @@ -25,24 +33,54 @@ extern "C" { fn dispatch_get_global_queue(identifier: usize, flag: usize) -> dispatch_queue_t; } -#[cfg(test)] -mod test { - use super::list_interfaces; +pub(crate) struct WatchHandle { + path_monitor: nw_path_monitor_t, +} - #[test] - fn list() { - let ifaces = list_interfaces().unwrap(); - println!("{:?}", ifaces); +impl Drop for WatchHandle { + fn drop(&mut self) { + unsafe { nw_path_monitor_cancel(self.path_monitor); } } } -pub(crate) struct WatchHandle; +struct CallbackState { + prev_list: List, + callback: Box, +} -pub(crate) fn watch_interfaces( +pub(crate) fn watch_interfaces( callback: F, ) -> Result { - // stop current worker thread - // post this into a thread that will use it - drop(callback); - Ok(WatchHandle) + let state = CallbackState { + prev_list: List::default(), + callback: Box::new(callback), + }; + // Blocks are Fn, not FnMut + let state = Mutex::new(state); + let block = RcBlock::new(move |_: nw_path| { + let mut state = state.lock().unwrap(); + let Ok(new_list) = crate::list::list_interfaces() else { + return; + }; + if new_list == state.prev_list { + return; + } + let update = Update { + interfaces: new_list.0.clone(), + diff: new_list.diff_from(&state.prev_list), + }; + (state.callback)(update); + state.prev_list = new_list; + }); + let path_monitor: nw_path_monitor_t; + unsafe { + let queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0); + path_monitor = nw_path_monitor_create(); + nw_path_monitor_set_update_handler(path_monitor, &block); + nw_path_monitor_set_queue(path_monitor, queue); + nw_path_monitor_start(path_monitor); + } + Ok(WatchHandle { + path_monitor, + }) }