1#![allow(
6 non_snake_case,
7 non_upper_case_globals,
8 clippy::unreadable_literal,
9 clippy::declare_interior_mutable_const
10)]
11
12use std::ffi::c_void;
13use std::io;
14use std::marker::{PhantomData, PhantomPinned};
15use std::os::raw::c_uint;
16use std::path::Path;
17use std::time::Duration;
18
19use core_foundation::array::{CFArray, CFArrayRef};
20use core_foundation::base::{
21 kCFAllocatorDefault, Boolean, CFAllocatorCopyDescriptionCallBack, CFAllocatorRef,
22 CFAllocatorReleaseCallBack, CFAllocatorRetainCallBack, CFIndex, TCFType,
23};
24use core_foundation::date::CFTimeInterval;
25use core_foundation::runloop::{CFRunLoop, CFRunLoopIsWaiting, CFRunLoopMode, CFRunLoopRef};
26use core_foundation::string::{CFString, CFStringRef};
27use core_foundation::url::{kCFURLPOSIXPathStyle, CFURL};
28use once_cell::unsync::Lazy;
29
30fn str_path_to_cfstring_ref(source: &Path) -> io::Result<CFString> {
31 CFURL::from_path(source, source.is_dir())
32 .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))
33 .map(|path| path.absolute().get_file_system_path(kCFURLPOSIXPathStyle))
34}
35
36pub(crate) trait CFRunLoopExt {
37 fn is_waiting(&self) -> bool;
38}
39
40impl CFRunLoopExt for CFRunLoop {
41 fn is_waiting(&self) -> bool {
42 unsafe { CFRunLoopIsWaiting(self.as_concrete_TypeRef()) != 0 }
43 }
44}
45
46#[doc(hidden)]
47#[repr(C)]
48pub struct __FSEventStream {
49 _data: [u8; 0],
50 _marker: PhantomData<(*mut u8, PhantomPinned)>,
51}
52
53pub type SysFSEventStreamRef = *mut __FSEventStream;
54
55pub struct SysFSEventStream(SysFSEventStreamRef);
59
60unsafe impl Send for SysFSEventStream {}
64
65pub type FSEventStreamCallback = extern "C" fn(
66 SysFSEventStreamRef, *mut c_void, usize, *mut c_void, *const FSEventStreamEventFlags, *const FSEventStreamEventId, );
73
74pub type FSEventStreamEventId = u64;
75
76pub type FSEventStreamCreateFlags = c_uint;
77
78pub type FSEventStreamEventFlags = c_uint;
79
80pub const kFSEventStreamEventIdSinceNow: FSEventStreamEventId = 0xFFFFFFFFFFFFFFFF;
81
82pub const kFSEventStreamCreateFlagNone: FSEventStreamCreateFlags = 0x00000000;
83pub const kFSEventStreamCreateFlagUseCFTypes: FSEventStreamCreateFlags = 0x00000001;
84pub const kFSEventStreamCreateFlagNoDefer: FSEventStreamCreateFlags = 0x00000002;
85pub const kFSEventStreamCreateFlagWatchRoot: FSEventStreamCreateFlags = 0x00000004;
86pub const kFSEventStreamCreateFlagIgnoreSelf: FSEventStreamCreateFlags = 0x00000008;
87pub const kFSEventStreamCreateFlagFileEvents: FSEventStreamCreateFlags = 0x00000010;
88pub const kFSEventStreamCreateFlagMarkSelf: FSEventStreamCreateFlags = 0x00000020;
89pub const kFSEventStreamCreateFlagUseExtendedData: FSEventStreamCreateFlags = 0x00000040;
90
91pub const kFSEventStreamEventFlagNone: FSEventStreamEventFlags = 0x00000000;
92pub const kFSEventStreamEventFlagMustScanSubDirs: FSEventStreamEventFlags = 0x00000001;
93pub const kFSEventStreamEventFlagUserDropped: FSEventStreamEventFlags = 0x00000002;
94pub const kFSEventStreamEventFlagKernelDropped: FSEventStreamEventFlags = 0x00000004;
95pub const kFSEventStreamEventFlagEventIdsWrapped: FSEventStreamEventFlags = 0x00000008;
96pub const kFSEventStreamEventFlagHistoryDone: FSEventStreamEventFlags = 0x00000010;
97pub const kFSEventStreamEventFlagRootChanged: FSEventStreamEventFlags = 0x00000020;
98pub const kFSEventStreamEventFlagMount: FSEventStreamEventFlags = 0x00000040;
99pub const kFSEventStreamEventFlagUnmount: FSEventStreamEventFlags = 0x00000080;
100pub const kFSEventStreamEventFlagItemCreated: FSEventStreamEventFlags = 0x00000100;
101pub const kFSEventStreamEventFlagItemRemoved: FSEventStreamEventFlags = 0x00000200;
102pub const kFSEventStreamEventFlagItemInodeMetaMod: FSEventStreamEventFlags = 0x00000400;
103pub const kFSEventStreamEventFlagItemRenamed: FSEventStreamEventFlags = 0x00000800;
104pub const kFSEventStreamEventFlagItemModified: FSEventStreamEventFlags = 0x00001000;
105pub const kFSEventStreamEventFlagItemFinderInfoMod: FSEventStreamEventFlags = 0x00002000;
106pub const kFSEventStreamEventFlagItemChangeOwner: FSEventStreamEventFlags = 0x00004000;
107pub const kFSEventStreamEventFlagItemXattrMod: FSEventStreamEventFlags = 0x00008000;
108pub const kFSEventStreamEventFlagItemIsFile: FSEventStreamEventFlags = 0x00010000;
109pub const kFSEventStreamEventFlagItemIsDir: FSEventStreamEventFlags = 0x00020000;
110pub const kFSEventStreamEventFlagItemIsSymlink: FSEventStreamEventFlags = 0x00040000;
111pub const kFSEventStreamEventFlagOwnEvent: FSEventStreamEventFlags = 0x00080000;
112pub const kFSEventStreamEventFlagItemIsHardlink: FSEventStreamEventFlags = 0x00100000;
113pub const kFSEventStreamEventFlagItemIsLastHardlink: FSEventStreamEventFlags = 0x00200000;
114pub const kFSEventStreamEventFlagItemCloned: FSEventStreamEventFlags = 0x00400000;
115
116pub const kFSEventStreamEventExtendedDataPathKey: Lazy<CFString> =
117 Lazy::new(|| CFString::new("path"));
118pub const kFSEventStreamEventExtendedFileIDKey: Lazy<CFString> =
119 Lazy::new(|| CFString::new("fileID"));
120
121#[repr(C)]
122pub struct SysFSEventStreamContext {
123 pub version: CFIndex,
124 pub info: *mut c_void,
125 pub retain: Option<CFAllocatorRetainCallBack>,
126 pub release: Option<CFAllocatorReleaseCallBack>,
127 pub copy_description: Option<CFAllocatorCopyDescriptionCallBack>,
128}
129
130#[macro_export]
140macro_rules! impl_release_callback {
141 ($name: ident, $ctx_ty: ty) => {
142 extern "C" fn $name(ctx: *mut std::ffi::c_void) {
143 unsafe {
144 drop(Box::from_raw(ctx as *mut $ctx_ty));
145 }
146 }
147 };
148 ($name: ident, const $ctx_ty: ty) => {
149 extern "C" fn $name(ctx: *const std::ffi::c_void) {
150 unsafe {
151 drop(Box::from_raw(ctx as *mut $ctx_ty));
152 }
153 }
154 };
155}
156
157impl SysFSEventStreamContext {
158 pub fn new<T>(ctx: T, release_callback: CFAllocatorReleaseCallBack) -> Self {
162 let ctx = Box::into_raw(Box::new(ctx));
163 Self {
164 version: 0,
165 info: ctx.cast(),
166 retain: None,
167 release: Some(release_callback),
168 copy_description: None,
169 }
170 }
171}
172
173impl SysFSEventStream {
174 pub fn new<P: AsRef<Path>>(
179 callback: FSEventStreamCallback,
180 context: &SysFSEventStreamContext,
181 paths_to_watch: impl IntoIterator<Item = P>,
182 since_when: FSEventStreamEventId,
183 latency: Duration,
184 flags: FSEventStreamCreateFlags,
185 ) -> io::Result<Self> {
186 let cf_paths: Vec<_> = paths_to_watch
187 .into_iter()
188 .map(|item| str_path_to_cfstring_ref(item.as_ref()))
189 .collect::<Result<_, _>>()?;
190 let cf_path_array = CFArray::from_CFTypes(&*cf_paths);
191 Ok(Self(unsafe {
192 FSEventStreamCreate(
193 kCFAllocatorDefault,
194 callback,
195 context,
196 cf_path_array.as_concrete_TypeRef(),
197 since_when,
198 latency.as_secs_f64() as CFTimeInterval,
199 flags,
200 )
201 }))
202 }
203 pub fn show(&mut self) {
204 unsafe { FSEventStreamShow(self.0) }
205 }
206 pub fn schedule(&mut self, run_loop: &CFRunLoop, run_loop_mode: CFStringRef) {
207 unsafe {
208 FSEventStreamScheduleWithRunLoop(self.0, run_loop.as_concrete_TypeRef(), run_loop_mode);
209 }
210 }
211 pub fn unschedule(&mut self, run_loop: &CFRunLoop, run_loop_mode: CFStringRef) {
212 unsafe {
213 FSEventStreamUnscheduleFromRunLoop(
214 self.0,
215 run_loop.as_concrete_TypeRef(),
216 run_loop_mode,
217 );
218 }
219 }
220 pub fn start(&mut self) -> bool {
221 unsafe { FSEventStreamStart(self.0) != 0 }
222 }
223 pub fn flush_sync(&mut self) {
224 unsafe { FSEventStreamFlushSync(self.0) };
225 }
226 pub fn stop(&mut self) {
227 unsafe { FSEventStreamStop(self.0) };
228 }
229 pub fn invalidate(&mut self) {
230 unsafe { FSEventStreamInvalidate(self.0) };
231 }
232}
233
234impl Drop for SysFSEventStream {
235 fn drop(&mut self) {
236 unsafe { FSEventStreamRelease(self.0) };
237 }
238}
239
240#[link(name = "CoreServices", kind = "framework")]
241extern "C" {
242 fn FSEventStreamCreate(
243 allocator: CFAllocatorRef,
244 callback: FSEventStreamCallback,
245 context: *const SysFSEventStreamContext,
246 pathsToWatch: CFArrayRef,
247 sinceWhen: FSEventStreamEventId,
248 latency: CFTimeInterval,
249 flags: FSEventStreamCreateFlags,
250 ) -> SysFSEventStreamRef;
251
252 fn FSEventStreamShow(stream_ref: SysFSEventStreamRef);
253 fn FSEventStreamScheduleWithRunLoop(
254 stream_ref: SysFSEventStreamRef,
255 run_loop: CFRunLoopRef,
256 run_loop_mode: CFRunLoopMode,
257 );
258
259 fn FSEventStreamUnscheduleFromRunLoop(
260 stream_ref: SysFSEventStreamRef,
261 run_loop: CFRunLoopRef,
262 run_loop_mode: CFRunLoopMode,
263 );
264
265 fn FSEventStreamStart(stream_ref: SysFSEventStreamRef) -> Boolean;
266 fn FSEventStreamFlushSync(stream_ref: SysFSEventStreamRef);
267 fn FSEventStreamStop(stream_ref: SysFSEventStreamRef);
268 fn FSEventStreamInvalidate(stream_ref: SysFSEventStreamRef);
269 fn FSEventStreamRelease(stream_ref: SysFSEventStreamRef);
270
271 pub fn FSEventsGetCurrentEventId() -> FSEventStreamEventId;
272}