import Foundation /// The contract every phbar event recognizer conforms to. /// /// phbar ships a set of built-in recognizers (volume, network, appearance, /// power, mpd) compiled into the host. External recognizers are loaded at /// runtime as shared libraries from the events directory /// (`/events//`) and conform to this same protocol, so the host /// drives built-in and external recognizers through one shape. /// /// Conform an `NSObject` subclass and export a zero-argument factory via /// `@_cdecl(PHEventCreateSymbol)`: /// /// ```swift /// import phbarEvents /// /// @objc(MyVolumeRecognizer) /// final class MyVolumeRecognizer: NSObject, PHEventRecognizer { /// func start(notify: @escaping () -> Void) { /* observe, call notify() */ } /// func stop() { /* tear down */ } /// } /// /// @_cdecl("phbar_event_create") /// public func phbar_event_create() -> AnyObject { MyVolumeRecognizer() } /// ``` /// /// `notify` may be invoked from any thread; the host marshals each firing onto /// the main actor, so a recognizer that receives callbacks on a background queue /// (CoreAudio, Network, IOKit) may call it directly without hopping. @objc public protocol PHEventRecognizer: AnyObject { /// Begin observing. `notify` must be retained for as long as the recognizer /// is started and is invoked whenever the observed state changes. /// Idempotent: calling `start` while already started is a no-op. func start(notify: @escaping () -> Void) /// Stop observing and release system resources. Idempotent. func stop() } /// The C entry point every external event library must export. /// /// Returns a retained instance of an `NSObject` subclass conforming to /// `PHEventRecognizer`. Authors declare it with `@_cdecl(PHEventCreateSymbol)`. public typealias PHEventCreate = @convention(c) () -> AnyObject /// The `dlsym` symbol the host looks up in an external event library when the /// manifest omits one (`"phbar_event_create"`). public let PHEventCreateSymbol = "phbar_event_create" /// Bumped whenever `PHEventRecognizer` changes in a source-incompatible way. /// External libraries declare the version they target in their manifest /// (`apiVersion`); the host refuses to load a mismatched library. Omitting the /// field skips the check (treat the library as unversioned). public let PHEventAPIVersion = 1