libpulse-binding-2.28.1/.cargo_vcs_info.json0000644000000001530000000000100143540ustar { "git": { "sha1": "83adaf1280fe7ac5fdff6cd14f80bf749bddd29c" }, "path_in_vcs": "pulse-binding" }libpulse-binding-2.28.1/CHANGELOG.md000064400000000000000000001120731046102023000147620ustar 00000000000000# 2.28.1 (July 18th, 2023) * Fixed cross-crate version requirement. # 2.28.0 (July 18th, 2023) * Fixed 'double-reference clone' warnings. * Fixed docs.rs build failure. * Dropped PA v4 support. # 2.27.1 (January 9th, 2023) * Fixed broken build status badge in readme. # 2.27.0 (January 9th, 2023) * Bumped Rust edition to 2021. * Bumped MSRV from 1.46 to 1.56. * Added `rust-version` (MSRV) attribute to `cargo.toml` file. * Tweaked some doc-tests to work with rust v1.63+ (at the expense of not working with older versions). This relates to float-point calculation changes in `Duration::try_from_secs_f32/64` made in rust stdlib version 1.60 and further tweaked in 1.63. See [this issue][issue48]. * Updated required dependencies: - `libpulse-sys` from 1.19 to 1.20. # 2.26.0 (January 13th, 2022) * Changed the type of the `timestamp` member of `def::TimingInfo` from `Timeval` to `UnixTs` since it is actually a system time not a generic duration. * Time: Added missing `#[repr(transparent)]` marker to `MicroSeconds`. * Time: Made use of `#[track_caller]` such that panics from overflows and such in math operation implementations will point to the caller as the source of the problem. # 2.25.0 (August 29th, 2021) * Made the attributes of `MainloopInner` private. Methods `new()` and `get_api_ptr()` were added to support this, while `get_ptr()` was marked `unsafe`. * Converted `channelmap::Position::to_mask()` to `const fn`. * Bumped MSRV from 1.41 to 1.46 to better reflect practical requirement since bitflags v1.3 now requires 1.46 and I expect it likely that most projects will promptly be upgrading it. * Minor tweaks not worth documenting. * Fixed wrong version for previous changelog entry. # 2.24.0 (July 28th, 2021) * Added PA v15 support (API additions): - Added `Introspector::send_message_to_object()`. - Added `proplist::properties::{CONTEXT_FORCE_DISABLE_SHM, BLUETOOTH_CODEC}`. * Updated required dependencies: - `libpulse-sys` from 1.18 to 1.19. # 2.23.1 (April 9th, 2021) * Minor documentation tweaks. # 2.23.0 (January 30th, 2021) * Implemented `std::error::Error` for `error::{Code, PAErr}`. * Implemented `Debug` for `ListResult`. * Now using intra-doc-linking introduced in Rust 1.48. The MSRV remains unchanged at 1.41; this is too great a simplification to maintaining documentation to not implement immediately; the only negative is that links will be broken in locally generated documentation when built with an older Rust compiler than version 1.48. * Minor documentation fixes. * Re-implemented list-callback handling (internal change only). * Updated required dependencies: - `libpulse-sys` from 1.17 to 1.18. # 2.22.0 (December 22nd, 2020) * The `Context` creation functions `new()` and `new_with_proplist()` will now fail if the version of the PulseAudio client system library is older then the minimum compatibility level set via use of the version feature flags. This is done to block use of the library in such a circumstance as a means of helping to prevent the undefined behaviour that could result from the possible “forward” compatibility problems discussed in the project `COMPATIBILITY.md` documentation. * Added the following functions to help check the version of the PA system library at runtime: `version::get_library_version_numbers()`, `version::compare_with_library_version()` and `version::library_version_is_too_old()`. * Tweaked version constants. * Deprecated `version::check_version()`. * Updated required dependencies: - `libpulse-sys` from 1.16 to 1.17. # 2.21.0 (December 15th, 2020) * Converted `context::FlagSet` to a [`bitflags`] type, moving the `context::flags::*` constants to associated constants of it, deprecating the old constant set. This was overlooked in 2.20. # 2.20.1 (December 15th, 2020) * Fixed mistake made trying to conditionally enable `#[cfg(doc)]` for docs.rs. # 2.20.0 (December 14th, 2020) * Made some changes to cargo features: - Removed the now obsolete `dox` cargo feature. - Removed the `pa_latest` and `pa_latest_common` cargo features. - Changed the default version feature level to `pa_v8`. * MSRV bumped from 1.40 to 1.41. * Re-wrote the `COMPATIBILITY.md` guide (available in the code repo), including adding important discussion of the hazard of “forward compatibility” concerns. * Improved the basic implementations of integer operations `Add`, `Sub`, `Mul`, `Div` and `Rem` for time types, such that those that were not previously making use of “checked” calculations now do so, and thus overflow causes a panic rather than being wrapped. If panicing is not wanted, the “checked” methods should be used directly which return a `None` on failure. * Added an `Option` wrapper to the `volume` param of `Context::play_sample()` and `Context::play_sample_with_proplist()` “scache” methods to simplify usage. Supplying `None` is a nicer alternative to passing `Volume::INVALID`. * Moved the following functions from the `sample` mod to methods of `sample::Spec`: `format_is_valid()`, `rate_is_valid()` and `channels_are_valid()`, having noticed that they clearly relate to the attributes of that type and so should have been there all along. * Shrunk the type of the `channels` param of `channelmap::Map` methods `init_auto()` and `init_extend()` from `u32` to `u8`. * Shrunk the `sample::CHANNELS_MAX` type from `usize` to `u8`. * Moved many existing constants to be associated constants (long overdue): - Deprecated `sample::{CHANNELS_MAX, RATE_MAX}`. - Added `sample::Spec::{CHANNELS_MAX, RATE_MAX}` replacements. - Added `volume::ChannelVolumes::CHANNELS_MAX` replacement. - Deprecated `volume::{VOLUME_NORM, VOLUME_MUTED, VOLUME_MAX, VOLUME_INVALID}`. - Added `volume::Volume::{NORMAL, MUTED, MAX, INVALID}` replacements. - Deprecated `volume::DECIBEL_MINUS_INFINITY`. - Added `volume::VolumeDB::MINUS_INFINITY` replacement. - Deprecated `time::{USEC_INVALID, USEC_MAX}`. - Added `time::MicroSeconds::{INVALID, MAX}` replacements. - Deprecated `context::subscribe::{FACILITY_MASK, OPERATION_MASK}`. - Added `context::subscribe::{Facility::MASK, Operation::MASK}` replacements. * Made some improvements to `MicroSeconds`: - Added `MicroSeconds::{MIN, ZERO, SECOND, MILLISECOND}` associated constants. - Added `MicroSeconds::is_zero()`, `MicroSeconds::from_secs()`, `MicroSeconds::from_millis()`, `MicroSeconds::inner()`, `MicroSeconds::as_secs()`, `MicroSeconds::as_millis()`, and `MicroSeconds::diff()`. - Additionally added `MicroSeconds::from_secs_f64()`, `MicroSeconds::from_secs_f32()`, `MicroSeconds::as_secs_f64()` and `MicroSeconds::as_secs_f32()` for converting to/from floating point form, and `MicroSeconds::mul_f32()`, `MicroSeconds::mul_f64()`, `MicroSeconds::div_f32()` and `MicroSeconds::div_f64()` for multiplication and division by floats. - Removed the restriction using `Mul` operations between an integer primitive and a `MicroSeconds` that required the Rhs to be the integer, by extending the implementation. Thus you can now do `2 * MicroSeconds(20)` not just `MicroSeconds(20) * 2`. - Enabled `MicroSeconds` to be added or subtracted from a `Duration`, not just the other way around. * Improved handling of flag sets through making use of [`bitflags`] (long overdue). The following types which have been simple integer primitive type aliases have now been changed to [`bitflags`] wrappers. The set of flags associated with them have been thus assigned as associated constants to them, and the separate set marked as deprecated. - `stream::FlagSet`, with its `stream::flags::*` flags. - `def::{SinkFlagSet, SourceFlagSet}` and their `def::sink_flags::*` and `def::source_flags::*` flag sets. - `direction::FlagSet` and its `direction::flags::*` flags. - `mainloop::events::io::IoEventFlagSet` and its `mainloop::events::io::flags::*` flags. - `context::subscribe::InterestMaskSet` and its `context::subscribe::subscription_masks` flags. * Renamed the `mainloop::events::io::IoEventFlagSet` type to simply `FlagSet`. * Renamed the `MASK_CARD` constant from `context::subscribe` to simply `CARD`, since the inclusion of `MASK_` in the name was a mistake (part of a prefix removed from others but mistakenly left in place for this one). * Moved `direction::is_valid()` and `direction::to_string()` to methods of `direction::FlagSet`. * Added `MonotonicTs::checked_add_duration()` and `MonotonicTs::checked_sub_duration()` along with corresponding `Add[Assign]` and `Sub[Assign]` impls for adding a `Duration` to `MonotonicTs`. This complements the existing functionality for adding a `MicroSeconds`, without requiring conversion of a `Duration` to a `MicroSeconds`. * Made use of `#[cfg(doc)]` to always include stuff behind PA version feature guards in generated documentation. (Required bump of minimum supported Rust version from 1.40 to 1.41). * Added support for feature tagging in documentation (requires nightly Rust version, so only enabled if a certain config flag is used, as for the docs.rs copy). * Marked `version::Compatibility` as `#[non_exhaustive]`. * Updated required dependencies: - `libpulse-sys` from 1.15 to 1.16. - `bitflags` 1.2 now required. # 2.19.0 (December 8th, 2020) * Fixed broken conversions between `Duration` and other time duration types (`MicroSeconds` and `Timeval`), which were mistakenly converting based upon milliseconds rather than microseconds and thus were off by a scale of 1000. Lack of tests failed to catch this, which have now been added. * Added missing `Default` implementation for `channelmap::MapDef`. * Added documentation for `version::check_version()`. If you use it, please double check that it actually does what you think it does. It does **not** involve talking to the client library. # 2.18.1 (November 25th, 2020) * Fixed deprecated license attribute syntax. # 2.18.0 (November 25th, 2020) * Marked `format::Encoding` as `#[non_exhaustive]`. * Replaced the `From for Code` impl with `TryFrom for Code`, since the conversion is fallible and now that `TryFrom` has been stable for a while (since 1.40). Unfortunately it was not possible to leave the `From` version in place with a deprecation notice due to conflicts that result. The solution was also re-implemented based upon `FromPrimitive`. * Implemented `std::fmt::Display` for `error::Code`. * Added derive of `FromPrimitive` and `ToPrimitive` from the `num-derive` crate on basic enums. * Fixed an extremely unlikely but possible source of undefined behaviour in introspection. This relates to translation of a few integers into enum variants. Previously values that did not match an enum variant would be transmuted to the enum type anyway, resulting in undefined behaviour. Now a panic will occur instead. Pragmatically you should never encounter this, so no advisory is being issued - it would only occur via a bug in PulseAudio, the mapping, or a mismatch in version compatibility with the running PulseAudio client library, with this latter issue being something dangerous that users must avoid anyway. * Some test additions. * Updated required dependencies: - `libpulse-sys` from 1.14 to 1.15. - `num-traits` 0.2 now required. - `num-derive` 0.3 now required. # 2.17.0 (November 24th, 2020) * Added PA v14 support (API additions). * Mainloop (standard): Changed the `prepare()` method to take an `Option` instead of `Option`. * Renamed the error `Code::Io` enum variant to `Code::IO`. * Updated required dependencies: - `libpulse-sys` from 1.13 to 1.14. # 2.16.3 (November 21st, 2020) * Trivial documentation fixes. * Trivial internal tidy-up in volume module. # 2.16.2 (September 9th, 2020) * Bumped `pa_latest_common` feature to target PA v13. # 2.16.1 (July 10th, 2020) * Fixed some dangling pointer issues with handling of string arrays, thanks to @yatinmaan on github. # 2.16.0 (April 18th, 2020) * Made the attributes of `channelmap::{ChannelVolumes, Map}` types private. (Direct access had been deprecated previously). * Removed deprecated comparison methods. * Removed deprecated proplist methods. * Removed deprecated Cargo features. * Updated required dependencies: - `libpulse-sys` from 1.12 to 1.13. # 2.15.1 (April 18th, 2020) * Fixed issues with a threaded mainloop example in the documentation. * Fixed miscellaneous other documentation mistakes. # 2.15.0 (December 29th, 2019) * Updated required dependencies: - `libpulse-sys` from 1.11 to 1.12. # 2.14.1 (December 29th, 2019) * Fixed issues compiling on Windows: - Needed to reference a different `pollfd` definition - Needed to reference different inner `timeval` attribute type definitions Thanks to @allquixotic on github for reporting. # 2.14.0 (October 28th, 2019) * Reverted "Changed the `mainloop` param of `Context::rttime_new()` from trait object to generic (`dyn` to`impl`)" from v2.7. Failed to test sufficiently. This introduces an E0632 error in a test app and I was unsuccessful in finding a compilable workaround. # 2.13.0 (September 17th, 2019) * Changed the license model from LGPL to dual MIT and Apache-2.0. See [here][issue26] for details. * Updated required dependencies: - `libpulse-sys` from 1.10 to 1.11. # 2.12.0 (September 15th, 2019) * Implemented PA v13 enhancements, including: - Added `get_sample_format()`, `get_rate()`, `get_channel_count()` and `get_channel_map()` methods to `format::Info`. - Added `util::make_thread_realtime()`. - Added `Encoding::TRUEHD_IEC61937` and `Encoding::DTSHD_IEC61937`. - A wrapper for `pa_threaded_mainloop_once_unlocked` has not been added at this time. * Changed the `ss` param of `Context::get_tile_size()` to add an `Option` wrapper. The C API function allows a null pointer to be used for this param, which was not a use case allowed in the binding, for no particular reason. This enables it. * Added PA v13 compatibility control feature. * Updated required dependencies: - `libpulse-sys` from 1.9 to 1.10. # 2.11.1 (August 19th, 2019) * Fixed broken doc.rs documentation generation. # 2.11.0 (August 19th, 2019) * Extended support to even older versions of PA, specifically up to and including v4. * Revised `version::Compatibility` variants to make more sense. * Simplified feature flags, old ones left as temporary aliases, to be removed later. * Added a `dox` feature flag, for use with `cargo doc`. It enables the very latest PA version compatibility, while bypassing the pkg-config check, thus is useful for generating documentation that includes information on features from versions of PA that may be newer than the version you have installed. * Improved version documentation further. * Updated required dependencies: - `libpulse-sys` from 1.8 to 1.9. # 2.10.1 (August 17th, 2019) * Version: Improved mod documentation. * Added missing version info to deprecation notices. # 2.10.0 (August 15th, 2019) * Modified `channelmap::{ChannelVolumes, Map}` types to be more Rust-like. Both of these types hold an array of size `sample::CHANNELS_MAX`, along with a `channels` attribute which controls how much of the initial portion of that array is “active” (equivalent to the `len()` of a `Vec`). Previously the array and len were simply public attributes, with changes to be applied directly. While for the time being they remain publicly accessible for backwards compatibility, this will become private in a future release. These types should now be used in a way more similar to a `Vec`: - New methods `len()` and `set_len()` get read/write access to the `channels` attribute that records how much of the array is considered “active”. - Traits `Borrow<[_]>` and `BorrowMut<[_]>` have been implemented, along with the addition of new methods `get()` and `get_mut()` (for convenience - you can avoid type ambiguity), for accessing the array as a slice (of just the “active” portion). * Updated `use` conventions to that of Rust 1.30/1.31. * Specified edition in toml file. * Made the following functions `const`: - `version::get_compatibility()`. - `Volume::is_valid()`. - `Timeval::new()` and `Timeval::new_zero()`. - `subscribe::Facility::to_interest_mask()`. * Version: purged deprecated items. * Updated required dependencies: - `libpulse-sys` from 1.6 to 1.8. Note: versions 2.8 and 2.9 skipped, used only for `libpulse-glib-binding` crate changes. # 2.7.1 (August 13th, 2019) * Fixed overlooked use of new method names in docs. # 2.7.0 (August 12th, 2019) * Renamed a few methods of `Proplist` for clarity: - `Proplist::sets()` to `Proplist::set_str()`. - `Proplist::gets()` to `Proplist::get_str()`. - `Proplist::setp()` to `Proplist::set_pl()`. * Added `Stream::write_copy()` which is just a simplified interface for asking PA to make an internal copy of the to-be-written data (same as providing `None` in the `free_cb` param of `Stream::write()`. * Changed the `mainloop` param of `Context::rttime_new()` from trait object to generic (`dyn` to `impl`). * Made better use of `PartialEq` implementations: - Deprecated `ChannelMap::is_equal_to()`, `ChannelVolumes::equal_to()`, `ChannelVolumes::channels_equal_to()`, `Spec::equal_to()`, and `Proplist::equal_to()` in favour of `PartialEq` implementations. - Added implementation for `Proplist` and `PartialEq` impl for `ChannelVolumes`. * Changed `PartialEq` implementations for `channelmap::{Map, Spec, ChannelVolumes}` to delegate the logic to the C API. * Removed stray `repr(C)` attribute from `SinkPortInfo` introspection type. * Added a new `latest_pa_common_compatibility` feature flag, used by default now instead of `latest_pa_compatibility`. * Added tests to assert that size and alignment of certain structs and enums are identical to their `sys` crate counterparts. * Updated required dependencies: - `libpulse-sys` from 1.5 to 1.6. # 2.6.0 (March 10th, 2019) * Implemented use of `std::panic::catch_unwind()` in callbacks. # 2.5.0 (December 22nd, 2018) **Note: This includes a security fix!** * Fixed potential use-after-free with `proplist::Iterator` (not to be confused with the `std::iter::Iterator` trait). An instance of this object type is created from a `Proplist` object and holds a copy of the same raw pointer to the underlying C object; the `Proplist` object had sole responsibility for destroying it via its `Drop` implementation. There was no actual lifetime association however linking the lifetime of the `Iterator` object to the `Proplist` object, and thus it was possible for the `Proplist` object to be destroyed first, leaving the `Iterator` object working on a freed C object. This is unlikely to have been done in actual user code, but would have been trivial to achieve, including simply by using the `into_iter()` function. This affects versions all the way back to 1.0.5. * Enabled `Send`+`Sync` for various types. This was previously not done due to uncertainty as to whether or not it was safe to do so, but I have now reconsidered it and arrived at the conclusion that it should be okay: With the threaded mainloop, a lock must be held when using objects; this is taken by the mainloop dispatcher when executing callbacks, and otherwise must be taken by the user before using objects within any thread. With that locking mechanism, it should be safe I presume for these objects to be marked Send+Sync. It is not ideal that the user can so easily just forget to grab the lock, as opposed to the Rust design of `Arc>` forcing unlocking to get at things, but it’s not certain that we can easily really do anything to address this. So long as users stick to the principle of grabbing the mainloop lock though, they should be fine. * Simplified converting `Duration` to `MicroSeconds` or `Timeval` using `Duration::subsec_millis()`. * Made `proplist::Iterator::new()` private, since it’s very unlikely anyone needs it. * Added the new `latest_pa_compatibility` and `pa_v12_compatibility` feature flags, and deprecated `pa_encoding_from_string` in favour of `pa_v12_compatibility`. * Removed unnecessary `From` conversion implementation to/from C type for `format::Info`. * Updated required dependencies: - `libpulse-sys` from 1.4 to 1.5. # 2.4.0 (November 28th, 2018) * Changed the `channelmap::Map::new_from_string()` method return value to use a `Result` wrapper. Previously failure was just ignored, expecting strings provided to always be valid, as obtained from the `Map::print()` and `Map::to_name()` methods, but let's be more cautious. * Updated out-of-date documentation of return types for a large number of functions, particularly introspection ones, which had not been updated following the removal of the `Option` wrapper back in v2.0. Thanks to @0xpr03 on github for noticing a discrepancy. * Restored the `Option` wrapper to the `Context::drain()` return value. It was incorrectly removed from this function at the same time as legitimately being removed from many others. * Changed the `Drop` implementation on `Stream` to no longer unwrap the `Result` returned by the `disconnect()` attempt it makes. This should fix [this problem][issue11] encountered by @futpib on github. # 2.3.0 (November 4th, 2018) * Improved the `version` mod: - Constants now vary depending upon backwards compatibility flags, correctly indicating the newest supported PA version. - Added the `Compatibility` enum and `get_compatibility()` function. - Renamed `BINDING_TARGET_VERSION` to `TARGET_VERSION_STRING`. - Introduced `TARGET_VERSION` and deprecated `MAJOR`, `MINOR` and `MICRO`. - Deprecated `get_headers_version()`. * Clarified PA version compatibility in `version` mod. * Clarified `pa_encoding_from_string` feature purpose. * Updated required dependencies: - `libpulse-sys` from 1.3 to 1.4. # 2.2.6 (October 28th, 2018) * Minor tweaks, improving code clarity and such. # 2.2.5 (October 8th, 2018) * Fixed broken attempt to include license file in bundled package. # 2.2.4 (October 8th, 2018) * Added dedicated changelog, split off from the old single project overview one. * Included copy of license file in bundled package and excluded the `.gitignore` and `README.md` files. # 2.2.3 (September 20th, 2018) * Fixed feature control. # 2.2.2 (September 3rd, 2018) * Added homepage and repo links, thanks to @berkus on github for the patch. # 2.2.1 (August 29th, 2018) * Fixed reported compilation errors in the `time` mode on macOS and raspberry pi platforms. Thanks to @noahbkim on github for reporting and @ssendev for the suggested solution. # 2.2 (August 21st, 2018) * Renamed `mainloop::standard::InterateResult` to `IterateResult`, fixing an unintentional typo. Thanks to @HyperHamster on github for reporting. * Fixed a couple of compile time warnings in the `time` mod. # 2.1 (July 17th, 2018) * Renamed the `timeval` mod to `time`. * Added `UnixTs` and `MonotonicTs` timestamp types, and put them to use with functions handling time events. * Replaced `rtclock::now()` with `MonotonicTs::now()`. * Mainloop events now take closures for callbacks, like the rest of the API. * `Operation`: Fixed possible memory leak with cancellation. * `Context`: Now takes a ref to the mainloop instead of the mainloop’s API in creation methods. * Mainloop/api: Moved the `mainloop_api_once()` method to the `Mainloop` trait from the mainloop API structure, and renamed it to `once_event()`. * Mainloop/signals: Converted to a trait and implemented on mainloops, rather than being implemented as methods implemented on the mainloop API structure. * Mainloop API objects now correctly treated as immutable. The PA devs have informed me that these structures are not intended to be mutated. Patches have been sent to them to correct it in the PA C API itself. No point in waiting for those to be accepted. The get/set userdata methods have been removed. * Mainloop/api: Added method for creating time events using monotonic time values, as an alternate to the similar method available on the `Context` object. * Added `TimeEvent::restart_rt()` method, taking monotonic time. * Removed `Context::rttime_restart()` method, made obsolete by the new `TimeEvent::restart_rt()` method on the event itself. * Removed the `Stream::get_context()` method. This method returned a ‘weak’ wrapper object, where ‘weak’ means that it deliberately will not decrement the ref count of the underlying C object on drop. This was exactly what was wanted back in v1.x, however in v2.0 we introduced closure based callbacks, and the `Context` object (wrapper) was extended to hold saved multi-use callbacks. This causes a problem. If you use this `get_context()` method to get a weak ref, then use it to change a multi-use callback, the new callback gets saved into the ‘weak’ object, and then you need both that and the original context object wrapper to both exist for the lifetime that you want the new callback to remain in use. Not ideal, and not obvious. To fix it would require that `Stream` creation methods take the `Context` with an `Rc` wrapper so it can hold onto a cloned `Rc` ref, instead of taking a reference, and then returning a cloned `Rc` from `get_context()`. The more simple option was to just remove this method, as I have done. * Removed the `set_destroy_cb()` event methods, which became obsolete with the switch to closure based callbacks. * Split the `DeferEvent::enable()` method into separate `DeferEvent::enable()` and `DeferEvent::disable()` methods. * Events/timer: Fixed api pointer type in callback types. Was using C API (sys) type instead of binding type, unlike other event callbacks. * Added convenience `Timeval::new()` and `Timeval::new_zero()` methods. * `Timeval`: Added math operation implementations, and removed the `Timeval::add()` and `Timeval::sub()` methods in favour of them. Also removed the `Timeval::set()` method since you can convert and assign from `MicroSeconds` simply with `From` and `into()`. * Enabled conversions between `Duration` and `MicroSeconds`, and between `Duration` and `Timeval`. * Added math operation implementations to `MicroSeconds` and `Timeval` for adding/subtracting `Duration`. * Removed the `Timeval::get_time_of_day()` method, preferring use of the new timestamp types. * Changed the `Stream::get_device_name()` method to return a `Cow` instead of a `CStr`. * Avoided creating owned copies of static strings in a few places, returning `Cow` instead. This includes `channelmap::Position::to_string()`, `channelmap::Map::to_name()`, `format::Encoding::to_string()` and `sample::Format::to_string()`. * Default-enabled the feature enabling the `format::Encoding::from_string()` method, for which the underlying function symbol was missing from PA's symbol file and thus not available in the client library before v12. * Fixed `format::Encoding::from_string()`, which would not compile due to a return type mismatch. (As mentioned above, the underlying symbol was missing before PA v12, leaving this previously untested, and it unfortunately turned out to have been broken). * Simplified callback proxy / setup code (internal change only). * Purged context documentation discussing ref counting. * Moved documentation discussing mainloop abstraction from the `context` mod to the `mainloop` mod. * Added mainloop documentation discussing callback execution. * Updated required dependencies: - `libpulse-sys` from 1.2 to 1.3. # 2.0.1 (June 26th, 2018) * Updated declared PA version compatibility (11.0 → 12.0). * `Operation`: Documented possible memory leak. * `Proplist`: Fixed debug output to use comma separator rather than newline (for consistency in dumping introspection data), and output in list style instead of mixed struct/list style. * Documentation typo fixes. * Updated version in `README` usage example. # 2.0 (June 16th, 2018) * Changed handling of callbacks to support closures! Now you can simply supply a closure, instead of an `extern C` function and a raw `c_void` data pointer, in almost any place across the binding’s API that takes a callback. (Note, there are a few places that have not been changed: Tackling the mainloop API stuff has been postponed to look at later; same for the stream write method’s optional ‘free’ callback; the `SpawnApi` has no userdata arg and thus cannot be done, which is not a big deal; finally the standard mainloop’s function for specifying an alternate ‘poll’ function has been left, at least for now). * Introspection: Big clean up. Previously we simply transmuted from the raw C structs, which was very efficient, and some attributes (like enums) transmuted perfectly to binding counterparts thus were ‘clean’. A lot of attributes however were ugly, exposing raw pointers, particularly with strings and lists. Now instead a proper conversion is done. This takes more effort, but gives a much more pleasant to use interface. Note, the `Debug` trait has been implemented, thus combined with the new support of closures for callbacks, makes it very easy to dump a copy of introspection data. * Cleaned up `context::ext_*` in the same way as introspection. * Renamed `CVolume` to `ChannelVolumes`. * Removed `Option` wrapper around `Operation` objects, using assertion that the inner pointer is not null. * Context: Handful of methods changed to take immutable `self`, per respective change in version 1.2 of `libpulse-sys`. * Simplified `Format`: The inner pointer to a raw `InfoInternal` object (mistakenly hidden in v1.1, restored in v1.2.2) has been hidden, replaced with cleaner set/get methods. A method already existed for setting the encoding; one has now also been added for reading it, saving users from having to do an unsafe raw pointer dereference. Also, with respect to the property list, if the existing convenience methods are insufficient and direct access is needed, this is also now far cleaner; not only is the unsafe raw pointer dereference avoided, but now you are given access to a clean `Proplist` wrapper (see `Format::get_properties()` and `Format::get_properties_mut()`). * Assert added to block use of threaded mainloop `lock()` method from within the event loop thread (i.e. within most instances of callback execution). * Renamed `Stream::proplist_remove()` to `Stream::remove_proplist()`. * Common `Stream::set_event_callback()` callback event names moved to submodule. * Introspection: Removed unnecessary converters. The `From` trait was implemented for introspection objects in both directions between the binding and the sys instances. While this is necessary for the sys to binding direction, the other really wasn’t needed. * Purged subscribe autoload API (deprecated in PulseAudio since 2009). * Updated required dependencies: - `libpulse-sys` from 1.1 to 1.2. # 1.2.2 (June 16th, 2018) * Restored access to `format::Info`’s `ptr` attribute. # 1.2.1 (June 15th, 2018) **Note: This includes a security fix!** * Fixed use-after-frees with `Stream::get_format_info()` and `Stream::get_context()`. These should have used `Stream::from_raw_weak()` instead of `Stream::from_raw()` to avoid incorrectly freeing the underlying C object, leaving a dangling pointer. # 1.2 (June 1st, 2018) * Fixed lifetime issues with a handful of stream methods. * Fixed lifetime issues with `get_api()` mainloop method. * mainloop/standard: Fixed `run()` method’s return data. Incorrectly was returning function call result, while claiming in the documentation that this was the quit retval, which wasn’t returned at all. * Tidied up error code handling: - Added `PAErr` wrapper for the `i32` error type, for cleaner interfaces. - Moved the `strerror()` function to be a method of this new `PAErr` type (`PAErr::to_string()`). - Renamed `Code::strerror()` to `Code::to_string()`. - Converted the error `CStr` to `String` for users; no need to make users do it. - Implemented `From` for converting between `PAErr` and `Code` error types. * Simplified volume handling: - `Volume` and `VolumeDB` are now wrappers rather than type aliases. - Added the `VolumeLinear` wrapper. I had mistakenly taken floating point volumes to all be dB values, but there is actually a distinction between dB and ‘linear factor’, as per the C API conversion functions. This is now used in linear related conversions, which thus no longer incorrectly portray such values to be dB scale. - Renamed `DECIBEL_MININFTY` to `DECIBEL_MINUS_INFINITY`. - Renamed `CVolume::inc()` to `CVolume::increase()` and `CVolume::dec()` to `CVolume::decrease()` for clarity (could have been confused as increment and decrement functions). * Improved `Stream::begin_write()`: - The buffer returned is now converted to a slice for you, rather than burdening the caller. - The return type has ditched the custom `BufferResult` object. * Derived more common traits for a handful of structs/enums. * Implemented `PartialEq` for `channelmap::Map` and `volume::CVolume`. * Removed `Copy` and `Clone` derives from `def::SpawnApi`. * Improved time handling: - Added `Timeval` wrapper. - Introduced `MicroSeconds` as an `u64` wrapper, replacing use of `sample::Usecs` (now removed). - Tidied up conversion constants. Note, names (and in some cases types) have changed. - Re-exported `libc::timeval` (primarily for timer event callback use) from the `timeval` mod instead of `mainloop::events::timer`. * Added and put to use wrapper for ‘quit return values’. * Changed a handful of methods to return `String` rather than `CStr`. The original intention was to avoid unnecessary conversion, but users most likely would prefer `String`s, and there should definitely not be a problem with “lossy” utf8 conversion in these cases. * Changed return value of `Stream::get_underflow_index()` to unsigned. * Hid string printing length constants, only used internally. * Rewrote string printing functions to use a `Vec` as the string buffer instead of `libc::malloc()`, thus more simple, and removed the `Option` result wrapper. * Changed several `sample::Format` related functions to methods. * Minor `Stream` documentation fixes. # 1.1 (May 27th, 2018) * Privatised the `from_raw()` method of various objects, having become aware of the more granular `pub(crate)` type scope limiting syntax. Note, `from_raw_weak()` methods designed for use in callbacks are still available. * Also privatised inner pointer attributes of structs in a few places on the same basis. * Privatised the `utils::unwrap_optional_callback()` function. * Privatised timer event's `get_ptr()` method. * Various functions have been changed to take immutable references for certain params, reflecting the change in version 1.1 of the `libpulse-sys` crate. * Promoted `self` reference to mutable for various methods across context, mainloops, streams, proplist and operation objects. Although our objects in these cases can be used immutably (no change in the wrapped pointer), we should reflect mutability needs of the operation being performed on the underlying C objects. * Updated required dependencies: - `libpulse-sys` from 1.0 to 1.1. # 1.0.5 (May 27th, 2018) * Fixed and simplified `Proplist` iteration: - Fixed an infinite loop bug: I misread the documentation, it’s the return value from the C function call that will be NULL when it reaches the end of the list, not the state variable. - Fixed infinite loop bug #2: The state tracking variable used for the underlying C function cannot be hidden within the iterate function, it causes an infinite loop whereby the function always just returns the first entry wrapped in `Some`. I don’t know what I was thinking. - Implemented proper iterator semantics. The `iterate()` method was renamed `iter()` and now returns an actual Rust `Iterator` object, which makes iterating much more simple and tidy. * Made `self` immutable for `CVolume::is_muted()` and `CVolume::is_norm()`. * Fixed unwanted double option wrapping of callback fn ptr with `Stream` write methods. * Combined `Stream::write_ext_free()`’s `free_cb_data` param with `free_cb` as tuple, as done elsewhere. Note, version number 1.0.4 skipped (it was used for non-crate project changes). # 1.0.3 (February 10th, 2018) * Added `From` methods for transmuting between certain introspection structs and their `sys` counterparts. (They are identical, and only duplicated in the binding to add documentation). # 1.0.2 (February 9th, 2018) * Added travis badge. # 1.0.1 (February 2nd, 2018) * Fixed toml file license string `LGPL-2.1` → `LGPL-2.1+`. * Fixed toml file missing author email address. * Removed obsolete readme doc links. # 1.0 (January 24th, 2018) * Original release. [`bitflags`]: https://docs.rs/bitflags [issue11]: https://github.com/jnqnfe/pulse-binding-rust/issues/11 [issue26]: https://github.com/jnqnfe/pulse-binding-rust/issues/26 [issue48]: https://github.com/jnqnfe/pulse-binding-rust/issues/48 libpulse-binding-2.28.1/Cargo.toml0000644000000033060000000000100123550ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.56" name = "libpulse-binding" version = "2.28.1" authors = ["Lyndon Brown "] exclude = ["README.md"] description = "A Rust language binding for the PulseAudio libpulse library." homepage = "https://github.com/jnqnfe/pulse-binding-rust" readme = "README.md" keywords = [ "binding", "pulseaudio", "audio", ] license = "MIT OR Apache-2.0" repository = "https://github.com/jnqnfe/pulse-binding-rust" [package.metadata.docs.rs] all-features = false no-default-features = true rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.bitflags] version = "1.2" [dependencies.libc] version = "0.2" [dependencies.libpulse-sys] version = "1.21" default-features = false [dependencies.num-derive] version = "0.3" [dependencies.num-traits] version = "0.2" [features] default = ["pa_v8"] pa_v12 = [ "pa_v8", "libpulse-sys/pa_v12", ] pa_v13 = [ "pa_v12", "libpulse-sys/pa_v13", ] pa_v14 = [ "pa_v13", "libpulse-sys/pa_v14", ] pa_v15 = [ "pa_v14", "libpulse-sys/pa_v15", ] pa_v6 = ["libpulse-sys/pa_v6"] pa_v8 = [ "pa_v6", "libpulse-sys/pa_v8", ] [target."cfg(windows)".dependencies.winapi] version = "0.3" features = ["winsock2"] default-features = false libpulse-binding-2.28.1/Cargo.toml.orig000064400000000000000000000023371046102023000160410ustar 00000000000000[package] edition = "2021" name = "libpulse-binding" version = "2.28.1" authors = ["Lyndon Brown "] license = "MIT OR Apache-2.0" readme = "README.md" description = "A Rust language binding for the PulseAudio libpulse library." keywords = ["binding", "pulseaudio", "audio"] homepage = "https://github.com/jnqnfe/pulse-binding-rust" repository = "https://github.com/jnqnfe/pulse-binding-rust" exclude = ["README.md"] rust-version = "1.56" [dependencies] libc = "0.2" bitflags = "1.2" num-traits = "0.2" num-derive = "0.3" libpulse-sys = { path = "../pulse-sys", version = "1.21", default-features = false } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winsock2"], default-features = false } [features] default = ["pa_v8"] # PA version compatibility selection # See the overall project `COMPATIBILITY.md` file for explanation. pa_v15 = ["pa_v14", "libpulse-sys/pa_v15"] pa_v14 = ["pa_v13", "libpulse-sys/pa_v14"] pa_v13 = ["pa_v12", "libpulse-sys/pa_v13"] pa_v12 = ["pa_v8", "libpulse-sys/pa_v12"] pa_v8 = ["pa_v6", "libpulse-sys/pa_v8"] pa_v6 = [ "libpulse-sys/pa_v6"] [package.metadata.docs.rs] all-features = false no-default-features = true rustdoc-args = ["--cfg", "docsrs"] libpulse-binding-2.28.1/LICENSE-APACHE000064400000000000000000000251371046102023000151010ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. libpulse-binding-2.28.1/LICENSE-MIT000064400000000000000000000020401046102023000145750ustar 00000000000000Copyright (c) 2019 Lyndon Brown Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libpulse-binding-2.28.1/README.md000064400000000000000000000043131046102023000144250ustar 00000000000000libpulse-binding ================ [GitHub Workflow Status](https://github.com/jnqnfe/pulse-binding-rust/actions) [crates.io](https://crates.io/crates/libpulse-binding) [docs.rs](https://docs.rs/libpulse-binding) [min-rust-version](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) A Rust language binding for the PulseAudio libpulse library. ## Usage Add this crate to the dependencies specified in your `Cargo.toml`: ```toml [dependencies] libpulse-binding = "2.0" ``` Though you may wish to rename the crate to a shorter name (for example `pulse`) for cleaner references within your code: ```toml [dependencies] pulse = { version = "2.0", package = "libpulse-binding" } ``` An alternative to that which some may prefer is: ```toml [dependencies.pulse] version = "2.0" package = "libpulse-binding" ``` ### PulseAudio version compatibility The default functionality provided is support for PulseAudio version 8.0 or newer. This should be good enough for most needs, however if you _need_ to use something only available in a newer version then you can select the corresponding version compatibility feature to raise the minimum compatibility level. You can also reduce support down to even older versions if you wish. See the overall project `COMPATIBILITY.md` file for further details. ## License Licensed under either of * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) * [MIT license](http://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. libpulse-binding-2.28.1/src/callbacks.rs000064400000000000000000000161761046102023000162340ustar 00000000000000// Copyright 2018 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Callback handling. use std::os::raw::c_void; use std::ptr::null_mut; use std::marker::PhantomData; use std::mem::ManuallyDrop; /// List result instance. /// /// Fetching a list can result in a callback being fired for each list item, and then once more to /// signal the end of the list having been reached. This type is used to distinguish such state to a /// closure callback. #[derive(Debug)] pub enum ListResult { /// List item Item(T), /// End of list reached End, /// Failure, an error occurred Error, } /// A saved multi-use callback. /// /// Closures of multi-use callbacks (those that may be called multiple times) need saving, and /// releasing later at an appropriate time (on change of registered callback, or on destruction of /// associated object). This is used for saving the pointer to it for such deferred destruction. pub(crate) struct MultiUseCallback { saved: Option<*mut Box>, proxy: PhantomData<*const ProxyProto>, } impl Default for MultiUseCallback { #[inline(always)] fn default() -> Self { MultiUseCallback:: { saved: None, proxy: PhantomData } } } impl MultiUseCallback { /// Creates a new instance. /// /// **Note**, an existing instance should always be overwritten with a new one, to ensure the /// old one is correctly freed. #[inline] pub fn new(cb: Option>) -> Self { match cb { Some(f) => MultiUseCallback:: { saved: Some(Box::into_raw(Box::new(f))), proxy: PhantomData, }, None => Default::default(), } } /// Returns callback params to give to the C API (a tuple of function pointer and data pointer). #[inline] pub fn get_capi_params(&self, proxy: ProxyProto) -> (Option, *mut c_void) { match self.saved { Some(ref f) => (Some(proxy), *f as *mut c_void), None => (None, null_mut::()), } } /// Converts void closure pointer back to real type. /// /// For use in callback proxies. /// /// Only a reference is returned, in order to deliberately avoid reclaiming ownership and thus /// triggering of destruction. /// /// Panics if `ptr` is null. #[inline(always)] pub fn get_callback<'a>(ptr: *mut c_void) -> &'a mut Box { assert!(!ptr.is_null()); // Note, does NOT destroy closure callback after use - only handles pointer unsafe { &mut *(ptr as *mut Box) } } } impl Drop for MultiUseCallback { #[inline] fn drop(&mut self) { if self.saved.is_some() { let _to_drop = unsafe { Box::from_raw(self.saved.unwrap()) }; } } } /// Converts single-use-callback closure to pointer for C API. /// /// It can be restored in an `extern "C"` callback proxy with `get_su_callback`. /// /// Note: The closure itself needs to exist on the heap, and here we take it as such (wrapped in a /// `Box`); from this you may assume that a pointer is already available. However, this is a pointer /// to a trait object, and these are special in Rust, they are twice the size of a normal pointer /// because in actual fact it is implemented as a pair of pointers (one to a type instance and one /// to a ‘vtable’). We can only pass a normal sized pointer through the C API, so we must further /// box it, producing `Box>` which we convert to `*mut Box` and then further /// to simply `*mut c_void`. #[inline(always)] pub(crate) fn box_closure_get_capi_ptr(callback: Box) -> *mut c_void { Box::into_raw(Box::new(callback)) as *mut c_void } /// Gets the C API callback params (function pointer and data pointer pair), for an optional /// single-use callback closure. /// /// The proxy function must be specified. If `callback` is `None` then a pair of null pointers will /// be returned. Otherwise, a pair consisting of the given proxy and a pointer for the given closure /// will be returned. The data pointer can be restored to the actual (boxed) closure in the /// `extern "C"` callback proxy with `get_su_callback`. #[inline] pub(crate) fn get_su_capi_params( callback: Option>, proxy: ProxyProto) -> (Option, *mut c_void) { match callback { Some(f) => (Some(proxy), box_closure_get_capi_ptr::(f)), None => (None, null_mut::()), } } /// Converts void single-use-callback closure pointer back to real type. /// /// For use in callback proxies. /// /// Returns ownership of the closure, thus it can be destroyed after use. /// /// Panics if `ptr` is null. #[inline(always)] pub(crate) fn get_su_callback(ptr: *mut c_void) -> Box> { assert!(!ptr.is_null()); unsafe { Box::from_raw(ptr as *mut Box) } } /// Used by list-iteration style callback proxies, which are single use, but make multiple /// executions of the callback, once per item and once to signal end-of-list. #[inline] pub(crate) fn callback_for_list_instance(i: *const ItemRaw, eol: i32, userdata: *mut c_void, conv: Conv) where Conv: Fn(*const ItemRaw) -> Item // Converter, from raw item pointer to item wrapper { assert!(!userdata.is_null()); let mut callback = ManuallyDrop::new(unsafe { Box::from_raw(userdata as *mut Box)>) }); // NOTE: The creation of this reference variable is required to fix compiling before 1.46! use std::ops::DerefMut; #[allow(unused_mut)] let mut callback_ref = callback.deref_mut(); match eol { // Item instance (NOT end-of-list or error) 0 => { assert!(!i.is_null()); let item = conv(i); // Convert from raw item pointer to item wrapper (callback_ref)(ListResult::Item(&item)); // Deliberately not dropping! return; }, // End of list marker i if i > 0 => { (callback_ref)(ListResult::End); }, // Error _ => { (callback_ref)(ListResult::Error); }, } unsafe { ManuallyDrop::drop(&mut callback) }; } libpulse-binding-2.28.1/src/channelmap.rs000064400000000000000000000531661046102023000164230ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Constants and routines for handing channel mapping. //! //! # Overview //! //! Channel maps provide a way to associate channels in a stream with a specific speaker position. //! This relieves applications of having to make sure their channel order is identical to the final //! output. //! //! # Initialisation //! //! A channel map consists of an array of [`Position`] values, one for each channel. This array is //! stored together with a channel count in a [`Map`] structure. //! //! Before filling the structure, the application must initialise it using [`Map::init()`]. There //! are also a number of convenience functions for standard channel mappings: //! //! * [`Map::init_mono()`]: Create a channel map with only mono audio. //! * [`Map::init_stereo()`]: Create a standard stereo mapping. //! * [`Map::init_auto()`]: Create a standard channel map for a specific number of channels. //! * [`Map::init_extend()`]: Similar to [`Map::init_auto()`] but synthesize a channel map if no //! predefined one is known for the specified number of channels. use std::borrow::{Borrow, BorrowMut}; use std::ffi::{CStr, CString}; use std::borrow::Cow; use num_derive::{FromPrimitive, ToPrimitive}; use crate::sample; pub use capi::pa_channel_map_def_t as MapDef; /// A mask of channel positions. pub type PositionMask = capi::channelmap::pa_channel_position_mask_t; /// Position mask covering all positions. pub const POSITION_MASK_ALL: PositionMask = 0xffffffffffffffffu64; /// A list of channel labels. /// /// Note, certain aliases, specifically `Left`, `Right`, `Center` and `Subwoofer`, available in the /// equivalent C enum are not provided here, since Rust does not allow aliases. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] pub enum Position { /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate (C API) equivalent */ /// Invalid. Invalid = -1, /// Mono. Mono = 0, /// Apple, Dolby call this ‘Left’. FrontLeft, /// Apple, Dolby call this ‘Right’. FrontRight, /// Apple, Dolby call this ‘Center’. FrontCenter, /// Microsoft calls this ‘Back Center’, Apple calls this ‘Center Surround’, /// Dolby calls this ‘Surround Rear Center’. RearCenter, /// Microsoft calls this ‘Back Left’, Apple calls this ‘Left Surround’, /// Dolby calls this ‘Surround Rear Left’. RearLeft, /// Microsoft calls this ‘Back Right’, Apple calls this ‘Right Surround’, /// Dolby calls this ‘Surround Rear Right’. RearRight, /// Aka subwoofer. Microsoft calls this ‘Low Frequency’, /// Apple calls this ‘LFEScreen’. Lfe, /// Apple, Dolby call this ‘Left Center’. FrontLeftOfCenter, /// Apple, Dolby call this ‘Right Center’. FrontRightOfCenter, /// Apple calls this ‘Left Surround Direct’, /// Dolby calls this ‘Surround Left’. SideLeft, /// Apple calls this ‘Right Surround Direct’, /// Dolby calls this ‘Surround Right’. SideRight, /// Auxillary 0. Aux0, /// Auxillary 1. Aux1, /// Auxillary 2. Aux2, /// Auxillary 3. Aux3, /// Auxillary 4. Aux4, /// Auxillary 5. Aux5, /// Auxillary 6. Aux6, /// Auxillary 7. Aux7, /// Auxillary 8. Aux8, /// Auxillary 9. Aux9, /// Auxillary 10. Aux10, /// Auxillary 11. Aux11, /// Auxillary 12. Aux12, /// Auxillary 13. Aux13, /// Auxillary 14. Aux14, /// Auxillary 15. Aux15, /// Auxillary 16. Aux16, /// Auxillary 17. Aux17, /// Auxillary 18. Aux18, /// Auxillary 19. Aux19, /// Auxillary 20. Aux20, /// Auxillary 21. Aux21, /// Auxillary 22. Aux22, /// Auxillary 23. Aux23, /// Auxillary 24. Aux24, /// Auxillary 25. Aux25, /// Auxillary 26. Aux26, /// Auxillary 27. Aux27, /// Auxillary 28. Aux28, /// Auxillary 29. Aux29, /// Auxillary 30. Aux30, /// Auxillary 31. Aux31, /// Apple calls this ‘Top Center Surround’. TopCenter, /// Apple calls this ‘Vertical Height Left’. TopFrontLeft, /// Apple calls this ‘Vertical Height Right’. TopFrontRight, /// Apple calls this ‘Vertical Height Center’. TopFrontCenter, /// Microsoft and Apple call this ‘Top Back Left’. TopRearLeft, /// Microsoft and Apple call this ‘Top Back Right’. TopRearRight, /// Microsoft and Apple call this ‘Top Back Center’. TopRearCenter, } impl Default for Position { #[inline(always)] fn default() -> Self { Position::Invalid } } /// Check is equal to `sys` equivalent #[test] fn pos_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); // Check order and value of variants match // No point checking conversions in both directions since both are a transmute assert_eq!(Position::Invalid, Position::from(capi::pa_channel_position_t::Invalid)); assert_eq!(Position::Mono, Position::from(capi::pa_channel_position_t::Mono)); assert_eq!(Position::FrontLeft, Position::from(capi::pa_channel_position_t::FrontLeft)); assert_eq!(Position::FrontRight, Position::from(capi::pa_channel_position_t::FrontRight)); assert_eq!(Position::FrontCenter, Position::from(capi::pa_channel_position_t::FrontCenter)); assert_eq!(Position::RearCenter, Position::from(capi::pa_channel_position_t::RearCenter)); assert_eq!(Position::RearLeft, Position::from(capi::pa_channel_position_t::RearLeft)); assert_eq!(Position::RearRight, Position::from(capi::pa_channel_position_t::RearRight)); assert_eq!(Position::Lfe, Position::from(capi::pa_channel_position_t::Lfe)); assert_eq!(Position::FrontLeftOfCenter, Position::from(capi::pa_channel_position_t::FrontLeftOfCenter)); assert_eq!(Position::FrontRightOfCenter, Position::from(capi::pa_channel_position_t::FrontRightOfCenter)); assert_eq!(Position::SideLeft, Position::from(capi::pa_channel_position_t::SideLeft)); assert_eq!(Position::SideRight, Position::from(capi::pa_channel_position_t::SideRight)); assert_eq!(Position::Aux0, Position::from(capi::pa_channel_position_t::Aux0)); assert_eq!(Position::Aux1, Position::from(capi::pa_channel_position_t::Aux1)); assert_eq!(Position::Aux2, Position::from(capi::pa_channel_position_t::Aux2)); assert_eq!(Position::Aux3, Position::from(capi::pa_channel_position_t::Aux3)); assert_eq!(Position::Aux4, Position::from(capi::pa_channel_position_t::Aux4)); assert_eq!(Position::Aux5, Position::from(capi::pa_channel_position_t::Aux5)); assert_eq!(Position::Aux6, Position::from(capi::pa_channel_position_t::Aux6)); assert_eq!(Position::Aux7, Position::from(capi::pa_channel_position_t::Aux7)); assert_eq!(Position::Aux8, Position::from(capi::pa_channel_position_t::Aux8)); assert_eq!(Position::Aux9, Position::from(capi::pa_channel_position_t::Aux9)); assert_eq!(Position::Aux10, Position::from(capi::pa_channel_position_t::Aux10)); assert_eq!(Position::Aux11, Position::from(capi::pa_channel_position_t::Aux11)); assert_eq!(Position::Aux12, Position::from(capi::pa_channel_position_t::Aux12)); assert_eq!(Position::Aux13, Position::from(capi::pa_channel_position_t::Aux13)); assert_eq!(Position::Aux14, Position::from(capi::pa_channel_position_t::Aux14)); assert_eq!(Position::Aux15, Position::from(capi::pa_channel_position_t::Aux15)); assert_eq!(Position::Aux16, Position::from(capi::pa_channel_position_t::Aux16)); assert_eq!(Position::Aux17, Position::from(capi::pa_channel_position_t::Aux17)); assert_eq!(Position::Aux18, Position::from(capi::pa_channel_position_t::Aux18)); assert_eq!(Position::Aux19, Position::from(capi::pa_channel_position_t::Aux19)); assert_eq!(Position::Aux20, Position::from(capi::pa_channel_position_t::Aux20)); assert_eq!(Position::Aux21, Position::from(capi::pa_channel_position_t::Aux21)); assert_eq!(Position::Aux22, Position::from(capi::pa_channel_position_t::Aux22)); assert_eq!(Position::Aux23, Position::from(capi::pa_channel_position_t::Aux23)); assert_eq!(Position::Aux24, Position::from(capi::pa_channel_position_t::Aux24)); assert_eq!(Position::Aux25, Position::from(capi::pa_channel_position_t::Aux25)); assert_eq!(Position::Aux26, Position::from(capi::pa_channel_position_t::Aux26)); assert_eq!(Position::Aux27, Position::from(capi::pa_channel_position_t::Aux27)); assert_eq!(Position::Aux28, Position::from(capi::pa_channel_position_t::Aux28)); assert_eq!(Position::Aux29, Position::from(capi::pa_channel_position_t::Aux29)); assert_eq!(Position::Aux30, Position::from(capi::pa_channel_position_t::Aux30)); assert_eq!(Position::Aux31, Position::from(capi::pa_channel_position_t::Aux31)); assert_eq!(Position::TopCenter, Position::from(capi::pa_channel_position_t::TopCenter)); assert_eq!(Position::TopFrontLeft, Position::from(capi::pa_channel_position_t::TopFrontLeft)); assert_eq!(Position::TopFrontRight, Position::from(capi::pa_channel_position_t::TopFrontRight)); assert_eq!(Position::TopFrontCenter, Position::from(capi::pa_channel_position_t::TopFrontCenter)); assert_eq!(Position::TopRearLeft, Position::from(capi::pa_channel_position_t::TopRearLeft)); assert_eq!(Position::TopRearRight, Position::from(capi::pa_channel_position_t::TopRearRight)); assert_eq!(Position::TopRearCenter, Position::from(capi::pa_channel_position_t::TopRearCenter)); } impl From for capi::pa_channel_position_t { #[inline] fn from(p: Position) -> Self { unsafe { std::mem::transmute(p) } } } impl From for Position { #[inline] fn from(p: capi::pa_channel_position_t) -> Self { unsafe { std::mem::transmute(p) } } } /// A channel map which can be used to attach labels to specific channels of a stream. /// /// These values are relevant for conversion and mixing of streams. #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct Map { /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */ /// Number of channels mapped. channels: u8, /// Channel labels. map: [Position; Self::CHANNELS_MAX as usize], } impl Borrow<[Position]> for Map { fn borrow(&self) -> &[Position] { &self.map[..self.channels as usize] } } impl BorrowMut<[Position]> for Map { fn borrow_mut(&mut self) -> &mut [Position] { &mut self.map[..self.channels as usize] } } /// Test size is equal to `sys` equivalent #[test] fn map_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); } impl AsRef for Map { #[inline] fn as_ref(&self) -> &capi::pa_channel_map { unsafe { &*(self as *const Self as *const capi::pa_channel_map) } } } impl AsMut for Map { #[inline] fn as_mut(&mut self) -> &mut capi::pa_channel_map { unsafe { &mut *(self as *mut Self as *mut capi::pa_channel_map) } } } impl AsRef for capi::pa_channel_map { #[inline] fn as_ref(&self) -> &Map { unsafe { &*(self as *const Self as *const Map) } } } impl From for Map { #[inline] fn from(m: capi::pa_channel_map) -> Self { unsafe { std::mem::transmute(m) } } } impl Default for Map { fn default() -> Self { Self { channels: 0, map: [Position::Invalid; Self::CHANNELS_MAX as usize] } } } impl PartialEq for Map { #[inline] fn eq(&self, other: &Self) -> bool { unsafe { capi::pa_channel_map_equal(self.as_ref(), other.as_ref()) == 1 } } } impl Position { /// Makes a bit mask from a channel position. pub const fn to_mask(self) -> PositionMask { match self { Position::Invalid => 0, _ => (1 as PositionMask) << (self as PositionMask), } } /// Gets a text label for the specified channel position. pub fn to_string(pos: Self) -> Option> { let ptr = unsafe { capi::pa_channel_position_to_string(pos.into()) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }), true => None, } } /// Gets a human readable text label for the specified channel position. pub fn to_pretty_string(pos: Self) -> Option { let ptr = unsafe { capi::pa_channel_position_to_pretty_string(pos.into()) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }), true => None, } } /// Creates a new instance from a string representation, as given by /// [`to_string()`](Self::to_string). pub fn from_string(s: &str) -> Self { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_str = CString::new(s.clone()).unwrap(); unsafe { capi::pa_channel_position_from_string(c_str.as_ptr()).into() } } } impl Map { /// Maximum number of allowed channels. pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX; /// Parses a channel position list or well-known mapping name into a channel map structure. /// /// This turns the output of [`print()`](Self::print) and [`to_name()`](Self::to_name) back into /// a `Map`. pub fn new_from_string(s: &str) -> Result { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_str = CString::new(s.clone()).unwrap(); let mut map: Self = Self::default(); unsafe { if capi::pa_channel_map_parse((&mut map).as_mut(), c_str.as_ptr()).is_null() { return Err(()); } } Ok(map) } /// Initializes the specified channel map and returns a pointer to it. /// /// The map will have a defined state but [`is_valid()`](Self::is_valid) will fail for it. #[inline] pub fn init(&mut self) -> &mut Self { unsafe { capi::pa_channel_map_init(self.as_mut()) }; self } /// Initializes the specified channel map for monaural audio and returns a pointer to it. #[inline] pub fn init_mono(&mut self) -> &mut Self { unsafe { capi::pa_channel_map_init_mono(self.as_mut()) }; self } /// Initializes the specified channel map for stereophonic audio and returns a pointer to it. #[inline] pub fn init_stereo(&mut self) -> &mut Self { unsafe { capi::pa_channel_map_init_stereo(self.as_mut()) }; self } /// Initializes the specified channel map for the specified number of channels using default /// labels and returns a pointer to it. /// /// This call will fail (return `None`) if there is no default channel map known for this /// specific number of channels and mapping. pub fn init_auto(&mut self, channels: u8, def: MapDef) -> Option<&mut Self> { debug_assert!(channels <= Self::CHANNELS_MAX); unsafe { if capi::pa_channel_map_init_auto(self.as_mut(), channels as u32, def).is_null() { return None; } } Some(self) } /// Similar to [`init_auto()`](Self::init_auto) but instead of failing if no default mapping is /// known with the specified parameters it will synthesize a mapping based on a known mapping /// with fewer channels and fill up the rest with AUX0...AUX31 channels. pub fn init_extend(&mut self, channels: u8, def: MapDef) -> &mut Self { debug_assert!(channels <= Self::CHANNELS_MAX); unsafe { capi::pa_channel_map_init_extend(self.as_mut(), channels as u32, def) }; self } /// Checks whether or not the map is considered valid. #[inline] pub fn is_valid(&self) -> bool { unsafe { capi::pa_channel_map_valid(self.as_ref()) != 0 } } /// Gets the number of active channels. #[inline] pub const fn len(&self) -> u8 { self.channels } /// Sets the number of active channels. /// /// Positions for up to [`Self::CHANNELS_MAX`] channels can be held. This sets the portion of /// the internal array considered “active” and thus available for reading/writing (i.e. when /// borrowing `self` as a slice). /// /// **Panics** if the number of channels specified is greater than [`Self::CHANNELS_MAX`]. #[inline] pub fn set_len(&mut self, channels: u8) { assert!(channels <= Self::CHANNELS_MAX); self.channels = channels; } /// Gets an immutable slice of the set of “active” channels. #[inline] pub fn get(&self) -> &[Position] { self.borrow() } /// Gets a mutable slice of the set of “active” channels. #[inline] pub fn get_mut(&mut self) -> &mut [Position] { self.borrow_mut() } /// Makes a human readable string from the map. pub fn print(&self) -> String { const PRINT_MAX: usize = capi::PA_CHANNEL_MAP_SNPRINT_MAX; let mut tmp = Vec::with_capacity(PRINT_MAX); unsafe { capi::pa_channel_map_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.as_ref()); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } /// Checks whether or not the specified map is compatible with the specified sample spec. #[inline] pub fn is_compatible_with_sample_spec(&self, ss: &sample::Spec) -> bool { unsafe { capi::pa_channel_map_compatible(self.as_ref(), ss.as_ref()) != 0 } } /// Checks whether every channel defined in `of` is also defined in self. #[inline] pub fn is_superset_of(&self, of: &Self) -> bool { unsafe { capi::pa_channel_map_superset(self.as_ref(), of.as_ref()) != 0 } } /// Checks whether or not it makes sense to apply a volume “balance” with this mapping, i.e. if /// there are left/right channels available. #[inline] pub fn can_balance(&self) -> bool { unsafe { capi::pa_channel_map_can_balance(self.as_ref()) != 0 } } /// Checks whether or not it makes sense to apply a volume “fade” (i.e. “balance” between front /// and rear) with this mapping, i.e. if there are front/rear channels available. #[inline] pub fn can_fade(&self) -> bool { unsafe { capi::pa_channel_map_can_fade(self.as_ref()) != 0 } } /// Checks whether or not it makes sense to apply a volume “LFE balance” (i.e. “balance” between /// LFE and non-LFE channels) with this mapping, i.e. if there are LFE and non-LFE channels /// available. #[inline] #[cfg(any(doc, feature = "pa_v8"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v8")))] pub fn can_lfe_balance(&self) -> bool { unsafe { capi::pa_channel_map_can_lfe_balance(self.as_ref()) != 0 } } /// Tries to find a well-known channel mapping name for this channel mapping, i.e. “stereo”, /// “surround-71” and so on. This name can be parsed with /// [`new_from_string()`](Self::new_from_string). pub fn to_name(&self) -> Option> { let ptr = unsafe { capi::pa_channel_map_to_name(self.as_ref()) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }), true => None, } } /// Similar to [`to_name()`](Self::to_name), but returning prettier, human readable text labels, /// i.e. “Stereo”, “Surround 7.1” and so on. pub fn to_pretty_name(&self) -> Option { let ptr = unsafe { capi::pa_channel_map_to_pretty_name(self.as_ref()) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }), true => None, } } /// Checks whether or not the specified channel position is available at least once in the map. #[inline] pub fn has_position(&self, p: Position) -> bool { unsafe { capi::pa_channel_map_has_position(self.as_ref(), p.into()) != 0 } } /// Generates a bit mask from a map. #[inline] pub fn get_mask(&self) -> PositionMask { unsafe { capi::pa_channel_map_mask(self.as_ref()) } } } libpulse-binding-2.28.1/src/context/ext_device_manager.rs000064400000000000000000000306671046102023000216130ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Routines for controlling module-device-manager. use std::ffi::{CStr, CString}; use std::borrow::Cow; use std::os::raw::{c_char, c_void}; use std::ptr::{null, null_mut}; use capi::pa_ext_device_manager_info as InfoInternal; use capi::pa_ext_device_manager_role_priority_info as RolePriorityInfoInternal; use super::{ContextInternal, Context}; use crate::def; use crate::callbacks::{ListResult, box_closure_get_capi_ptr, callback_for_list_instance}; use crate::operation::Operation; /// Role priority information. #[derive(Debug)] pub struct RolePriorityInfo<'a> { /// Role name pub role: Option>, /// Priority pub priority: u32, } impl<'a> RolePriorityInfo<'a> { fn new_from_raw(p: *const RolePriorityInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { p.as_ref().unwrap() }; unsafe { RolePriorityInfo { role: match src.role.is_null() { false => Some(CStr::from_ptr(src.role).to_string_lossy()), true => None, }, priority: src.priority, } } } } /// Stores information about one device in the device database that is maintained by /// module-device-manager. #[derive(Debug)] pub struct Info<'a> { /// Identifier string of the device. A string like “sink:” or similar followed by the name of /// the device. pub name: Option>, /// The description of the device when it was last seen, if applicable and saved. pub description: Option>, /// The icon given to the device. pub icon: Option>, /// The device index if it is currently available or `None` if invalid. pub index: Option, /// A set of role priority structures. pub role_priorities: Vec>, } impl<'a> Info<'a> { fn new_from_raw(p: *const InfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { p.as_ref().unwrap() }; let mut rp_vec = Vec::with_capacity(src.n_role_priorities as usize); assert!(src.n_role_priorities == 0 || !src.role_priorities.is_null()); for i in 0..src.n_role_priorities as isize { let indexed_ptr = unsafe { src.role_priorities.offset(i) as *mut RolePriorityInfoInternal }; if !indexed_ptr.is_null() { rp_vec.push(RolePriorityInfo::new_from_raw(indexed_ptr)); } } unsafe { Info { name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, description: match src.description.is_null() { false => Some(CStr::from_ptr(src.description).to_string_lossy()), true => None, }, icon: match src.icon.is_null() { false => Some(CStr::from_ptr(src.icon).to_string_lossy()), true => None, }, index: match src.index { def::INVALID_INDEX => None, i => Some(i), }, role_priorities: rp_vec, } } } } /// A wrapper object providing device manager routines to a context. /// /// Note: Saves a copy of active multi-use closure callbacks, which it frees on drop. pub struct DeviceManager { context: *mut ContextInternal, /// Multi-use callback closure pointers cb_ptrs: CallbackPointers, } unsafe impl Send for DeviceManager {} unsafe impl Sync for DeviceManager {} /// Holds copies of callback closure pointers, for those that are “multi-use” (may be fired multiple /// times), for freeing at the appropriate time. #[derive(Default)] struct CallbackPointers { subscribe: super::ExtSubscribeCb, } impl Context { /// Gets a device manager object linked to the current context, giving access to device manager /// routines. /// /// See [`context::ext_device_manager`](mod@crate::context::ext_device_manager). pub fn device_manager(&self) -> DeviceManager { unsafe { capi::pa_context_ref(self.ptr) }; DeviceManager::from_raw(self.ptr) } } impl DeviceManager { /// Creates a new `DeviceManager` from an existing [`ContextInternal`] pointer. fn from_raw(context: *mut ContextInternal) -> Self { Self { context: context, cb_ptrs: Default::default() } } /// Tests if this extension module is available in the server. /// /// Panics if the underlying C function returns a null pointer. pub fn test(&mut self, callback: F) -> Operation where F: FnMut(u32) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_manager_test(self.context, Some(super::ext_test_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Reads all entries from the device database. /// /// Panics if the underlying C function returns a null pointer. pub fn read(&mut self, callback: F) -> Operation)> where F: FnMut(ListResult<&Info>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_manager_read(self.context, Some(read_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Sets the description for a device. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn set_device_description(&mut self, device: &str, description: &str, callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a // variable, leading to as_ptr() giving dangling pointers! let c_dev = CString::new(device.clone()).unwrap(); let c_desc = CString::new(description.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_manager_set_device_description(self.context, c_dev.as_ptr(), c_desc.as_ptr(), Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Deletes entries from the device database. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn delete(&mut self, devices: &[&str], callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let mut c_devs: Vec = Vec::with_capacity(devices.len()); for device in devices { c_devs.push(CString::new(*device).unwrap()); } // Capture array of pointers to the above CString values. // We also add a `NULL` pointer entry on the end, as expected by the C function called here. let mut c_dev_ptrs: Vec<*const c_char> = Vec::with_capacity(c_devs.len() + 1); for c_dev in &c_devs { c_dev_ptrs.push(c_dev.as_ptr()); } c_dev_ptrs.push(null()); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_manager_delete(self.context, c_dev_ptrs.as_ptr(), Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Enables the role-based device-priority routing mode. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn enable_role_device_priority_routing(&mut self, enable: bool, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_manager_enable_role_device_priority_routing(self.context, enable as i32, Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Reorders the position of a given device in the priority list to give preference to it. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn reorder_devices_for_role(&mut self, role: &str, devices: &[&str], callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_role = CString::new(role.clone()).unwrap(); let mut c_devs: Vec = Vec::with_capacity(devices.len()); for device in devices { c_devs.push(CString::new(*device).unwrap()); } // Capture array of pointers to the above CString values. // We also add a `NULL` pointer entry on the end, as expected by the C function called here. let mut c_dev_ptrs: Vec<*const c_char> = Vec::with_capacity(c_devs.len() + 1); for c_dev in &c_devs { c_dev_ptrs.push(c_dev.as_ptr()); } c_dev_ptrs.push(null()); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_manager_reorder_devices_for_role(self.context, c_role.as_ptr(), c_dev_ptrs.as_ptr(), Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Subscribes to changes in the device database. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn subscribe(&mut self, enable: bool, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_manager_subscribe(self.context, enable as i32, Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the subscription callback that is called when [`subscribe()`](Self::subscribe) was /// called. pub fn set_subscribe_cb(&mut self, callback: F) where F: FnMut() + 'static { let saved = &mut self.cb_ptrs.subscribe; *saved = super::ExtSubscribeCb::new(Some(Box::new(callback))); let (cb_fn, cb_data) = saved.get_capi_params(super::ext_subscribe_cb_proxy); unsafe { capi::pa_ext_device_manager_set_subscribe_cb(self.context, cb_fn, cb_data) }; } } impl Drop for DeviceManager { fn drop(&mut self) { unsafe { capi::pa_context_unref(self.context) }; self.context = null_mut::(); } } /// Proxy for read list callbacks. /// /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn read_list_cb_proxy(_: *mut ContextInternal, i: *const InfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, Info::new_from_raw); }); } libpulse-binding-2.28.1/src/context/ext_device_restore.rs000064400000000000000000000211521046102023000216510ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Routines for controlling module-device-restore. use std::os::raw::c_void; use std::ptr::null_mut; use std::mem; use capi::pa_ext_device_restore_info as InfoInternal; use super::{ContextInternal, Context}; use crate::{def, format}; use crate::callbacks::{ ListResult, box_closure_get_capi_ptr, callback_for_list_instance, MultiUseCallback }; use crate::operation::Operation; /// Stores information about one device in the device database that is maintained by /// module-device-manager. #[derive(Debug)] pub struct Info { /// Device type sink or source? pub dtype: def::Device, /// The device index. pub index: u32, /// A set of formats. pub formats: Vec, } impl Info { fn new_from_raw(p: *const InfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { p.as_ref().unwrap() }; let mut formats_vec = Vec::with_capacity(src.n_formats as usize); assert!(src.n_formats == 0 || !src.formats.is_null()); for i in 0..src.n_formats as isize { let indexed_ptr = unsafe { (*src.formats.offset(i)) as *mut format::InfoInternal }; if !indexed_ptr.is_null() { formats_vec.push(format::Info::from_raw_weak(indexed_ptr)); } } Info { dtype: src.dtype, index: src.index, formats: formats_vec } } } /// A wrapper object providing device restore routines to a context. /// /// Note: Saves a copy of active multi-use closure callbacks, which it frees on drop. pub struct DeviceRestore { context: *mut ContextInternal, /// Multi-use callback closure pointers cb_ptrs: CallbackPointers, } unsafe impl Send for DeviceRestore {} unsafe impl Sync for DeviceRestore {} /// Holds copies of callback closure pointers, for those that are “multi-use” (may be fired multiple /// times), for freeing at the appropriate time. #[derive(Default)] struct CallbackPointers { subscribe: SubscribeCb, } type SubscribeCb = MultiUseCallback; impl Context { /// Gets a device restore object linked to the current context, giving access to device restore /// routines. /// /// See [`context::ext_device_restore`](mod@crate::context::ext_device_restore). pub fn device_restore(&self) -> DeviceRestore { unsafe { capi::pa_context_ref(self.ptr) }; DeviceRestore::from_raw(self.ptr) } } impl DeviceRestore { /// Creates a new `DeviceRestore` from an existing [`ContextInternal`] pointer. fn from_raw(context: *mut ContextInternal) -> Self { Self { context: context, cb_ptrs: Default::default() } } /// Tests if this extension module is available in the server. /// /// The callback must accept an integer, which indicates version. /// /// Panics if the underlying C function returns a null pointer. pub fn test(&mut self, callback: F) -> Operation where F: FnMut(u32) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_restore_test(self.context, Some(super::ext_test_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Subscribes to changes in the device database. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn subscribe(&mut self, enable: bool, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_restore_subscribe(self.context, enable as i32, Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the subscription callback that is called when [`subscribe()`](Self::subscribe) was /// called. /// /// The callback must accept two parameters, firstly a [`Device`] variant, and secondly an /// integer index. /// /// [`Device`]: crate::def::Device pub fn set_subscribe_cb(&mut self, callback: F) where F: FnMut(def::Device, u32) + 'static { let saved = &mut self.cb_ptrs.subscribe; *saved = SubscribeCb::new(Some(Box::new(callback))); let (cb_fn, cb_data) = saved.get_capi_params(ext_subscribe_cb_proxy); unsafe { capi::pa_ext_device_restore_set_subscribe_cb(self.context, cb_fn, cb_data); } } /// Reads the formats for all present devices from the device database. /// /// Panics if the underlying C function returns a null pointer. pub fn read_formats_all(&mut self, callback: F) -> Operation)> where F: FnMut(ListResult<&Info>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_restore_read_formats_all(self.context, Some(read_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Reads an entry from the device database. /// /// Panics if the underlying C function returns a null pointer. pub fn read_formats(&mut self, type_: def::Device, index: u32, callback: F) -> Operation)> where F: FnMut(ListResult<&Info>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_restore_read_formats(self.context, type_, index, Some(read_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Reads an entry from the device database. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn save_formats(&mut self, type_: def::Device, index: u32, formats: &mut [&mut format::Info], callback: F) -> Operation where F: FnMut(bool) + 'static { // Capture array of pointers to the above `format::InfoInternal` objects let mut format_ptrs: Vec<*mut capi::pa_format_info> = Vec::with_capacity(formats.len()); for format in formats { format_ptrs.push(unsafe { mem::transmute(&format.ptr) }); } let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_device_restore_save_formats(self.context, type_, index, format_ptrs.len() as u8, format_ptrs.as_ptr(), Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } impl Drop for DeviceRestore { fn drop(&mut self) { unsafe { capi::pa_context_unref(self.context) }; self.context = null_mut::(); } } /// Proxy for the extension subscribe callback. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn ext_subscribe_cb_proxy(_: *mut ContextInternal, type_: def::Device, index: u32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = SubscribeCb::get_callback(userdata); (callback)(type_, index); }); } /// Proxy for read list callbacks. /// /// Warning: This is for list cases only! On EOL or error it destroys the actual closure callback. extern "C" fn read_list_cb_proxy(_: *mut ContextInternal, i: *const InfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, Info::new_from_raw); }); } libpulse-binding-2.28.1/src/context/ext_stream_restore.rs000064400000000000000000000215061046102023000217100ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Routines for controlling module-stream-restore. use std::os::raw::{c_char, c_void}; use std::ffi::{CStr, CString}; use std::borrow::Cow; use std::ptr::{null, null_mut}; use std::mem; use capi::pa_ext_stream_restore_info as InfoInternal; use super::{ContextInternal, Context}; use crate::{channelmap, proplist}; use crate::callbacks::{ListResult, box_closure_get_capi_ptr, callback_for_list_instance}; use crate::{operation::Operation, volume::ChannelVolumes}; /// Stores information about one entry in the stream database that is maintained by /// module-stream-restore. #[derive(Debug)] pub struct Info<'a> { /// Identifier string of the stream. A string like “sink-input-by-role:” or similar followed by /// some arbitrary property value. pub name: Option>, /// The channel map for the volume field, if applicable. pub channel_map: channelmap::Map, /// The volume of the stream when it was seen last, if applicable and saved. pub volume: ChannelVolumes, /// The sink/source of the stream when it was last seen, if applicable and saved. pub device: Option>, /// The boolean mute state of the stream when it was last seen, if applicable and saved. pub mute: bool, } impl<'a> Info<'a> { fn new_from_raw(p: *const InfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { p.as_ref().unwrap() }; unsafe { Info { name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, channel_map: src.channel_map.into(), volume: src.volume.into(), device: match src.name.is_null() { false => Some(CStr::from_ptr(src.device).to_string_lossy()), true => None, }, mute: match src.mute { 0 => false, _ => true, }, } } } } /// A wrapper object providing stream restore routines to a context. /// /// Note: Saves a copy of active multi-use closure callbacks, which it frees on drop. pub struct StreamRestore { context: *mut ContextInternal, /// Multi-use callback closure pointers cb_ptrs: CallbackPointers, } unsafe impl Send for StreamRestore {} unsafe impl Sync for StreamRestore {} /// Holds copies of callback closure pointers, for those that are “multi-use” (may be fired multiple /// times), for freeing at the appropriate time. #[derive(Default)] struct CallbackPointers { subscribe: super::ExtSubscribeCb, } impl Context { /// Gets a stream restore object linked to the current context, giving access to stream restore /// routines. /// /// See [`context::ext_stream_restore`](mod@crate::context::ext_stream_restore). pub fn stream_restore(&self) -> StreamRestore { unsafe { capi::pa_context_ref(self.ptr) }; StreamRestore::from_raw(self.ptr) } } impl StreamRestore { /// Creates a new `StreamRestore` from an existing [`ContextInternal`] pointer. fn from_raw(context: *mut ContextInternal) -> Self { Self { context: context, cb_ptrs: Default::default() } } /// Tests if this extension module is available in the server. /// /// Panics if the underlying C function returns a null pointer. pub fn test(&mut self, callback: F) -> Operation where F: FnMut(u32) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_stream_restore_test(self.context, Some(super::ext_test_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Reads all entries from the stream database. /// /// Panics if the underlying C function returns a null pointer. pub fn read(&mut self, callback: F) -> Operation)> where F: FnMut(ListResult<&Info>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_ext_stream_restore_read(self.context, Some(read_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Stores entries in the stream database. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn write(&mut self, mode: proplist::UpdateMode, data: &[&Info], apply_immediately: bool, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_stream_restore_write(self.context, mode, mem::transmute(data.as_ptr()), data.len() as u32, apply_immediately as i32, Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Deletes entries from the stream database. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn delete(&mut self, streams: &[&str], callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let mut c_streams: Vec = Vec::with_capacity(streams.len()); for stream in streams { c_streams.push(CString::new(*stream).unwrap()); } // Capture array of pointers to the above CString values. // We also add a `NULL` pointer entry on the end, as expected by the C function called here. let mut c_stream_ptrs: Vec<*const c_char> = Vec::with_capacity(c_streams.len() + 1); for c_stream in &c_streams { c_stream_ptrs.push(c_stream.as_ptr()); } c_stream_ptrs.push(null()); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_stream_restore_delete(self.context, c_stream_ptrs.as_ptr(), Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Subscribes to changes in the stream database. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn subscribe(&mut self, enable: bool, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_ext_stream_restore_subscribe(self.context, enable as i32, Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the subscription callback that is called when [`subscribe()`](Self::subscribe) was /// called. pub fn set_subscribe_cb(&mut self, callback: F) where F: FnMut() + 'static { let saved = &mut self.cb_ptrs.subscribe; *saved = super::ExtSubscribeCb::new(Some(Box::new(callback))); let (cb_fn, cb_data) = saved.get_capi_params(super::ext_subscribe_cb_proxy); unsafe { capi::pa_ext_stream_restore_set_subscribe_cb(self.context, cb_fn, cb_data); } } } impl Drop for StreamRestore { fn drop(&mut self) { unsafe { capi::pa_context_unref(self.context) }; self.context = null_mut::(); } } /// Proxy for read list callbacks. /// /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn read_list_cb_proxy(_: *mut ContextInternal, i: *const InfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, Info::new_from_raw); }); } libpulse-binding-2.28.1/src/context/introspect.rs000064400000000000000000003144611046102023000201710ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Routines for daemon introspection. //! //! # Overview //! //! Sometimes it is necessary to query and modify global settings in the server. For this, //! PulseAudio has the introspection API. It can list sinks, sources, samples and other aspects of //! the server. It can also modify the attributes of the server that will affect operations on a //! global level, and not just the application’s context. //! //! # Usage //! //! The introspection routines are exposed as methods on an [`Introspector`] object held by the //! [`Context`] object, and can be accessed via the [`Context::introspect()`] method. //! //! # Querying //! //! All querying is done through callbacks. This approach is necessary to maintain an asynchronous //! design. The client will request the information and some time later, the server will respond //! with the desired data. //! //! Some objects can have multiple instances on the server. When requesting all of these at once, //! the callback will be called multiple times, each time with an [`ListResult`] variant. It will be //! called once for each item in turn, using the `Item` variant, and then once more time with the //! `End` variant to signal that the end of the list has been reached. If an error occurs, then //! the `Error` variant will be given. //! //! Note that even if a single object is requested, and not the entire list, the terminating call //! will still be made. //! //! Data members in the information structures are only valid during the duration of the callback. //! If they are required after the callback is finished, a deep copy of the information structure //! must be performed. //! //! # Server Information //! //! The server can be queried about its name, the environment it’s running on and the currently //! active global defaults. Calling [`Introspector::get_server_info()`] provides access to a //! [`ServerInfo`] structure containing all of these. //! //! # Memory Usage //! //! Statistics about memory usage can be fetched using [`Introspector::stat()`], giving a //! [`StatInfo`] structure. //! //! # Sinks and Sources //! //! The server can have an arbitrary number of sinks and sources. Each sink and source have both an //! index and a name associated with it. As such, there are three ways to get access to them: //! //! * By index: [`Introspector::get_sink_info_by_index()`], //! [`Introspector::get_source_info_by_index()`] //! * By name: [`Introspector::get_sink_info_by_name()`], //! [`Introspector::get_source_info_by_name()`] //! * All: [`Introspector::get_sink_info_list()`], [`Introspector::get_source_info_list()`] //! //! All three methods use the same callback and will provide a [`SinkInfo`] or [`SourceInfo`] //! structure. //! //! # Sink Inputs and Source Outputs //! //! Sink inputs and source outputs are the representations of the client ends of streams inside the //! server. I.e. they connect a client stream to one of the global sinks or sources. //! //! Sink inputs and source outputs only have an index to identify them. As such, there are only two //! ways to get information about them: //! //! * By index: [`Introspector::get_sink_input_info()`], [`Introspector::get_source_output_info()`] //! * All: [`Introspector::get_sink_input_info_list()`], //! [`Introspector::get_source_output_info_list()`] //! //! The structure returned is the [`SinkInputInfo`] or [`SourceOutputInfo`] structure. //! //! # Samples //! //! The list of cached samples can be retrieved from the server. Three methods exist for querying //! the sample cache list: //! //! * By index: [`Introspector::get_sample_info_by_index()`] //! * By name: [`Introspector::get_sample_info_by_name()`] //! * All: [`Introspector::get_sample_info_list()`] //! //! Note that this only retrieves information about the sample, not the sample data itself. //! //! # Driver Modules //! //! PulseAudio driver modules are identified by index and are retrieved using either //! [`Introspector::get_module_info()`] or [`Introspector::get_module_info_list()`]. The information //! structure is called [`ModuleInfo`]. //! //! # Clients //! //! PulseAudio clients are also identified by index and are retrieved using either //! [`Introspector::get_client_info()`] or [`Introspector::get_client_info_list()`]. The information //! structure is called [`ClientInfo`]. //! //! # Control //! //! Some parts of the server are only possible to read, but most can also be modified in different //! ways. Note that these changes will affect all connected clients and not just the one issuing the //! request. //! //! # Sinks and Sources //! //! The most common change one would want to apply to sinks and sources is to modify the volume of //! the audio. Identically to how sinks and sources can be queried, there are two ways of //! identifying them: //! //! * By index: [`Introspector::set_sink_volume_by_index()`], //! [`Introspector::set_source_volume_by_index()`] //! * By name: [`Introspector::set_sink_volume_by_name()`], //! [`Introspector::set_source_volume_by_name()`] //! //! It is also possible to mute a sink or source: //! //! * By index: [`Introspector::set_sink_mute_by_index()`], //! [`Introspector::set_source_mute_by_index()`] //! * By name: [`Introspector::set_sink_mute_by_name()`], //! [`Introspector::set_source_mute_by_name()`] //! //! # Sink Inputs and Source Outputs //! //! If an application desires to modify the volume of just a single stream (commonly one of its own //! streams), this can be done by setting the volume of its associated sink input or source output, //! using [`Introspector::set_sink_input_volume()`] or [`Introspector::set_source_output_volume()`]. //! //! It is also possible to remove sink inputs and source outputs, terminating the streams associated //! with them: //! //! * Sink input: [`Introspector::kill_sink_input()`] //! * Source output: [`Introspector::kill_source_output()`] //! //! It is strongly recommended that all volume changes are done as a direct result of user input. //! With automated requests, such as those resulting from misguided attempts of crossfading, //! PulseAudio can store the stream volume at an inappropriate moment and restore it later. Besides, //! such attempts lead to OSD popups in some desktop environments. //! //! As a special case of the general rule above, it is recommended that your application leaves the //! task of saving and restoring the volume of its streams to PulseAudio and does not attempt to do //! it by itself. PulseAudio really knows better about events such as stream moving or headphone //! plugging that would make the volume stored by the application inapplicable to the new //! configuration. //! //! Another important case where setting a sink input volume may be a bad idea is related to //! interpreters that interpret potentially untrusted scripts. PulseAudio relies on your application //! not making malicious requests (such as repeatedly setting the volume to 100%). Thus, script //! interpreters that represent a security boundary must sandbox volume-changing requests coming //! from their scripts. In the worst case, it may be necessary to apply the script-requested volume //! to the script-produced sounds by altering the samples in the script interpreter and not touching //! the sink or sink input volume as seen by PulseAudio. //! //! If an application changes any volume, it should also listen to changes of the same volume //! originating from outside the application (e.g., from the system mixer application) and update //! its user interface accordingly. Use [`Context::subscribe()`] to get such notifications. //! //! # Modules //! //! Server modules can be remotely loaded and unloaded using [`Introspector::load_module()`] and //! [`Introspector::unload_module()`]. //! //! # Messages //! //! Server objects like sinks, sink inputs or modules can register a message handler to communicate //! with clients. A message can be sent to a named message handler using //! [`Introspector::send_message_to_object()`]. //! //! # Clients //! //! The only operation supported on clients is the possibility of kicking them off the server using //! [`Introspector::kill_client()`]. use std::os::raw::c_void; #[cfg(any(doc, feature = "pa_v15"))] use std::os::raw::c_char; use std::ffi::{CStr, CString}; use std::borrow::Cow; use std::ptr::null_mut; use num_traits::FromPrimitive; use capi::pa_sink_port_info as SinkPortInfoInternal; use capi::pa_sink_info as SinkInfoInternal; use capi::pa_source_port_info as SourcePortInfoInternal; use capi::pa_source_info as SourceInfoInternal; use capi::pa_server_info as ServerInfoInternal; use capi::pa_module_info as ModuleInfoInternal; use capi::pa_client_info as ClientInfoInternal; use capi::pa_card_profile_info2 as CardProfileInfoInternal; use capi::pa_card_port_info as CardPortInfoInternal; use capi::pa_card_info as CardInfoInternal; use capi::pa_sink_input_info as SinkInputInfoInternal; use capi::pa_source_output_info as SourceOutputInfoInternal; use capi::pa_sample_info as SampleInfoInternal; use super::{Context, ContextInternal}; use crate::{def, sample, channelmap, format, direction}; use crate::time::MicroSeconds; use crate::callbacks::{ ListResult, box_closure_get_capi_ptr, callback_for_list_instance, get_su_capi_params, get_su_callback }; use crate::volume::{ChannelVolumes, Volume}; use crate::{operation::Operation, proplist::Proplist}; #[cfg(any(doc, feature = "pa_v14"))] use crate::def::DevicePortType; pub use capi::pa_stat_info as StatInfo; /// A wrapper object providing introspection routines to a context. pub struct Introspector { context: *mut super::ContextInternal, } unsafe impl Send for Introspector {} unsafe impl Sync for Introspector {} impl Context { /// Gets an introspection object linked to the current context, giving access to introspection /// routines. /// /// See [`context::introspect`](mod@crate::context::introspect). #[inline] pub fn introspect(&self) -> Introspector { unsafe { capi::pa_context_ref(self.ptr) }; Introspector::from_raw(self.ptr) } } impl Introspector { /// Creates a new `Introspector` from an existing [`ContextInternal`] pointer. #[inline(always)] fn from_raw(context: *mut ContextInternal) -> Self { Self { context: context } } } impl Drop for Introspector { fn drop(&mut self) { unsafe { capi::pa_context_unref(self.context) }; self.context = null_mut::(); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // Sink info //////////////////////////////////////////////////////////////////////////////////////////////////// /// Stores information about a specific port of a sink. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct SinkPortInfo<'a> { /// Name of this port. pub name: Option>, /// Description of this port. pub description: Option>, /// The higher this value is, the more useful this port is as a default. pub priority: u32, /// A flag indicating availability status of this port. pub available: def::PortAvailable, /// An indentifier for the group of ports that share their availability status with each other. /// /// This is meant especially for handling cases where one 3.5 mm connector is used for /// headphones, headsets and microphones, and the hardware can only tell that something was /// plugged in but not what exactly. In this situation the ports for all those devices share /// their availability status, and PulseAudio can’t tell which one is actually plugged in, and /// some application may ask the user what was plugged in. Such applications should get a list /// of all card ports and compare their `availability_group` fields. Ports that have the same /// group are those that need input from the user to determine which device was plugged in. The /// application should then activate the user-chosen port. /// /// May be `None`, in which case the port is not part of any availability group (which is the /// same as having a group with only one member). /// /// The group identifier must be treated as an opaque identifier. The string may look like an /// ALSA control name, but applications must not assume any such relationship. The group naming /// scheme can change without a warning. /// /// Since one group can include both input and output ports, the grouping should be done using /// `CardPortInfo` instead of `SinkPortInfo`, but this field is duplicated also in /// `SinkPortInfo` (and `SourcePortInfo`) in case someone finds that convenient. #[cfg(any(doc, feature = "pa_v14"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))] pub availability_group: Option>, /// Port device type. #[cfg(any(doc, feature = "pa_v14"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))] pub r#type: DevicePortType, } impl<'a> SinkPortInfo<'a> { fn new_from_raw(p: *const SinkPortInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; unsafe { SinkPortInfo { name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, description: match src.description.is_null() { false => Some(CStr::from_ptr(src.description).to_string_lossy()), true => None, }, priority: src.priority, available: def::PortAvailable::from_i32(src.available).unwrap(), #[cfg(any(doc, feature = "pa_v14"))] availability_group: match src.availability_group.is_null() { false => Some(CStr::from_ptr(src.availability_group).to_string_lossy()), true => None, }, #[cfg(any(doc, feature = "pa_v14"))] r#type: DevicePortType::from_u32(src.r#type).unwrap(), } } } } /// Stores information about sinks. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct SinkInfo<'a> { /// Name of the sink. pub name: Option>, /// Index of the sink. pub index: u32, /// Description of this sink. pub description: Option>, /// Sample spec of this sink. pub sample_spec: sample::Spec, /// Channel map. pub channel_map: channelmap::Map, /// Index of the owning module of this sink, or `None` if is invalid. pub owner_module: Option, /// Volume of the sink. pub volume: ChannelVolumes, /// Mute switch of the sink. pub mute: bool, /// Index of the monitor source connected to this sink. pub monitor_source: u32, /// The name of the monitor source. pub monitor_source_name: Option>, /// Length of queued audio in the output buffer. pub latency: MicroSeconds, /// Driver name. pub driver: Option>, /// Flags. pub flags: def::SinkFlagSet, /// Property list. pub proplist: Proplist, /// The latency this device has been configured to. pub configured_latency: MicroSeconds, /// Some kind of “base” volume that refers to unamplified/unattenuated volume in the context of /// the output device. pub base_volume: Volume, /// State. pub state: def::SinkState, /// Number of volume steps for sinks which do not support arbitrary volumes. pub n_volume_steps: u32, /// Card index, or `None` if invalid. pub card: Option, /// Set of available ports. pub ports: Vec>, /// Pointer to active port in the set, or `None`. pub active_port: Option>>, /// Set of formats supported by the sink. pub formats: Vec, } impl<'a> SinkInfo<'a> { fn new_from_raw(p: *const SinkInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; let mut port_vec = Vec::with_capacity(src.n_ports as usize); assert!(src.n_ports == 0 || !src.ports.is_null()); for i in 0..src.n_ports as isize { let indexed_ptr = unsafe { (*src.ports.offset(i)) as *mut SinkPortInfoInternal }; if !indexed_ptr.is_null() { port_vec.push(SinkPortInfo::new_from_raw(indexed_ptr)); } } let mut formats_vec = Vec::with_capacity(src.n_formats as usize); assert!(src.n_formats == 0 || !src.formats.is_null()); for i in 0..src.n_formats as isize { let indexed_ptr = unsafe { (*src.formats.offset(i)) as *mut format::InfoInternal }; if !indexed_ptr.is_null() { formats_vec.push(format::Info::from_raw_weak(indexed_ptr)); } } unsafe { SinkInfo { name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, index: src.index, description: match src.description.is_null() { false => Some(CStr::from_ptr(src.description).to_string_lossy()), true => None, }, sample_spec: src.sample_spec.into(), channel_map: src.channel_map.into(), owner_module: match src.owner_module { def::INVALID_INDEX => None, i => Some(i), }, volume: src.volume.into(), mute: match src.mute { 0 => false, _ => true, }, monitor_source: src.monitor_source, monitor_source_name: match src.monitor_source_name.is_null() { false => Some(CStr::from_ptr(src.monitor_source_name).to_string_lossy()), true => None, }, latency: MicroSeconds(src.latency), driver: match src.driver.is_null() { false => Some(CStr::from_ptr(src.driver).to_string_lossy()), true => None, }, flags: def::SinkFlagSet::from_bits_truncate(src.flags), proplist: Proplist::from_raw_weak(src.proplist), configured_latency: MicroSeconds(src.configured_latency), base_volume: Volume(src.base_volume), state: src.state.into(), n_volume_steps: src.n_volume_steps, card: match src.card { def::INVALID_INDEX => None, i => Some(i), }, ports: port_vec, active_port: match src.active_port.is_null() { true => None, false => Some(Box::new(SinkPortInfo::new_from_raw(src.active_port))), }, formats: formats_vec, } } } } impl Introspector { /// Gets information about a sink by its name. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_sink_info_by_name(&self, name: &str, callback: F) -> Operation)> where F: FnMut(ListResult<&SinkInfo>) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_sink_info_by_name(self.context, c_name.as_ptr(), Some(get_sink_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets information about a sink by its index. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_sink_info_by_index(&self, index: u32, callback: F) -> Operation)> where F: FnMut(ListResult<&SinkInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_sink_info_by_index(self.context, index, Some(get_sink_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets the complete sink list. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_sink_info_list(&self, callback: F) -> Operation)> where F: FnMut(ListResult<&SinkInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_sink_info_list(self.context, Some(get_sink_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Sets the volume of a sink device specified by its index. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_sink_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_sink_volume_by_index(self.context, index, volume.as_ref(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the volume of a sink device specified by its name. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_sink_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_sink_volume_by_name(self.context, c_name.as_ptr(), volume.as_ref(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the mute switch of a sink device specified by its index. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_sink_mute_by_index(&mut self, index: u32, mute: bool, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_sink_mute_by_index(self.context, index, mute as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the mute switch of a sink device specified by its name. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_sink_mute_by_name(&mut self, name: &str, mute: bool, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_sink_mute_by_name(self.context, c_name.as_ptr(), mute as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Suspends/Resumes a sink. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn suspend_sink_by_name(&mut self, sink_name: &str, suspend: bool, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(sink_name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_suspend_sink_by_name(self.context, c_name.as_ptr(), suspend as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Suspends/Resumes a sink. /// /// If `index` is [`def::INVALID_INDEX`] all sinks will be suspended. /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn suspend_sink_by_index(&mut self, index: u32, suspend: bool, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_suspend_sink_by_index(self.context, index, suspend as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Changes the profile of a sink. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_sink_port_by_index(&mut self, index: u32, port: &str, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_port = CString::new(port.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_sink_port_by_index(self.context, index, c_port.as_ptr(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Changes the profile of a sink. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_sink_port_by_name(&mut self, name: &str, port: &str, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let c_port = CString::new(port.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_sink_port_by_name(self.context, c_name.as_ptr(), c_port.as_ptr(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } /// Proxy for get sink info list callbacks. /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn get_sink_info_list_cb_proxy(_: *mut ContextInternal, i: *const SinkInfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, SinkInfo::new_from_raw); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Source info //////////////////////////////////////////////////////////////////////////////////////////////////// /// Stores information about a specific port of a source. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct SourcePortInfo<'a> { /// Name of this port. pub name: Option>, /// Description of this port. pub description: Option>, /// The higher this value is, the more useful this port is as a default. pub priority: u32, /// A flag indicating availability status of this port. pub available: def::PortAvailable, /// An indentifier for the group of ports that share their availability status with each other. /// /// This is meant especially for handling cases where one 3.5 mm connector is used for /// headphones, headsets and microphones, and the hardware can only tell that something was /// plugged in but not what exactly. In this situation the ports for all those devices share /// their availability status, and PulseAudio can’t tell which one is actually plugged in, and /// some application may ask the user what was plugged in. Such applications should get a list /// of all card ports and compare their `availability_group` fields. Ports that have the same /// group are those that need input from the user to determine which device was plugged in. The /// application should then activate the user-chosen port. /// /// May be `None`, in which case the port is not part of any availability group (which is the /// same as having a group with only one member). /// /// The group identifier must be treated as an opaque identifier. The string may look like an /// ALSA control name, but applications must not assume any such relationship. The group naming /// scheme can change without a warning. /// /// Since one group can include both input and output ports, the grouping should be done using /// `CardPortInfo` instead of `SourcePortInfo`, but this field is duplicated also in /// `SourcePortInfo` (and `SinkPortInfo`) in case someone finds that convenient. #[cfg(any(doc, feature = "pa_v14"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))] pub availability_group: Option>, /// Port device type. #[cfg(any(doc, feature = "pa_v14"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))] pub r#type: DevicePortType, } impl<'a> SourcePortInfo<'a> { fn new_from_raw(p: *const SourcePortInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; unsafe { SourcePortInfo { name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, description: match src.description.is_null() { false => Some(CStr::from_ptr(src.description).to_string_lossy()), true => None, }, priority: src.priority, available: def::PortAvailable::from_i32(src.available).unwrap(), #[cfg(any(doc, feature = "pa_v14"))] availability_group: match src.availability_group.is_null() { false => Some(CStr::from_ptr(src.availability_group).to_string_lossy()), true => None, }, #[cfg(any(doc, feature = "pa_v14"))] r#type: DevicePortType::from_u32(src.r#type).unwrap(), } } } } /// Stores information about sources. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct SourceInfo<'a> { /// Name of the source. pub name: Option>, /// Index of the source. pub index: u32, /// Description of this source. pub description: Option>, /// Sample spec of this source. pub sample_spec: sample::Spec, /// Channel map. pub channel_map: channelmap::Map, /// Owning module index, or `None`. pub owner_module: Option, /// Volume of the source. pub volume: ChannelVolumes, /// Mute switch of the sink. pub mute: bool, /// If this is a monitor source, the index of the owning sink, otherwise `None`. pub monitor_of_sink: Option, /// Name of the owning sink, or `None`. pub monitor_of_sink_name: Option>, /// Length of filled record buffer of this source. pub latency: MicroSeconds, /// Driver name. pub driver: Option>, /// Flags. pub flags: def::SourceFlagSet, /// Property list. pub proplist: Proplist, /// The latency this device has been configured to. pub configured_latency: MicroSeconds, /// Some kind of “base” volume that refers to unamplified/unattenuated volume in the context of /// the input device. pub base_volume: Volume, /// State. pub state: def::SourceState, /// Number of volume steps for sources which do not support arbitrary volumes. pub n_volume_steps: u32, /// Card index, or `None`. pub card: Option, /// Set of available ports. pub ports: Vec>, /// Pointer to active port in the set, or `None`. pub active_port: Option>>, /// Set of formats supported by the sink. pub formats: Vec, } impl<'a> SourceInfo<'a> { fn new_from_raw(p: *const SourceInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; let mut port_vec = Vec::with_capacity(src.n_ports as usize); assert!(src.n_ports == 0 || !src.ports.is_null()); for i in 0..src.n_ports as isize { let indexed_ptr = unsafe { (*src.ports.offset(i)) as *mut SourcePortInfoInternal }; if !indexed_ptr.is_null() { port_vec.push(SourcePortInfo::new_from_raw(indexed_ptr)); } } let mut formats_vec = Vec::with_capacity(src.n_formats as usize); assert!(src.n_formats == 0 || !src.formats.is_null()); for i in 0..src.n_formats as isize { let indexed_ptr = unsafe { (*src.formats.offset(i)) as *mut format::InfoInternal }; if !indexed_ptr.is_null() { formats_vec.push(format::Info::from_raw_weak(indexed_ptr)); } } unsafe { SourceInfo { name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, index: src.index, description: match src.description.is_null() { false => Some(CStr::from_ptr(src.description).to_string_lossy()), true => None, }, sample_spec: src.sample_spec.into(), channel_map: src.channel_map.into(), owner_module: match src.owner_module { def::INVALID_INDEX => None, i => Some(i), }, volume: src.volume.into(), mute: match src.mute { 0 => false, _ => true, }, monitor_of_sink: match src.monitor_of_sink { def::INVALID_INDEX => None, i => Some(i), }, monitor_of_sink_name: match src.monitor_of_sink_name.is_null() { false => Some(CStr::from_ptr(src.monitor_of_sink_name).to_string_lossy()), true => None, }, latency: MicroSeconds(src.latency), driver: match src.driver.is_null() { false => Some(CStr::from_ptr(src.driver).to_string_lossy()), true => None, }, flags: def::SourceFlagSet::from_bits_truncate(src.flags), proplist: Proplist::from_raw_weak(src.proplist), configured_latency: MicroSeconds(src.configured_latency), base_volume: Volume(src.base_volume), state: src.state.into(), n_volume_steps: src.n_volume_steps, card: match src.card { def::INVALID_INDEX => None, i => Some(i), }, ports: port_vec, active_port: match src.active_port.is_null() { true => None, false => Some(Box::new(SourcePortInfo::new_from_raw(src.active_port))), }, formats: formats_vec, } } } } impl Introspector { /// Gets information about a source by its name. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_source_info_by_name(&self, name: &str, callback: F) -> Operation)> where F: FnMut(ListResult<&SourceInfo>) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_source_info_by_name(self.context, c_name.as_ptr(), Some(get_source_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets information about a source by its index. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_source_info_by_index(&self, index: u32, callback: F) -> Operation)> where F: FnMut(ListResult<&SourceInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_source_info_by_index(self.context, index, Some(get_source_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets the complete source list. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_source_info_list(&self, callback: F) -> Operation)> where F: FnMut(ListResult<&SourceInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_source_info_list(self.context, Some(get_source_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Sets the volume of a source device specified by its index. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_source_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_source_volume_by_index(self.context, index, volume.as_ref(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the volume of a source device specified by its name. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_source_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_source_volume_by_name(self.context, c_name.as_ptr(), volume.as_ref(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the mute switch of a source device specified by its index. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_source_mute_by_index(&mut self, index: u32, mute: bool, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_source_mute_by_index(self.context, index, mute as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the mute switch of a source device specified by its name. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_source_mute_by_name(&mut self, name: &str, mute: bool, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_source_mute_by_name(self.context, c_name.as_ptr(), mute as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Suspends/Resumes a source. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn suspend_source_by_name(&mut self, name: &str, suspend: bool, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_suspend_source_by_name(self.context, c_name.as_ptr(), suspend as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Suspends/Resumes a source. /// /// If `index` is [`def::INVALID_INDEX`], all sources will be suspended. /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn suspend_source_by_index(&mut self, index: u32, suspend: bool, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_suspend_source_by_index(self.context, index, suspend as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Changes the profile of a source. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_source_port_by_index(&mut self, index: u32, port: &str, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_port = CString::new(port.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_source_port_by_index(self.context, index, c_port.as_ptr(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Changes the profile of a source. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_source_port_by_name(&mut self, name: &str, port: &str, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let c_port = CString::new(port.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_source_port_by_name(self.context, c_name.as_ptr(), c_port.as_ptr(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } /// Proxy for get source info list callbacks. /// /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn get_source_info_list_cb_proxy(_: *mut ContextInternal, i: *const SourceInfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, SourceInfo::new_from_raw); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Server info //////////////////////////////////////////////////////////////////////////////////////////////////// /// Server information. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct ServerInfo<'a> { /// User name of the daemon process. pub user_name: Option>, /// Host name the daemon is running on. pub host_name: Option>, /// Version string of the daemon. pub server_version: Option>, /// Server package name (usually “pulseaudio”). pub server_name: Option>, /// Default sample specification. pub sample_spec: sample::Spec, /// Name of default sink. pub default_sink_name: Option>, /// Name of default source. pub default_source_name: Option>, /// A random cookie for identifying this instance of PulseAudio. pub cookie: u32, /// Default channel map. pub channel_map: channelmap::Map, } impl<'a> ServerInfo<'a> { fn new_from_raw(p: *const ServerInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; unsafe { ServerInfo { user_name: match src.user_name.is_null() { false => Some(CStr::from_ptr(src.user_name).to_string_lossy()), true => None, }, host_name: match src.host_name.is_null() { false => Some(CStr::from_ptr(src.host_name).to_string_lossy()), true => None, }, server_version: match src.server_version.is_null() { false => Some(CStr::from_ptr(src.server_version).to_string_lossy()), true => None, }, server_name: match src.server_name.is_null() { false => Some(CStr::from_ptr(src.server_name).to_string_lossy()), true => None, }, sample_spec: src.sample_spec.into(), default_sink_name: match src.default_sink_name.is_null() { false => Some(CStr::from_ptr(src.default_sink_name).to_string_lossy()), true => None, }, default_source_name: match src.default_source_name.is_null() { false => Some(CStr::from_ptr(src.default_source_name).to_string_lossy()), true => None, }, cookie: src.cookie, channel_map: src.channel_map.into(), } } } } impl Introspector { /// Gets some information about the server. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_server_info(&self, callback: F) -> Operation where F: FnMut(&ServerInfo) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_server_info(self.context, Some(get_server_info_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } /// Proxy for get server info callbacks. /// Warning: This is for single-use cases only! It destroys the actual closure callback. extern "C" fn get_server_info_cb_proxy(_: *mut ContextInternal, i: *const ServerInfoInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { assert!(!i.is_null()); let obj = ServerInfo::new_from_raw(i); // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = get_su_callback::(userdata); (callback)(&obj); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Module info //////////////////////////////////////////////////////////////////////////////////////////////////// /// Stores information about modules. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct ModuleInfo<'a> { /// Index of the module. pub index: u32, /// Name of the module. pub name: Option>, /// Argument string of the module. pub argument: Option>, /// Usage counter or `None` if invalid. pub n_used: Option, /// Property list. pub proplist: Proplist, } impl<'a> ModuleInfo<'a> { fn new_from_raw(p: *const ModuleInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; unsafe { ModuleInfo { index: src.index, name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, argument: match src.argument.is_null() { false => Some(CStr::from_ptr(src.argument).to_string_lossy()), true => None, }, n_used: match src.n_used { def::INVALID_INDEX => None, i => Some(i), }, proplist: Proplist::from_raw_weak(src.proplist), } } } } impl Introspector { /// Gets some information about a module by its index. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_module_info(&self, index: u32, callback: F) -> Operation)> where F: FnMut(ListResult<&ModuleInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_module_info(self.context, index, Some(mod_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets the complete list of currently loaded modules. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_module_info_list(&self, callback: F) -> Operation)> where F: FnMut(ListResult<&ModuleInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_module_info_list(self.context, Some(mod_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Loads a module. /// /// Panics on error, i.e. invalid arguments or state. The callback is provided with the /// index. pub fn load_module(&mut self, name: &str, argument: &str, callback: F) -> Operation where F: FnMut(u32) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let c_arg = CString::new(argument.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_load_module(self.context, c_name.as_ptr(), c_arg.as_ptr(), Some(context_index_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Unloads a module. /// /// Panics on error, i.e. invalid arguments or state. /// /// The callback must accept a `bool`, which indicates success. pub fn unload_module(&mut self, index: u32, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_unload_module(self.context, index, Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } /// Proxy for get module info list callbacks. /// /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn mod_info_list_cb_proxy(_: *mut ContextInternal, i: *const ModuleInfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, ModuleInfo::new_from_raw); }); } /// Proxy for context index callbacks. /// /// Warning: This is for single-use cases only! It destroys the actual closure callback. extern "C" fn context_index_cb_proxy(_: *mut ContextInternal, index: u32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = get_su_callback::(userdata); (callback)(index); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Messages //////////////////////////////////////////////////////////////////////////////////////////////////// impl Introspector { /// Send a message to an object that registered a message handler. /// /// The callback must accept two params, firstly a boolean indicating success if `true`, and /// secondly, the response string. The response string may possibly not be given if /// unsuccessful. /// /// For more information see the [messaging_api.txt] documentation in the PulseAudio repository. /// /// [messaging_api.txt]: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/blob/master/doc/messaging_api.txt #[cfg(any(doc, feature = "pa_v15"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))] pub fn send_message_to_object(&mut self, recipient_name: &str, message: &str, message_parameters: &str, callback: F) -> Operation)> where F: FnMut(bool, Option) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_recipient_name = CString::new(recipient_name.clone()).unwrap(); let c_message = CString::new(message.clone()).unwrap(); let c_message_parameters = CString::new(message_parameters.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_send_message_to_object(self.context, c_recipient_name.as_ptr(), c_message.as_ptr(), c_message_parameters.as_ptr(), Some(send_message_to_object_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } } /// Proxy for send message to object callbacks. /// /// Warning: This is for single-use cases only! It destroys the actual closure callback. #[cfg(any(doc, feature = "pa_v15"))] extern "C" fn send_message_to_object_cb_proxy(_: *mut ContextInternal, success: i32, response: *const c_char, userdata: *mut c_void) { let success_actual = match success { 0 => false, _ => true, }; let _ = std::panic::catch_unwind(|| { let r = match response.is_null() { true => None, false => { let tmp = unsafe { CStr::from_ptr(response) }; Some(tmp.to_string_lossy().into_owned()) }, }; // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = get_su_callback::)>(userdata); (callback)(success_actual, r); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Client info //////////////////////////////////////////////////////////////////////////////////////////////////// /// Stores information about clients. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct ClientInfo<'a> { /// Index of this client. pub index: u32, /// Name of this client. pub name: Option>, /// Index of the owning module, or `None`. pub owner_module: Option, /// Driver name. pub driver: Option>, /// Property list. pub proplist: Proplist, } impl<'a> ClientInfo<'a> { fn new_from_raw(p: *const ClientInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; unsafe { ClientInfo { index: src.index, name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, owner_module: match src.owner_module { def::INVALID_INDEX => None, i => Some(i), }, driver: match src.driver.is_null() { false => Some(CStr::from_ptr(src.driver).to_string_lossy()), true => None, }, proplist: Proplist::from_raw_weak(src.proplist), } } } } impl Introspector { /// Gets information about a client by its index. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_client_info(&self, index: u32, callback: F) -> Operation)> where F: FnMut(ListResult<&ClientInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_client_info(self.context, index, Some(get_client_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets the complete client list. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_client_info_list(&self, callback: F) -> Operation)> where F: FnMut(ListResult<&ClientInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_client_info_list(self.context, Some(get_client_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Kills a client. /// /// Panics on error, i.e. invalid arguments or state. /// /// The callback must accept a `bool`, which indicates success. pub fn kill_client(&mut self, index: u32, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_kill_client(self.context, index, Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } /// Proxy for get sink info list callbacks. /// /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn get_client_info_list_cb_proxy(_: *mut ContextInternal, i: *const ClientInfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, ClientInfo::new_from_raw); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Card info //////////////////////////////////////////////////////////////////////////////////////////////////// /// Backwards compatable alias #[deprecated(since = "2.28.0", note = "Use the name CardProfileInfo instead")] pub type CardProfileInfo2<'a> = CardProfileInfo<'a>; /// Stores information about a specific profile of a card. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct CardProfileInfo<'a> { /// Name of this profile. pub name: Option>, /// Description of this profile. pub description: Option>, /// Number of sinks this profile would create. pub n_sinks: u32, /// Number of sources this profile would create. pub n_sources: u32, /// The higher this value is, the more useful this profile is as a default. pub priority: u32, /// Is this profile available? If this is `false`, meaning “unavailable”, then it makes no sense /// to try to activate this profile. If this is `true`, it’s still not a guarantee that /// activating the profile will result in anything useful, it just means that the server isn’t /// aware of any reason why the profile would definitely be useless. pub available: bool, } impl<'a> CardProfileInfo<'a> { fn new_from_raw(p: *const CardProfileInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; unsafe { CardProfileInfo { name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, description: match src.description.is_null() { false => Some(CStr::from_ptr(src.description).to_string_lossy()), true => None, }, n_sinks: src.n_sinks, n_sources: src.n_sources, priority: src.priority, available: match src.available { 0 => false, _ => true, }, } } } } /// Stores information about a specific port of a card. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct CardPortInfo<'a> { /// Name of this port. pub name: Option>, /// Description of this port. pub description: Option>, /// The higher this value is, the more useful this port is as a default. pub priority: u32, /// Availability status of this port. pub available: def::PortAvailable, /// The direction of this port. pub direction: direction::FlagSet, /// Property list. pub proplist: Proplist, /// Latency offset of the port that gets added to the sink/source latency when the port is /// active. pub latency_offset: i64, /// Set of available profiles. pub profiles: Vec>, /// An indentifier for the group of ports that share their availability status with each other. /// /// This is meant especially for handling cases where one 3.5 mm connector is used for /// headphones, headsets and microphones, and the hardware can only tell that something was /// plugged in but not what exactly. In this situation the ports for all those devices share /// their availability status, and PulseAudio can’t tell which one is actually plugged in, and /// some application may ask the user what was plugged in. Such applications should get a list /// of all card ports and compare their `availability_group` fields. Ports that have the same /// group are those that need input from the user to determine which device was plugged in. The /// application should then activate the user-chosen port. /// /// May be `None`, in which case the port is not part of any availability group (which is the /// same as having a group with only one member). /// /// The group identifier must be treated as an opaque identifier. The string may look like an /// ALSA control name, but applications must not assume any such relationship. The group naming /// scheme can change without a warning. #[cfg(any(doc, feature = "pa_v14"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))] pub availability_group: Option>, /// Port device type. #[cfg(any(doc, feature = "pa_v14"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))] pub r#type: DevicePortType, } impl<'a> CardPortInfo<'a> { fn new_from_raw(p: *const CardPortInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; let mut profiles_vec = Vec::with_capacity(src.n_profiles as usize); assert!(src.n_profiles == 0 || !src.profiles2.is_null()); for i in 0..src.n_profiles as isize { let indexed_ptr = unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfoInternal }; if !indexed_ptr.is_null() { profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr)); } } unsafe { CardPortInfo { name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, description: match src.description.is_null() { false => Some(CStr::from_ptr(src.description).to_string_lossy()), true => None, }, priority: src.priority, available: def::PortAvailable::from_i32(src.available).unwrap(), direction: direction::FlagSet::from_bits_truncate(src.direction), proplist: Proplist::from_raw_weak(src.proplist), latency_offset: src.latency_offset, profiles: profiles_vec, #[cfg(any(doc, feature = "pa_v14"))] availability_group: match src.availability_group.is_null() { false => Some(CStr::from_ptr(src.availability_group).to_string_lossy()), true => None, }, #[cfg(any(doc, feature = "pa_v14"))] r#type: DevicePortType::from_u32(src.r#type).unwrap(), } } } } /// Stores information about cards. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct CardInfo<'a> { /// Index of this card. pub index: u32, /// Name of this card. pub name: Option>, /// Index of the owning module, or `None`. pub owner_module: Option, /// Driver name. pub driver: Option>, /// Property list. pub proplist: Proplist, /// Set of ports. pub ports: Vec>, /// Set of available profiles. pub profiles: Vec>, /// Pointer to active profile in the set, or `None`. pub active_profile: Option>>, } impl<'a> CardInfo<'a> { fn new_from_raw(p: *const CardInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; let mut ports_vec = Vec::with_capacity(src.n_ports as usize); assert!(src.n_ports == 0 || !src.ports.is_null()); for i in 0..src.n_ports as isize { let indexed_ptr = unsafe { (*src.ports.offset(i)) as *mut CardPortInfoInternal }; if !indexed_ptr.is_null() { ports_vec.push(CardPortInfo::new_from_raw(indexed_ptr)); } } let mut profiles_vec = Vec::with_capacity(src.n_profiles as usize); assert!(src.n_profiles == 0 || !src.profiles2.is_null()); for i in 0..src.n_profiles as isize { let indexed_ptr = unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfoInternal }; if !indexed_ptr.is_null() { profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr)); } } unsafe { CardInfo { index: src.index, name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, owner_module: match src.owner_module { def::INVALID_INDEX => None, i => Some(i), }, driver: match src.driver.is_null() { false => Some(CStr::from_ptr(src.driver).to_string_lossy()), true => None, }, proplist: Proplist::from_raw_weak(src.proplist), ports: ports_vec, profiles: profiles_vec, active_profile: match src.active_profile2.is_null() { true => None, false => Some(Box::new(CardProfileInfo::new_from_raw(src.active_profile2))), }, } } } } impl Introspector { /// Gets information about a card by its index. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_card_info_by_index(&self, index: u32, callback: F) -> Operation)> where F: FnMut(ListResult<&CardInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_card_info_by_index(self.context, index, Some(get_card_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets information about a card by its name. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_card_info_by_name(&self, name: &str, callback: F) -> Operation)> where F: FnMut(ListResult<&CardInfo>) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_card_info_by_name(self.context, c_name.as_ptr(), Some(get_card_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets the complete card list. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_card_info_list(&self, callback: F) -> Operation)> where F: FnMut(ListResult<&CardInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_card_info_list(self.context, Some(get_card_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Changes the profile of a card. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_card_profile_by_index(&mut self, index: u32, profile: &str, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_profile = CString::new(profile.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_card_profile_by_index(self.context, index, c_profile.as_ptr(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Changes the profile of a card. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_card_profile_by_name(&mut self, name: &str, profile: &str, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let c_profile = CString::new(profile.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_card_profile_by_name(self.context, c_name.as_ptr(), c_profile.as_ptr(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the latency offset of a port. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_port_latency_offset(&mut self, card_name: &str, port_name: &str, offset: i64, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(card_name.clone()).unwrap(); let c_port = CString::new(port_name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_port_latency_offset(self.context, c_name.as_ptr(), c_port.as_ptr(), offset, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } /// Proxy for get card info list callbacks. /// /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn get_card_info_list_cb_proxy(_: *mut ContextInternal, i: *const CardInfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, CardInfo::new_from_raw); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Sink input info //////////////////////////////////////////////////////////////////////////////////////////////////// /// Stores information about sink inputs. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct SinkInputInfo<'a> { /// Index of the sink input. pub index: u32, /// Name of the sink input. pub name: Option>, /// Index of the module this sink input belongs to, or `None` when it does not belong to any /// module. pub owner_module: Option, /// Index of the client this sink input belongs to, or invalid when it does not belong to any /// client. pub client: Option, /// Index of the connected sink. pub sink: u32, /// The sample specification of the sink input. pub sample_spec: sample::Spec, /// Channel map. pub channel_map: channelmap::Map, /// The volume of this sink input. pub volume: ChannelVolumes, /// Latency due to buffering in sink input, see [`TimingInfo`](crate::def::TimingInfo) for /// details. pub buffer_usec: MicroSeconds, /// Latency of the sink device, see [`TimingInfo`](crate::def::TimingInfo) for details. pub sink_usec: MicroSeconds, /// The resampling method used by this sink input. pub resample_method: Option>, /// Driver name. pub driver: Option>, /// Stream muted. pub mute: bool, /// Property list. pub proplist: Proplist, /// Stream corked. pub corked: bool, /// Stream has volume. If not set, then the meaning of this struct’s volume member is /// unspecified. pub has_volume: bool, /// The volume can be set. If not set, the volume can still change even though clients can’t /// control the volume. pub volume_writable: bool, /// Stream format information. pub format: format::Info, } impl<'a> SinkInputInfo<'a> { fn new_from_raw(p: *const SinkInputInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; unsafe { SinkInputInfo { index: src.index, name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, owner_module: match src.owner_module { def::INVALID_INDEX => None, i => Some(i), }, client: match src.client { def::INVALID_INDEX => None, i => Some(i), }, sink: src.sink, sample_spec: src.sample_spec.into(), channel_map: src.channel_map.into(), volume: src.volume.into(), buffer_usec: MicroSeconds(src.buffer_usec), sink_usec: MicroSeconds(src.sink_usec), resample_method: match src.resample_method.is_null() { false => Some(CStr::from_ptr(src.resample_method).to_string_lossy()), true => None, }, driver: match src.driver.is_null() { false => Some(CStr::from_ptr(src.driver).to_string_lossy()), true => None, }, mute: match src.mute { 0 => false, _ => true, }, proplist: Proplist::from_raw_weak(src.proplist), corked: match src.corked { 0 => false, _ => true, }, has_volume: match src.has_volume { 0 => false, _ => true, }, volume_writable: match src.volume_writable { 0 => false, _ => true, }, format: format::Info::from_raw_weak(src.format as *mut format::InfoInternal), } } } } impl Introspector { /// Gets some information about a sink input by its index. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_sink_input_info(&self, index: u32, callback: F) -> Operation)> where F: FnMut(ListResult<&SinkInputInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_sink_input_info(self.context, index, Some(get_sink_input_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets the complete sink input list. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_sink_input_info_list(&self, callback: F) -> Operation)> where F: FnMut(ListResult<&SinkInputInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_sink_input_info_list(self.context, Some(get_sink_input_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Moves the specified sink input to a different sink. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn move_sink_input_by_name(&mut self, index: u32, sink_name: &str, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(sink_name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_move_sink_input_by_name(self.context, index, c_name.as_ptr(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Moves the specified sink input to a different sink. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn move_sink_input_by_index(&mut self, index: u32, sink_index: u32, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_move_sink_input_by_index(self.context, index, sink_index, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the volume of a sink input stream. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_sink_input_volume(&mut self, index: u32, volume: &ChannelVolumes, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_sink_input_volume(self.context, index, volume.as_ref(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the mute switch of a sink input stream. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_sink_input_mute(&mut self, index: u32, mute: bool, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_sink_input_mute(self.context, index, mute as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Kills a sink input. /// /// Panics on error, i.e. invalid arguments or state. /// /// The callback must accept a `bool`, which indicates success. pub fn kill_sink_input(&mut self, index: u32, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_kill_sink_input(self.context, index, Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } /// Proxy for get sink input info list callbacks. /// /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn get_sink_input_info_list_cb_proxy(_: *mut ContextInternal, i: *const SinkInputInfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, SinkInputInfo::new_from_raw); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Source output info //////////////////////////////////////////////////////////////////////////////////////////////////// /// Stores information about source outputs. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct SourceOutputInfo<'a> { /// Index of the source output. pub index: u32, /// Name of the source output. pub name: Option>, /// Index of the module this source output belongs to, or `None` when it does not belong to any /// module. pub owner_module: Option, /// Index of the client this source output belongs to, or `None` when it does not belong to any /// client. pub client: Option, /// Index of the connected source. pub source: u32, /// The sample specification of the source output. pub sample_spec: sample::Spec, /// Channel map. pub channel_map: channelmap::Map, /// Latency due to buffering in the source output, see [`TimingInfo`](crate::def::TimingInfo) /// for details. pub buffer_usec: MicroSeconds, /// Latency of the source device, see [`TimingInfo`](crate::def::TimingInfo) for details. pub source_usec: MicroSeconds, /// The resampling method used by this source output. pub resample_method: Option>, /// Driver name. pub driver: Option>, /// Property list. pub proplist: Proplist, /// Stream corked. pub corked: bool, /// The volume of this source output. pub volume: ChannelVolumes, /// Stream muted. pub mute: bool, /// Stream has volume. If not set, then the meaning of this struct’s volume member is /// unspecified. pub has_volume: bool, /// The volume can be set. If not set, the volume can still change even though clients can’t /// control the volume. pub volume_writable: bool, /// Stream format information. pub format: format::Info, } impl<'a> SourceOutputInfo<'a> { fn new_from_raw(p: *const SourceOutputInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; unsafe { SourceOutputInfo { index: src.index, name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, owner_module: match src.owner_module { def::INVALID_INDEX => None, i => Some(i), }, client: match src.client { def::INVALID_INDEX => None, i => Some(i), }, source: src.source, sample_spec: src.sample_spec.into(), channel_map: src.channel_map.into(), buffer_usec: MicroSeconds(src.buffer_usec), source_usec: MicroSeconds(src.source_usec), resample_method: match src.resample_method.is_null() { false => Some(CStr::from_ptr(src.resample_method).to_string_lossy()), true => None, }, driver: match src.driver.is_null() { false => Some(CStr::from_ptr(src.driver).to_string_lossy()), true => None, }, proplist: Proplist::from_raw_weak(src.proplist), corked: match src.corked { 0 => false, _ => true, }, volume: src.volume.into(), mute: match src.mute { 0 => false, _ => true, }, has_volume: match src.has_volume { 0 => false, _ => true, }, volume_writable: match src.volume_writable { 0 => false, _ => true, }, format: format::Info::from_raw_weak(src.format as *mut format::InfoInternal), } } } } impl Introspector { /// Gets information about a source output by its index. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_source_output_info(&self, index: u32, callback: F) -> Operation)> where F: FnMut(ListResult<&SourceOutputInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_source_output_info(self.context, index, Some(get_source_output_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets the complete list of source outputs. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_source_output_info_list(&self, callback: F) -> Operation)> where F: FnMut(ListResult<&SourceOutputInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_source_output_info_list(self.context, Some(get_source_output_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Moves the specified source output to a different source. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn move_source_output_by_name(&mut self, index: u32, source_name: &str, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(source_name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_move_source_output_by_name(self.context, index, c_name.as_ptr(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Moves the specified source output to a different source. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn move_source_output_by_index(&mut self, index: u32, source_index: u32, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_move_source_output_by_index(self.context, index, source_index, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the volume of a source output stream. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_source_output_volume(&mut self, index: u32, volume: &ChannelVolumes, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_source_output_volume(self.context, index, volume.as_ref(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the mute switch of a source output stream. /// /// Panics on error, i.e. invalid arguments or state. /// /// The optional callback must accept a `bool`, which indicates success. pub fn set_source_output_mute(&mut self, index: u32, mute: bool, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_set_source_output_mute(self.context, index, mute as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Kills a source output. /// /// Panics on error, i.e. invalid arguments or state. /// /// The callback must accept a `bool`, which indicates success. pub fn kill_source_output(&mut self, index: u32, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_kill_source_output(self.context, index, Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } /// Proxy for get source output info list callbacks. /// /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn get_source_output_info_list_cb_proxy(_: *mut ContextInternal, i: *const SourceOutputInfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, SourceOutputInfo::new_from_raw); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Stat info //////////////////////////////////////////////////////////////////////////////////////////////////// impl Introspector { /// Gets daemon memory block statistics. /// /// Panics on error, i.e. invalid arguments or state. pub fn stat(&self, callback: F) -> Operation where F: FnMut(&StatInfo) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_stat(self.context, Some(get_stat_info_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } } /// Proxy for get stat info callbacks. /// /// Warning: This is for single-use cases only! It destroys the actual closure callback. extern "C" fn get_stat_info_cb_proxy(_: *mut ContextInternal, i: *const StatInfo, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { assert!(!i.is_null()); // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = get_su_callback::(userdata); (callback)(unsafe { &*i }); }); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Sample info //////////////////////////////////////////////////////////////////////////////////////////////////// /// Stores information about sample cache entries. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. #[derive(Debug)] pub struct SampleInfo<'a> { /// Index of this entry. pub index: u32, /// Name of this entry. pub name: Option>, /// Default volume of this entry. pub volume: ChannelVolumes, /// Sample specification of the sample. pub sample_spec: sample::Spec, /// The channel map. pub channel_map: channelmap::Map, /// Duration of this entry. pub duration: MicroSeconds, /// Length of this sample in bytes. pub bytes: u32, /// Non-zero when this is a lazy cache entry. pub lazy: bool, /// In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. pub filename: Option>, /// Property list for this sample. pub proplist: Proplist, } impl<'a> SampleInfo<'a> { fn new_from_raw(p: *const SampleInfoInternal) -> Self { assert!(!p.is_null()); let src = unsafe { &*p }; unsafe { SampleInfo { index: src.index, name: match src.name.is_null() { false => Some(CStr::from_ptr(src.name).to_string_lossy()), true => None, }, volume: src.volume.into(), sample_spec: src.sample_spec.into(), channel_map: src.channel_map.into(), duration: MicroSeconds(src.duration), bytes: src.bytes, lazy: match src.lazy { 0 => false, _ => true, }, filename: match src.filename.is_null() { false => Some(CStr::from_ptr(src.filename).to_string_lossy()), true => None, }, proplist: Proplist::from_raw_weak(src.proplist), } } } } impl Introspector { /// Gets information about a sample by its name. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_sample_info_by_name(&self, name: &str, callback: F) -> Operation)> where F: FnMut(ListResult<&SampleInfo>) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_sample_info_by_name(self.context, c_name.as_ptr(), Some(get_sample_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets information about a sample by its index. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_sample_info_by_index(&self, index: u32, callback: F) -> Operation)> where F: FnMut(ListResult<&SampleInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_sample_info_by_index(self.context, index, Some(get_sample_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } /// Gets the complete list of samples stored in the daemon. /// /// Panics on error, i.e. invalid arguments or state. pub fn get_sample_info_list(&self, callback: F) -> Operation)> where F: FnMut(ListResult<&SampleInfo>) + 'static { let cb_data = box_closure_get_capi_ptr::)>(Box::new(callback)); let ptr = unsafe { capi::pa_context_get_sample_info_list(self.context, Some(get_sample_info_list_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } } /// Proxy for get sample info list callbacks. /// /// Warning: This is for list cases only! On EOL it destroys the actual closure callback. extern "C" fn get_sample_info_list_cb_proxy(_: *mut ContextInternal, i: *const SampleInfoInternal, eol: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { callback_for_list_instance(i, eol, userdata, SampleInfo::new_from_raw); }); } libpulse-binding-2.28.1/src/context/mod.rs000064400000000000000000000754251046102023000165620ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Connection contexts for asynchronous communication with a server. //! //! A `Context` object wraps a connection to a PulseAudio server using its native protocol. //! //! # Overview //! //! A context is the basic object for a connection to a PulseAudio server. It multiplexes commands, //! data streams and events through a single channel. //! //! There is no need for more than one context per application, unless connections to multiple //! servers are needed. //! //! # Operations //! //! All operations on the context are performed asynchronously. I.e. the client will not wait for //! the server to complete the request. To keep track of all these in-flight operations, the //! application is given an [`Operation`] object for each asynchronous operation. //! //! There are only two actions (besides reference counting) that can be performed on an //! [`Operation`]: querying its state with [`Operation::get_state()`] and aborting it with //! [`Operation::cancel()`]. //! //! An [`Operation`] object is reference counted, so an application must make sure to unreference //! it, even if it has no intention of using it. This however is taken care of automatically in this //! Rust binding via the implementation of the `Drop` trait on the object. //! //! # Connecting //! //! A context must be connected to a server before any operation can be issued. Calling //! [`Context::connect()`] will initiate the connection procedure. Unlike most asynchronous //! operations, connecting does not result in an [`Operation`] object. Instead, the application //! should register a callback using [`Context::set_state_callback()`]. //! //! # Disconnecting //! //! When the sound support is no longer needed, the connection needs to be closed using //! [`Context::disconnect()`]. This is an immediate function that works synchronously. //! //! Since the context object has references to other objects it must be disconnected after use or //! there is a high risk of memory leaks. If the connection has terminated by itself, then there is //! no need to explicitly disconnect the context using [`Context::disconnect()`]. //! //! # Functions //! //! The sound server’s functionality can be divided into a number of subsections: //! //! * [`stream`](mod@crate::stream) //! * [`context::scache`](mod@crate::context::scache) //! * [`context::introspect`](mod@crate::context::introspect) //! * [`context::subscribe`](mod@crate::context::subscribe) pub mod ext_device_manager; pub mod ext_device_restore; pub mod ext_stream_restore; pub mod introspect; pub mod scache; pub mod subscribe; use std::os::raw::{c_char, c_void}; use std::ffi::{CStr, CString}; use std::ptr::{null, null_mut}; use std::rc::Rc; use bitflags::bitflags; use num_derive::{FromPrimitive, ToPrimitive}; use crate::{def, sample}; use crate::mainloop::api::{Mainloop, MainloopInnerType}; use crate::mainloop::events; use crate::mainloop::events::timer::{TimeEvent, TimeEventRef}; use crate::operation::Operation; use crate::error::PAErr; use crate::time::MonotonicTs; use crate::proplist::{self, Proplist, ProplistInternal}; use crate::callbacks::{box_closure_get_capi_ptr, get_su_callback, MultiUseCallback}; use crate::capi::pa_context as ContextInternal; /// An opaque connection context to a daemon. /// /// Note: Saves a copy of active multi-use closure callbacks, which it frees on drop. pub struct Context { /// The actual C object. pub(crate) ptr: *mut ContextInternal, /// Used to avoid freeing the internal object when used as a weak wrapper in callbacks. weak: bool, /// Multi-use callback closure pointers. cb_ptrs: CallbackPointers, } unsafe impl Send for Context {} unsafe impl Sync for Context {} /// Holds copies of callback closure pointers, for those that are “multi-use” (may be fired multiple /// times), for freeing at the appropriate time. #[derive(Default)] struct CallbackPointers { set_state: NotifyCb, subscribe: self::subscribe::Callback, event: EventCb, } type NotifyCb = MultiUseCallback; type EventCb = MultiUseCallback; type ExtSubscribeCb = MultiUseCallback; /// The state of a connection context. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] pub enum State { /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate (C API) equivalent */ /// The context hasn’t been connected yet. Unconnected, /// A connection is being established. Connecting, /// The client is authorizing itself to the daemon. Authorizing, /// The client is passing its application name to the daemon. SettingName, /// The connection is established, the context is ready to execute operations. Ready, /// The connection failed or was disconnected. Failed, /// The connection was terminated cleanly. Terminated, } /// Test size is equal to `sys` equivalent #[test] fn state_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); // Check order and value of variants match // No point checking conversions in both directions since both are a transmute assert_eq!(State::Unconnected, State::from(capi::pa_context_state_t::Unconnected)); assert_eq!(State::Connecting, State::from(capi::pa_context_state_t::Connecting)); assert_eq!(State::Authorizing, State::from(capi::pa_context_state_t::Authorizing)); assert_eq!(State::SettingName, State::from(capi::pa_context_state_t::SettingName)); assert_eq!(State::Ready, State::from(capi::pa_context_state_t::Ready)); assert_eq!(State::Failed, State::from(capi::pa_context_state_t::Failed)); assert_eq!(State::Terminated, State::from(capi::pa_context_state_t::Terminated)); } impl From for capi::pa_context_state_t { #[inline] fn from(s: State) -> Self { unsafe { std::mem::transmute(s) } } } impl From for State { #[inline] fn from(s: capi::pa_context_state_t) -> Self { unsafe { std::mem::transmute(s) } } } impl State { /// Checks if the passed state is one of the connected states (returns `true` if so). pub fn is_good(self) -> bool { self == State::Connecting || self == State::Authorizing || self == State::SettingName || self == State::Ready } } bitflags! { /// Context flag set. #[repr(transparent)] pub struct FlagSet: u32 { /// No flags set. const NOFLAGS = capi::PA_CONTEXT_NOFLAGS; /// Disable autospawning of the PulseAudio daemon if required. const NOAUTOSPAWN = capi::PA_CONTEXT_NOAUTOSPAWN; /// Don’t fail if the daemon is not available when [`Context::connect()`] is called, instead /// enter [`State::Connecting`] state and wait for the daemon to appear. const NOFAIL = capi::PA_CONTEXT_NOFAIL; } } /// Some special flags for contexts. #[deprecated(since = "2.21.0", note = "Use the associated constants on `FlagSet`.")] pub mod flags { use super::FlagSet; /// No flags set. pub const NOFLAGS: FlagSet = FlagSet::NOFLAGS; /// Disable autospawning of the PulseAudio daemon if required. pub const NOAUTOSPAWN: FlagSet = FlagSet::NOAUTOSPAWN; /// Don’t fail if the daemon is not available when [`Context::connect()`] is called, instead /// enter [`State::Connecting`] state and wait for the daemon to appear. /// /// [`Context::connect()`]: super::Context::connect /// [`State::Connecting`]: super::State::Connecting pub const NOFAIL: FlagSet = FlagSet::NOFAIL; } impl Context { /// Instantiates a new connection context with an abstract mainloop API and an application name. /// /// It is recommended to use [`new_with_proplist()`](Self::new_with_proplist) instead and /// specify some initial properties. /// /// Note, this will fail either should the underlying C API call return a null pointer for some /// reason, or if the version of the PulseAudio client system library at runtime is found to be /// older than the minimum version set via this crate’s feature flags (as a means to help /// prevent “forward” compatibility problems, as discussed in the project `COMPATIBILITY.md` /// documentation). pub fn new(mainloop: &impl Mainloop, name: &str) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let ptr = unsafe { capi::pa_context_new(mainloop.inner().get_api().as_ref(), c_name.as_ptr()) }; Self::create(ptr) } /// Instantiates a new connection context with an abstract mainloop API and an application name, /// and specify the initial client property list. /// /// Note, this will fail either should the underlying C API call return a null pointer for some /// reason, or if the version of the PulseAudio client system library at runtime is found to be /// older than the minimum version set via this crate’s feature flags (as a means to help /// prevent “forward” compatibility problems, as discussed in the project `COMPATIBILITY.md` /// documentation). pub fn new_with_proplist(mainloop: &impl Mainloop, name: &str, proplist: &Proplist) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let ptr = unsafe { capi::pa_context_new_with_proplist(mainloop.inner().get_api().as_ref(), c_name.as_ptr(), proplist.0.ptr) }; Self::create(ptr) } /// Internal common creation function fn create(ptr: *mut ContextInternal) -> Option { // Block creation if runtime client system library is too old, to block the potential // “forward” compatibility problems discussed in the project `COMPATIBILITY.md` // documentation. if crate::version::library_version_is_too_old() != Ok(false) { return None; } match ptr.is_null() { false => Some(Self::from_raw(ptr)), true => None, } } /// Creates a new `Context` from an existing [`ContextInternal`] pointer. #[inline] pub(crate) fn from_raw(ptr: *mut ContextInternal) -> Self { assert_eq!(false, ptr.is_null()); Self { ptr: ptr, weak: false, cb_ptrs: Default::default() } } /// Sets a callback function that is called whenever the context status changes. pub fn set_state_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.set_state; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy_multi); unsafe { capi::pa_context_set_state_callback(self.ptr, cb_fn, cb_data); } } /// Sets a callback function that is called whenever a meta/policy control event is received. /// /// The callback is given a name which represents what event occurred. The set of defined events /// can be extended at any time. Also, server modules may introduce additional message types so /// make sure that your callback function ignores messages it doesn’t know. It is also given an /// (owned) property list. pub fn set_event_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.event; *saved = EventCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(event_cb_proxy); unsafe { capi::pa_context_set_event_callback(self.ptr, cb_fn, cb_data); } } /// Gets the error number of the last failed operation. #[inline] pub fn errno(&self) -> PAErr { PAErr(unsafe { capi::pa_context_errno(self.ptr) }) } /// Checks if some data is pending to be written to the connection (returns `true` if so). #[inline] pub fn is_pending(&self) -> bool { unsafe { capi::pa_context_is_pending(self.ptr) != 0 } } /// Gets the current context status. #[inline] pub fn get_state(&self) -> State { unsafe { capi::pa_context_get_state(self.ptr).into() } } /// Connects the context to the specified server. /// /// If server is `None`, connect to the default server. This routine may but will not always /// return synchronously on error. Use [`set_state_callback()`] to be notified when the /// connection is established. If `flags` doesn’t have [`FlagSet::NOAUTOSPAWN`] set and no /// specific server is specified or accessible, a new daemon is spawned. If `api` is not `None`, /// the functions specified in the structure are used when forking a new child process. /// /// [`set_state_callback()`]: Self::set_state_callback pub fn connect(&mut self, server: Option<&str>, flags: FlagSet, api: Option<&def::SpawnApi>) -> Result<(), PAErr> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_server = match server { Some(server) => CString::new(server.clone()).unwrap(), None => CString::new("").unwrap(), }; let p_api = api.map_or(null::(), |a| a.as_ref()); let p_server = server.map_or(null::(), |_| c_server.as_ptr() as *const c_char); match unsafe { capi::pa_context_connect(self.ptr, p_server, flags.bits(), p_api) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Terminates the context connection immediately. #[inline] pub fn disconnect(&mut self) { unsafe { capi::pa_context_disconnect(self.ptr); } } /// Drains the context. /// /// If there is nothing to drain, the function returns `None`. /// /// Note that it can also return `None` under other conditions. Many functions in the C API /// perform internal state validation checks and return a null pointer if they detect a problem, /// just as they return a null pointer on invalid input. Other functions panic on getting a null /// pointer return, however this function is unique in a null pointer also signalling something /// useful, and it is not possible to tell the difference. However, while I feel the need to be /// clear about the possibility, I believe that such invalid state conditions should only occur /// if there were a serious bug within PA, thus you are probably safe to just ignore this and /// always take a `None` return to indicate only that there is nothing to drain. pub fn drain(&mut self, callback: F) -> Option> where F: FnMut() + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_drain(self.ptr, Some(notify_cb_proxy_single), cb_data) }; // NOTE: this function is unique in NEEDING the `Option` wrapper on the return value, since // a null pointer may be returned if there is nothing to drain! Do not remove it! match ptr.is_null() { false => Some(Operation::from_raw(ptr, cb_data as *mut Box)), true => None, } } /// Tells the daemon to exit. /// /// The returned operation is unlikely to complete successfully, since the daemon probably died /// before returning a success notification. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn exit_daemon(&mut self, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_exit_daemon(self.ptr, Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the name of the default sink. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn set_default_sink(&mut self, name: &str, callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_set_default_sink(self.ptr, c_name.as_ptr(), Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the name of the default source. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn set_default_source(&mut self, name: &str, callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_set_default_source(self.ptr, c_name.as_ptr(), Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Checks if this is a connection to a local daemon. /// /// Returns `true` when the connection is to a local daemon. Returns `None` on error, for /// instance when no connection has been made yet. pub fn is_local(&self) -> Option { match unsafe { capi::pa_context_is_local(self.ptr) } { 1 => Some(true), 0 => Some(false), _ => None, } } /// Sets a different application name for context on the server. /// /// Panics if the underlying C function returns a null pointer. pub fn set_name(&mut self, name: &str, callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_set_name(self.ptr, c_name.as_ptr(), Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Gets the server name this context is connected to. pub fn get_server(&self) -> Option { let ptr = unsafe { capi::pa_context_get_server(self.ptr) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }), true => None, } } /// Gets the protocol version of the library. #[inline] pub fn get_protocol_version(&self) -> u32 { unsafe { capi::pa_context_get_protocol_version(self.ptr) } } /// Gets the protocol version of the connected server. /// /// Returns `None` on error. pub fn get_server_protocol_version(&self) -> Option { match unsafe { capi::pa_context_get_server_protocol_version(self.ptr) } { def::INVALID_INDEX => None, r => Some(r), } } /// Updates the property list of the client, adding new entries. /// /// Please note that it is highly recommended to set as many properties initially via /// [`new_with_proplist()`](Self::new_with_proplist) as possible instead a posteriori with this /// function, since that information may then be used to route streams of the client to the /// right device. /// /// Panics if the underlying C function returns a null pointer. pub fn proplist_update(&mut self, mode: proplist::UpdateMode, pl: &Proplist, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_proplist_update(self.ptr, mode, pl.0.ptr, Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Updates the property list of the client, remove entries. /// /// Panics if the underlying C function returns a null pointer. pub fn proplist_remove(&mut self, keys: &[&str], callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let mut c_keys: Vec = Vec::with_capacity(keys.len()); for key in keys { c_keys.push(CString::new(*key).unwrap()); } // Capture array of pointers to the above CString values. // We also add a NULL pointer entry on the end, as expected by the C function called here. let mut c_key_ptrs: Vec<*const c_char> = Vec::with_capacity(c_keys.len() + 1); for c_key in c_keys { c_key_ptrs.push(c_key.as_ptr()); } c_key_ptrs.push(null()); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_proplist_remove(self.ptr, c_key_ptrs.as_ptr(), Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Gets the client index this context is identified in the server with. /// /// This is useful for usage with the introspection functions, such as /// [`Introspector::get_client_info()`](self::introspect::Introspector::get_client_info). /// /// Returns `None` on error. pub fn get_index(&self) -> Option { match unsafe { capi::pa_context_get_index(self.ptr) } { def::INVALID_INDEX => None, r => Some(r), } } /// Creates a new timer event source for the specified time. /// /// This is an alternative to the mainloop `new_timer_event_rt` method. /// /// A reference to the mainloop object is needed, in order to associate the event object with /// it. The association is done to ensure the event does not outlive the mainloop. /// /// If pointer returned by underlying C function is `NULL`, `None` will be returned, otherwise a /// [`TimeEvent`](crate::mainloop::events::timer::TimeEvent) object will be returned. /// /// Example event set to fire in five seconds time: /// /// ```rust,ignore /// use libpulse_binding::time::{MonotonicTs, MicroSeconds}; /// let _t_event = context.rttime_new::(&mainloop, /// MonotonicTs::now() + MicroSeconds::from_secs(5).unwrap(), /// |_| { println!("Timer event fired!"); }); /// ``` /// /// **Note**: You must ensure that the returned event object lives for as long as you want its /// event(s) to fire, as its `Drop` implementation destroys the event source. I.e. if you create /// a new event, but then immediately drop the object returned here, no event will fire! pub fn rttime_new(&self, mainloop: &dyn Mainloop, time: MonotonicTs, mut callback: F) -> Option> where T: Mainloop + 'static, F: FnMut(TimeEventRef) + 'static { let inner_for_wrapper = mainloop.inner(); let wrapper_cb = Box::new(move |ptr| { let ref_obj = TimeEventRef::::from_raw(ptr, Rc::clone(&inner_for_wrapper)); callback(ref_obj); }); let to_save = events::timer::EventCb::new(Some(wrapper_cb)); let (cb_fn, cb_data) = to_save.get_capi_params(events::timer::event_cb_proxy); let ptr = unsafe { capi::pa_context_rttime_new(self.ptr, (time.0).0, std::mem::transmute(cb_fn), cb_data) }; match ptr.is_null() { false => Some(TimeEvent::::from_raw(ptr, mainloop.inner(), to_save)), true => None, } } /// Gets the optimal block size for passing around audio buffers. /// /// It is recommended to allocate buffers of the size returned here when writing audio data to /// playback streams, if the latency constraints permit this. It is not recommended writing /// larger blocks than this because usually they will then be split up internally into chunks of /// this size. It is not recommended writing smaller blocks than this (unless required due to /// latency demands) because this increases CPU usage. /// /// If `ss` is `None` you will be returned the byte-exact tile size. /// /// If `ss` is invalid, returns `None`, else returns tile size rounded down to multiple of the /// frame size. /// /// This is supposed to be used in a construct such as: /// /// ```rust,ignore /// let ss = stream.get_sample_spec().unwrap(); /// let size = context.get_tile_size(Some(ss)).unwrap(); /// ``` pub fn get_tile_size(&self, ss: Option<&sample::Spec>) -> Option { let p_ss = ss.map_or(null::(), |s| s.as_ref()); match unsafe { capi::pa_context_get_tile_size(self.ptr, p_ss) } { std::usize::MAX => None, r => Some(r), } } /// Loads the authentication cookie from a file. /// /// This function is primarily meant for PulseAudio’s own tunnel modules, which need to load the /// cookie from a custom location. Applications don’t usually need to care about the cookie at /// all, but if it happens that you know what the authentication cookie is and your application /// needs to load it from a non-standard location, feel free to use this function. pub fn load_cookie_from_file(&mut self, cookie_file_path: &str) -> Result<(), PAErr> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_path = CString::new(cookie_file_path.clone()).unwrap(); match unsafe { capi::pa_context_load_cookie_from_file(self.ptr, c_path.as_ptr()) } { 0 => Ok(()), e => Err(PAErr(e)), } } } impl Drop for Context { fn drop(&mut self) { if !self.weak { unsafe { capi::pa_context_unref(self.ptr) }; } self.ptr = null_mut::(); } } /// Proxy for completion success callbacks. /// /// Warning: This is for single-use cases only! It destroys the actual closure callback. extern "C" fn success_cb_proxy(_: *mut ContextInternal, success: i32, userdata: *mut c_void) { let success_actual = match success { 0 => false, _ => true }; let _ = std::panic::catch_unwind(|| { assert!(!userdata.is_null()); // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = unsafe { Box::from_raw(userdata as *mut Box) }; (callback)(success_actual); }); } /// Proxy for notification callbacks (single use). /// /// Warning: This is for single-use cases only! It destroys the actual closure callback. extern "C" fn notify_cb_proxy_single(_: *mut ContextInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { assert!(!userdata.is_null()); // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = unsafe { Box::from_raw(userdata as *mut Box) }; (callback)(); }); } /// Proxy for notification callbacks (multi use). /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn notify_cb_proxy_multi(_: *mut ContextInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = NotifyCb::get_callback(userdata); (callback)(); }); } /// Proxy for event callbacks. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn event_cb_proxy(_: *mut ContextInternal, name: *const c_char, proplist: *mut ProplistInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { assert!(!name.is_null()); let n = { let tmp = unsafe { CStr::from_ptr(name) }; tmp.to_string_lossy().into_owned() }; let pl = Proplist::from_raw_weak(proplist); let callback = EventCb::get_callback(userdata); (callback)(n, pl); }); } /// Proxy for extension test callbacks. /// /// Warning: This is for single-use cases only! It destroys the actual closure callback. extern "C" fn ext_test_cb_proxy(_: *mut ContextInternal, version: u32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = get_su_callback::(userdata); (callback)(version); }); } /// Proxy for extension subscribe callbacks. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn ext_subscribe_cb_proxy(_: *mut ContextInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = ExtSubscribeCb::get_callback(userdata); (callback)(); }); } libpulse-binding-2.28.1/src/context/scache.rs000064400000000000000000000175421046102023000172250ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Sample cache mechanism. //! //! # Overview //! //! The sample cache provides a simple way of overcoming high network latencies and reducing //! bandwidth. Instead of streaming a sound precisely when it should be played, it is stored on the //! server and only the command to start playing it needs to be sent. //! //! # Creation //! //! To create a sample, the normal stream API is used (see [`stream`]). The function //! [`Stream::connect_upload()`] will make sure the stream is stored as a sample on the server. //! //! To complete the upload, [`Stream::finish_upload()`] is called and the sample will receive the //! same name as the stream. If the upload should be aborted, simply call [`Stream::disconnect()`]. //! //! # Playing samples //! //! To play back a sample, simply call [`Context::play_sample()`]: //! //! ```rust,ignore //! extern crate libpulse_binding as pulse; //! //! use pulse::volume; //! //! //... //! //! let o = my_context.play_sample( //! "sample2", // Name of my sample //! None, // Use default sink //! volume::VOLUME_NORM, // Full volume //! None // Don’t need a callback //! ); //! ``` //! //! # Removing samples //! //! When a sample is no longer needed, it should be removed on the server to save resources. The //! sample is deleted using [`Context::remove_sample()`]. //! //! [`stream`]: mod@crate::stream //! [`Stream::connect_upload()`]: crate::stream::Stream::connect_upload //! [`Stream::finish_upload()`]: crate::stream::Stream::finish_upload //! [`Stream::disconnect()`]: crate::stream::Stream::disconnect use std::os::raw::{c_char, c_void}; use std::ffi::CString; use std::ptr::null; use super::{ContextInternal, Context}; use crate::def; use crate::callbacks::{box_closure_get_capi_ptr, get_su_capi_params, get_su_callback}; use crate::{operation::Operation, volume::Volume, proplist::Proplist}; impl Context { /// Removes a sample from the sample cache. /// /// Returns an operation object which may be used to cancel the operation while it is running. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn remove_sample(&mut self, name: &str, callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_remove_sample(self.ptr, c_name.as_ptr(), Some(super::success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Plays a sample from the sample cache to the specified device. /// /// If the specified device is `None` use the default sink. /// /// # Params /// /// * `name`: Name of the sample to play. /// * `dev`: Sink to play this sample on, or `None` for default. /// * `volume`: Volume to play this sample with, or `None` to leave the decision about the /// volume to the server side which is a good idea. [`Volume::INVALID`] has the same meaning /// as `None. /// * `callback`: Optional success callback. It must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn play_sample(&mut self, name: &str, dev: Option<&str>, volume: Option, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let c_dev = match dev { Some(dev) => CString::new(dev.clone()).unwrap(), None => CString::new("").unwrap(), }; let p_dev = dev.map_or(null::(), |_| c_dev.as_ptr() as *const c_char); let vol = volume.unwrap_or(Volume::INVALID); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, super::success_cb_proxy); let ptr = unsafe { capi::pa_context_play_sample(self.ptr, c_name.as_ptr(), p_dev, vol.0, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Plays a sample from the sample cache to the specified device, allowing specification of a /// property list for the playback stream. /// /// If the device is `None` use the default sink. /// /// # Params /// /// * `name`: Name of the sample to play. /// * `dev`: Sink to play this sample on, or `None` for default. /// * `volume`: Volume to play this sample with, or `None` to leave the decision about the /// volume to the server side which is a good idea. [`Volume::INVALID`] has the same meaning /// as `None. /// * `proplist`: Property list for this sound. The property list of the cached entry will have /// this merged into it. /// * `callback`: Optional success callback. It must accept an `u32` index value wrapper in a /// `Result`. The index is the index of the sink input object. `Err` is given instead on /// failure. /// /// Panics if the underlying C function returns a null pointer. pub fn play_sample_with_proplist(&mut self, name: &str, dev: Option<&str>, volume: Option, proplist: &Proplist, callback: Option) + 'static>>) -> Operation)> { // Warning: New CStrings will be immediately freed if not bound to a // variable, leading to as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let c_dev = match dev { Some(dev) => CString::new(dev.clone()).unwrap(), None => CString::new("").unwrap(), }; let p_dev = dev.map_or(null::(), |_| c_dev.as_ptr() as *const c_char); let vol = volume.unwrap_or(Volume::INVALID); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, play_sample_success_cb_proxy); let ptr = unsafe { capi::pa_context_play_sample_with_proplist(self.ptr, c_name.as_ptr(), p_dev, vol.0, proplist.0.ptr, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box)>) } } /// Proxy for completion success callbacks. /// /// Warning: This is for single-use cases only! It destroys the actual closure callback. extern "C" fn play_sample_success_cb_proxy(_: *mut ContextInternal, index: u32, userdata: *mut c_void) { let index_actual = match index { def::INVALID_INDEX => Err(()), i => Ok(i) }; let _ = std::panic::catch_unwind(|| { // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = get_su_callback::)>(userdata); (callback)(index_actual); }); } libpulse-binding-2.28.1/src/context/subscribe.rs000064400000000000000000000252571046102023000177620ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Daemon introspection event subscription subsystem. //! //! # Overview //! //! The application can be notified, asynchronously, whenever the internal layout of the server //! changes. The set of facilities and operations for which notifications are generated are //! enumerated in [`Facility`] and [`Operation`]. //! //! The application sets the notification mask using [`Context::subscribe()`] and the callback //! function that will be called whenever a notification occurs using //! [`Context::set_subscribe_callback()`]. //! //! The mask provided to [`Context::subscribe()`] can be created by binary ORing a set of values, //! either produced with [`Facility::to_interest_mask()`], or more simply with the provided //! constants in the [`subscription_masks`](mod@subscription_masks) submodule. //! //! The callback will be called with event type information representing the event that caused the //! callback, detailing *facility* and *operation*, where for instance [`Facility::Source`] with //! [`Operation::New`] indicates that a new source was added. //! //! # Example //! //! Subscribe (declare interest): //! //! ```rust,ignore //! use libpulse_binding::context::subscribe::subscription_masks; //! //! let interest = subscription_masks::SINK | //! subscription_masks::SOURCE; //! //! let op = my_context.subscribe( //! interest, // Our interest mask //! |_| {} // We won’t bother doing anything in the success callback in this example //! ); //! ``` use std::os::raw::c_void; use bitflags::bitflags; use num_derive::{FromPrimitive, ToPrimitive}; use super::{ContextInternal, Context}; use crate::operation; use crate::callbacks::{box_closure_get_capi_ptr, MultiUseCallback}; pub use capi::context::subscribe::pa_subscription_event_type_t as EventType; /// Mask to extract facility value from the event type passed to the user callback. #[deprecated(since = "2.20.0", note = "use the associated constant on `Facility` instead")] pub const FACILITY_MASK: u32 = capi::PA_SUBSCRIPTION_EVENT_FACILITY_MASK; /// Mask to extract operation value from the event type passed to the user callback. #[deprecated(since = "2.20.0", note = "use the associated constant on `Operation` instead")] pub const OPERATION_MASK: u32 = capi::PA_SUBSCRIPTION_EVENT_TYPE_MASK; bitflags! { /// A set of facility masks, to be passed to [`Context::subscribe()`]. /// /// Note that you can convert a [`Facility`] to a mask with [`Facility::to_interest_mask()`]. #[repr(transparent)] pub struct InterestMaskSet: u32 { /// No facility (null selection; zero). const NULL = capi::PA_SUBSCRIPTION_MASK_NULL; /// Sink facility. const SINK = capi::PA_SUBSCRIPTION_MASK_SINK; /// Source facility. const SOURCE = capi::PA_SUBSCRIPTION_MASK_SOURCE; /// Sink input facility. const SINK_INPUT = capi::PA_SUBSCRIPTION_MASK_SINK_INPUT; /// Source output facility. const SOURCE_OUTPUT = capi::PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT; /// Module facility. const MODULE = capi::PA_SUBSCRIPTION_MASK_MODULE; /// Client facility. const CLIENT = capi::PA_SUBSCRIPTION_MASK_CLIENT; /// Sample cache facility. const SAMPLE_CACHE = capi::PA_SUBSCRIPTION_MASK_SAMPLE_CACHE; /// Server facility. const SERVER = capi::PA_SUBSCRIPTION_MASK_SERVER; /// Card facility. const CARD = capi::PA_SUBSCRIPTION_MASK_CARD; /// All facilities. const ALL = capi::PA_SUBSCRIPTION_MASK_ALL; } } /// A set of masks used for expressing which facilities you are interested in when subscribing. #[deprecated(since = "2.20.0", note = "Use the associated constants on `InterestMaskSet`.")] pub mod subscription_masks { use super::InterestMaskSet; /// No facility (null selection; zero). pub const NULL: InterestMaskSet = InterestMaskSet::NULL; /// Sink facility. pub const SINK: InterestMaskSet = InterestMaskSet::SINK; /// Source facility. pub const SOURCE: InterestMaskSet = InterestMaskSet::SOURCE; /// Sink input facility. pub const SINK_INPUT: InterestMaskSet = InterestMaskSet::SINK_INPUT; /// Source output facility. pub const SOURCE_OUTPUT: InterestMaskSet = InterestMaskSet::SOURCE_OUTPUT; /// Module facility. pub const MODULE: InterestMaskSet = InterestMaskSet::MODULE; /// Client facility. pub const CLIENT: InterestMaskSet = InterestMaskSet::CLIENT; /// Sample cache facility. pub const SAMPLE_CACHE: InterestMaskSet = InterestMaskSet::SAMPLE_CACHE; /// Server facility. pub const SERVER: InterestMaskSet = InterestMaskSet::SERVER; /// Card facility. pub const MASK_CARD: InterestMaskSet = InterestMaskSet::CARD; /// All facilities. pub const ALL: InterestMaskSet = InterestMaskSet::ALL; } /// Facility component of an event. #[repr(u32)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] pub enum Facility { /// Sink. Sink = 0, /// Source. Source = 1, /// Sink-input. SinkInput = 2, /// Source-output. SourceOutput = 3, /// Module. Module = 4, /// Client. Client = 5, /// Sample-cache. SampleCache = 6, /// Global server change, only occurring with [`Operation::Changed`]. Server = 7, /* NOTE: value `8` previously assigned, obsoleted */ /// Card. Card = 9, } /// Operation component of an event. #[repr(u32)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] pub enum Operation { /// A new object was created New = 0, /// A property of the object was modified Changed = 0x10, /// An object was removed Removed = 0x20, } impl Facility { /// Mask to extract facility value from the event type passed to the user callback. pub const MASK: EventType = capi::PA_SUBSCRIPTION_EVENT_FACILITY_MASK; fn from_event(value: EventType) -> Option { match value & Self::MASK { 0 => Some(Facility::Sink), 1 => Some(Facility::Source), 2 => Some(Facility::SinkInput), 3 => Some(Facility::SourceOutput), 4 => Some(Facility::Module), 5 => Some(Facility::Client), 6 => Some(Facility::SampleCache), 7 => Some(Facility::Server), /* NOTE: value `8` previously assigned, obsoleted */ 9 => Some(Facility::Card), _ => None, } } /// Converts to an interest mask. #[inline(always)] pub const fn to_interest_mask(self) -> InterestMaskSet { InterestMaskSet::from_bits_truncate(1u32 << (self as u32)) } } impl Operation { /// Mask to extract operation value from the event type passed to the user callback. pub const MASK: EventType = capi::PA_SUBSCRIPTION_EVENT_TYPE_MASK; fn from_event(value: EventType) -> Option { match value & Self::MASK { 0 => Some(Operation::New), 0x10 => Some(Operation::Changed), 0x20 => Some(Operation::Removed), _ => None, } } } pub(super) type Callback = MultiUseCallback, Option, u32), extern "C" fn(*mut ContextInternal, EventType, u32, *mut c_void)>; impl Context { /// Enables event notification. /// /// The `mask` parameter is used to specify which facilities you are interested in being /// modified about. Use [`set_subscribe_callback()`] to set the actual callback that will be /// called when an event occurs. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. /// /// [`set_subscribe_callback()`]: Self::set_subscribe_callback pub fn subscribe(&mut self, mask: InterestMaskSet, callback: F) -> operation::Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_context_subscribe(self.ptr, mask.bits(), Some(super::success_cb_proxy), cb_data) }; operation::Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the context specific call back function that is called whenever a subscribed-to event /// occurs. /// /// Use [`subscribe()`] to set the facilities you are interested in receiving notifications for, /// and thus to start receiving notifications with the callback set here. /// /// The callback must take three parameters. The first two are the facility and operation /// components of the event type respectively (the underlying C API provides this information /// combined into a single integer, here we extract the two component parts for you); these are /// wrapped in `Option` wrappers should the given values ever not map to the enum variants, but /// it’s probably safe to always just `unwrap()` them). The third parameter is an associated /// index value. /// /// [`subscribe()`]: Self::subscribe pub fn set_subscribe_callback(&mut self, callback: Option, Option, u32) + 'static>>) { let saved = &mut self.cb_ptrs.subscribe; *saved = Callback::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(cb_proxy); unsafe { capi::pa_context_set_subscribe_callback(self.ptr, cb_fn, cb_data); } } } /// Proxy for callbacks. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn cb_proxy(_: *mut ContextInternal, et: EventType, index: u32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let facility = Facility::from_event(et); let operation = Operation::from_event(et); let callback = Callback::get_callback(userdata); (callback)(facility, operation, index); }); } libpulse-binding-2.28.1/src/def.rs000064400000000000000000000630661046102023000150530ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Global definitions. use std::os::raw::c_void; use bitflags::bitflags; use num_derive::{FromPrimitive, ToPrimitive}; use crate::time::{MicroSeconds, UnixTs}; pub use capi::PA_INVALID_INDEX as INVALID_INDEX; pub use capi::pa_device_type_t as Device; pub use capi::pa_port_available_t as PortAvailable; pub use capi::pa_device_port_type_t as DevicePortType; /// A callback type for releasing allocations. pub type FreeCb = extern "C" fn(p: *mut c_void); /// PulseAudio 'quit return value' type. pub type RetvalActual = i32; /// A wrapper around integer ‘quit return values’ returned by PulseAudio. #[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Retval(pub RetvalActual); /// Playback and record buffer metrics. #[repr(C)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] pub struct BufferAttr { /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */ /// Maximum length of the buffer in bytes. /// /// Setting this to [`std::u32::MAX`] will initialize this to the maximum value supported by the /// server, which is recommended. In strict low-latency playback scenarios you might want to set /// this to a lower value, likely together with the [`stream::FlagSet::ADJUST_LATENCY`] flag. If /// you do so, you ensure that the latency doesn’t grow beyond what is acceptable for the use /// case, at the cost of getting more underruns if the latency is lower than what the server can /// reliably handle. /// /// [`stream::FlagSet::ADJUST_LATENCY`]: crate::stream::FlagSet::ADJUST_LATENCY pub maxlength: u32, /// Target length of the buffer (playback only). The server tries to assure that at least /// `tlength` bytes are always available in the per-stream server-side playback buffer. The /// server will only send requests for more data as long as the buffer has less than this number /// of bytes of data. /// /// It is recommended to set this to [`std::u32::MAX`], which will initialize this to a value /// that is deemed sensible by the server. However, this value will default to something like /// 2s; for applications that have specific latency requirements this value should be set to /// the maximum latency that the application can deal with. /// /// When [`stream::FlagSet::ADJUST_LATENCY`] is not set this value will influence only the /// per-stream playback buffer size. When [`stream::FlagSet::ADJUST_LATENCY`] is set, the /// overall latency of the sink plus the playback buffer size is configured to this value. Set /// [`stream::FlagSet::ADJUST_LATENCY`] if you are interested in adjusting the overall latency. /// Don’t set it if you are interested in configuring the server-side per-stream playback buffer /// size. /// /// [`stream::FlagSet::ADJUST_LATENCY`]: crate::stream::FlagSet::ADJUST_LATENCY pub tlength: u32, /// Pre-buffering (playback only). The server does not start with playback before at least /// `prebuf` bytes are available in the buffer. It is recommended to set this to /// [`std::u32::MAX`], which will initialize this to the same value as `tlength`, whatever that /// may be. /// /// Initialize to `0` to enable manual start/stop control of the stream. This means that /// playback will not stop on underrun and playback will not start automatically, instead /// [`Stream::cork()`] needs to be called explicitly. If you set this value to `0` you should /// also set [`stream::FlagSet::START_CORKED`]. Should underrun occur, the read index of the /// output buffer overtakes the write index, and hence the fill level of the buffer is negative. /// /// Start of playback can be forced using [`Stream::trigger()`] even though the prebuffer size /// hasn’t been reached. If a buffer underrun occurs, this prebuffering will be again enabled. /// /// [`Stream::cork()`]: crate::stream::Stream::cork /// [`Stream::trigger()`]: crate::stream::Stream::trigger /// [`stream::FlagSet::START_CORKED`]: crate::stream::FlagSet::START_CORKED pub prebuf: u32, /// Minimum request (playback only). The server does not request less than `minreq` bytes from /// the client, instead it waits until the buffer is free enough to request more bytes at once. /// /// It is recommended to set this to [`std::u32::MAX`], which will initialize this to a value /// that is deemed sensible by the server. This should be set to a value that gives PulseAudio /// enough time to move the data from the per-stream playback buffer into the hardware playback /// buffer. pub minreq: u32, /// Fragment size (recording only). The server sends data in blocks of `fragsize` bytes size. /// /// Large values diminish interactivity with other operations on the connection context but /// decrease control overhead. It is recommended to set this to `std::u32::MAX`, which will /// initialize this to a value that is deemed sensible by the server. However, this value will /// default to something like 2s; For applications that have specific latency requirements this /// value should be set to the maximum latency that the application can deal with. /// /// If [`stream::FlagSet::ADJUST_LATENCY`] is set the overall source latency will be adjusted /// according to this value. If it is not set the source latency is left unmodified. /// /// [`stream::FlagSet::ADJUST_LATENCY`]: crate::stream::FlagSet::ADJUST_LATENCY pub fragsize: u32, } /// Test size is equal to `sys` equivalent (duplicated here for different documentation) #[test] fn bufferattr_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); } impl AsRef for BufferAttr { #[inline] fn as_ref(&self) -> &capi::pa_buffer_attr { unsafe { &*(self as *const Self as *const capi::pa_buffer_attr) } } } impl AsRef for capi::pa_buffer_attr { #[inline] fn as_ref(&self) -> &BufferAttr { unsafe { &*(self as *const Self as *const BufferAttr) } } } /// A structure for all kinds of timing information of a stream. /// /// See [`Stream::update_timing_info()`] and [`Stream::get_timing_info()`]. /// /// The total output latency a sample that is written with [`Stream::write()`] takes to be played /// may be estimated by: /// /// `` /// sink_usec + buffer_usec + transport_usec /// `` /// /// (Where `buffer_usec` is defined as the result of passing ``write_index - read_index`` to /// [`Spec::bytes_to_usec()`]). The output buffer which `buffer_usec` relates to may be manipulated /// freely (with [`Stream::write()`]’s `seek` argument, [`Stream::flush()`] and friends), the /// buffers `sink_usec` and `source_usec` relate to are first-in first-out (FIFO) buffers which /// cannot be flushed or manipulated in any way. The total input latency a sample that is recorded /// takes to be delivered to the application is: /// /// `` /// source_usec + buffer_usec + transport_usec - sink_usec /// `` /// /// (Take care of sign issues!). When connected to a monitor source `sink_usec` contains the latency /// of the owning sink. The two latency estimations described here are implemented in /// [`Stream::get_latency()`]. /// /// All time values are in the sound card clock domain, unless noted otherwise. The sound card clock /// usually runs at a slightly different rate than the system clock. /// /// Please note that this structure can be extended as part of evolutionary API updates at any time /// in any new release. /// /// [`Spec::bytes_to_usec()`]: crate::sample::Spec::bytes_to_usec /// [`Stream::update_timing_info()`]: crate::stream::Stream::update_timing_info /// [`Stream::get_timing_info()`]: crate::stream::Stream::get_timing_info /// [`Stream::write()`]: crate::stream::Stream::write /// [`Stream::flush()`]: crate::stream::Stream::flush /// [`Stream::get_latency()`]: crate::stream::Stream::get_latency #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TimingInfo { /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */ /// The system clock time when this timing info structure was current. pub timestamp: UnixTs, /// Non-zero if the local and the remote machine have synchronized clocks. If synchronized /// clocks are detected `transport_usec` becomes much more reliable. However, the code that /// detects synchronized clocks is very limited and unreliable itself. pub synchronized_clocks: i32, /// Time in usecs a sample takes to be played on the sink. For playback streams and record /// streams connected to a monitor source. pub sink_usec: MicroSeconds, /// Time in usecs a sample takes from being recorded to being delivered to the application. Only /// for record streams. pub source_usec: MicroSeconds, /// Estimated time in usecs a sample takes to be transferred to/from the daemon. For both /// playback and record streams. pub transport_usec: MicroSeconds, /// Non-zero when the stream is currently not underrun and data is being passed on to the /// device. Only for playback streams. This field does not say whether the data is actually /// already being played. To determine this check whether `since_underrun` (converted to usec) /// is larger than `sink_usec`. pub playing: i32, /// Non-zero if `write_index` is not up-to-date because a local write command that corrupted it /// has been issued in the time since this latency info was current. Only write commands with /// [`SeekMode::RelativeOnRead`] and [`SeekMode::RelativeEnd`] can corrupt `write_index`. /// /// [`SeekMode::RelativeOnRead`]: crate::stream::SeekMode::RelativeOnRead /// [`SeekMode::RelativeEnd`]: crate::stream::SeekMode::RelativeEnd pub write_index_corrupt: i32, /// Current write index into the playback buffer in bytes. /// /// Think twice before using this for seeking purposes: it might be out of date at the time you /// want to use it. Consider using [`SeekMode::Relative`] instead. /// /// [`SeekMode::Relative`]: crate::stream::SeekMode::Relative pub write_index: i64, /// Non-zero if `read_index` is not up-to-date because a local pause or flush request that /// corrupted it has been issued in the time since this latency info was current. pub read_index_corrupt: i32, /// Current read index into the playback buffer in bytes. /// /// Think twice before using this for seeking purposes: it might be out of date at the time you /// want to use it. Consider using [`SeekMode::RelativeOnRead`] instead. /// /// [`SeekMode::RelativeOnRead`]: crate::stream::SeekMode::RelativeOnRead pub read_index: i64, /// The configured latency for the sink. pub configured_sink_usec: MicroSeconds, /// The configured latency for the source. pub configured_source_usec: MicroSeconds, /// Bytes that were handed to the sink since the last underrun happened, or since playback /// started again after the last underrun. `playing` will tell you which case it is. pub since_underrun: i64, } /// Test size is equal to `sys` equivalent (duplicated here for different documentation) #[test] fn timinginfo_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); } impl AsRef for capi::pa_timing_info { #[inline] fn as_ref(&self) -> &TimingInfo { unsafe { &*(self as *const Self as *const TimingInfo) } } } /// A structure for the spawn API. /// /// This may be used to integrate auto spawned daemons into your application. For more information /// see [`Context::connect()`]. When spawning a new child process the `waitpid()` is used on the /// child’s PID. The spawn routine will not block or ignore SIGCHLD signals, since this cannot be /// done in a thread compatible way. You might have to do this in prefork/postfork. /// /// [`Context::connect()`]: crate::context::Context::connect #[repr(C)] #[derive(Debug)] pub struct SpawnApi { /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */ /// Is called just before the fork in the parent process. pub prefork: Option, /// Is called immediately after the fork in the parent process. pub postfork: Option, /// Is called immediately after the fork in the child process. /// /// It is not safe to close all file descriptors in this function unconditionally, since a UNIX /// socket (created using socketpair()) is passed to the new process. pub atfork: Option, } /// Test size is equal to `sys` equivalent (duplicated here for different documentation) #[test] fn spawnapi_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); } impl AsRef for SpawnApi { #[inline] fn as_ref(&self) -> &capi::pa_spawn_api { unsafe { &*(self as *const Self as *const capi::pa_spawn_api) } } } bitflags! { /// Set of sink flags. #[repr(transparent)] pub struct SinkFlagSet: u32 { /// Flag to pass when no specific options are needed. const NOFLAGS = capi::PA_SINK_NOFLAGS; /// Supports hardware volume control. This is a dynamic flag and may change at runtime after /// the sink has initialized. const HW_VOLUME_CTRL = capi::PA_SINK_HW_VOLUME_CTRL; /// Supports latency querying. const LATENCY = capi::PA_SINK_LATENCY; /// Is a hardware sink of some kind, in contrast to “virtual”/software sinks. const HARDWARE = capi::PA_SINK_HARDWARE; /// Is a networked sink of some kind. const NETWORK = capi::PA_SINK_NETWORK; /// Supports hardware mute control. This is a dynamic flag and may change at runtime after /// the sink has initialized. const HW_MUTE_CTRL = capi::PA_SINK_HW_MUTE_CTRL; /// Volume can be translated to dB with the `From` based conversions between [`Volume`], /// [`VolumeLinear`] and [`VolumeDB`] types. This is a dynamic flag and may change at /// runtime after the sink has initialized. /// /// [`Volume`]: crate::volume::Volume /// [`VolumeDB`]: crate::volume::VolumeDB /// [`VolumeLinear`]: crate::volume::VolumeLinear const DECIBEL_VOLUME = capi::PA_SINK_DECIBEL_VOLUME; /// This sink is in flat volume mode, i.e. always the maximum of the volume of all /// connected inputs. const FLAT_VOLUME = capi::PA_SINK_FLAT_VOLUME; /// The latency can be adjusted dynamically depending on the needs of the connected streams. const DYNAMIC_LATENCY = capi::PA_SINK_DYNAMIC_LATENCY; /// The sink allows setting what formats are supported by the connected hardware. The actual /// functionality to do this might be provided by an extension. const SET_FORMATS = capi::PA_SINK_SET_FORMATS; } } /// Special sink flags. #[deprecated(since = "2.20.0", note = "Use the associated constants on `SinkFlagSet`.")] pub mod sink_flags { use super::SinkFlagSet; /// Flag to pass when no specific options are needed. pub const NOFLAGS: SinkFlagSet = SinkFlagSet::NOFLAGS; /// Supports hardware volume control. This is a dynamic flag and may change at runtime after the /// sink has initialized. pub const HW_VOLUME_CTRL: SinkFlagSet = SinkFlagSet::HW_VOLUME_CTRL; /// Supports latency querying. pub const LATENCY: SinkFlagSet = SinkFlagSet::LATENCY; /// Is a hardware sink of some kind, in contrast to “virtual”/software sinks. pub const HARDWARE: SinkFlagSet = SinkFlagSet::HARDWARE; /// Is a networked sink of some kind. pub const NETWORK: SinkFlagSet = SinkFlagSet::NETWORK; /// Supports hardware mute control. This is a dynamic flag and may change at runtime after the /// sink has initialized. pub const HW_MUTE_CTRL: SinkFlagSet = SinkFlagSet::HW_MUTE_CTRL; /// Volume can be translated to dB with the `From` based conversions between [`Volume`], /// [`VolumeLinear`] and [`VolumeDB`] types. This is a dynamic flag and may change at runtime /// after the sink has initialized. /// /// [`Volume`]: crate::volume::Volume /// [`VolumeDB`]: crate::volume::VolumeDB /// [`VolumeLinear`]: crate::volume::VolumeLinear pub const DECIBEL_VOLUME: SinkFlagSet = SinkFlagSet::DECIBEL_VOLUME; /// This sink is in flat volume mode, i.e. always the maximum of the volume of all connected /// inputs. pub const FLAT_VOLUME: SinkFlagSet = SinkFlagSet::FLAT_VOLUME; /// The latency can be adjusted dynamically depending on the needs of the connected streams. pub const DYNAMIC_LATENCY: SinkFlagSet = SinkFlagSet::DYNAMIC_LATENCY; /// The sink allows setting what formats are supported by the connected hardware. The actual /// functionality to do this might be provided by an extension. pub const SET_FORMATS: SinkFlagSet = SinkFlagSet::SET_FORMATS; } /// Sink state. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] pub enum SinkState { /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate (C API) equivalent */ /// This state is used when the server does not support sink state introspection. Invalid = -1, /// Running, sink is playing and used by at least one non-corked sink-input. Running = 0, /// When idle, the sink is playing but there is no non-corked sink-input attached to it. Idle = 1, /// When suspended, actual sink access can be closed, for instance. Suspended = 2, } /// Check is equal to `sys` equivalent #[test] fn sink_state_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); // Check order and value of variants match // No point checking conversions in both directions since both are a transmute assert_eq!(SinkState::Invalid, SinkState::from(capi::pa_sink_state_t::Invalid)); assert_eq!(SinkState::Running, SinkState::from(capi::pa_sink_state_t::Running)); assert_eq!(SinkState::Idle, SinkState::from(capi::pa_sink_state_t::Idle)); assert_eq!(SinkState::Suspended, SinkState::from(capi::pa_sink_state_t::Suspended)); } impl From for capi::pa_sink_state_t { #[inline] fn from(s: SinkState) -> Self { unsafe { std::mem::transmute(s) } } } impl From for SinkState { #[inline] fn from(s: capi::pa_sink_state_t) -> Self { unsafe { std::mem::transmute(s) } } } impl SinkState { /// Checks if sink is playing: running or idle. #[inline] pub fn is_opened(self) -> bool { self == SinkState::Running || self == SinkState::Idle } /// Checks if sink is running. #[inline] pub fn is_running(self) -> bool { self == SinkState::Running } } bitflags! { /// Set of source flags. #[repr(transparent)] pub struct SourceFlagSet: u32 { /// Flag to pass when no specific options are needed. const NOFLAGS = capi::PA_SOURCE_NOFLAGS; /// Supports hardware volume control. This is a dynamic flag and may change at runtime after /// the source has initialized. const HW_VOLUME_CTRL = capi::PA_SOURCE_HW_VOLUME_CTRL; /// Supports latency querying. const LATENCY = capi::PA_SOURCE_LATENCY; /// Is a hardware source of some kind, in contrast to “virtual”/software source. const HARDWARE = capi::PA_SOURCE_HARDWARE; /// Is a networked source of some kind. const NETWORK = capi::PA_SOURCE_NETWORK; /// Supports hardware mute control. This is a dynamic flag and may change at runtime after /// the source has initialized. const HW_MUTE_CTRL = capi::PA_SOURCE_HW_MUTE_CTRL; /// Volume can be translated to dB with the `From` based conversions between [`Volume`], /// [`VolumeLinear`] and [`VolumeDB`] types. This is a dynamic flag and may change at /// runtime after the source has initialized. /// /// [`Volume`]: crate::volume::Volume /// [`VolumeDB`]: crate::volume::VolumeDB /// [`VolumeLinear`]: crate::volume::VolumeLinear const DECIBEL_VOLUME = capi::PA_SOURCE_DECIBEL_VOLUME; /// The latency can be adjusted dynamically depending on the needs of the connected streams. const DYNAMIC_LATENCY = capi::PA_SOURCE_DYNAMIC_LATENCY; /// This source is in flat volume mode, i.e. always the maximum of the volume of all /// connected outputs. const FLAT_VOLUME = capi::PA_SOURCE_FLAT_VOLUME; } } /// Special source flags. #[deprecated(since = "2.20.0", note = "Use the associated constants on `SourceFlagSet`.")] pub mod source_flags { use super::SourceFlagSet; /// Flag to pass when no specific options are needed. pub const NOFLAGS: SourceFlagSet = SourceFlagSet::NOFLAGS; /// Supports hardware volume control. This is a dynamic flag and may change at runtime after the /// source has initialized. pub const HW_VOLUME_CTRL: SourceFlagSet = SourceFlagSet::HW_VOLUME_CTRL; /// Supports latency querying. pub const LATENCY: SourceFlagSet = SourceFlagSet::LATENCY; /// Is a hardware source of some kind, in contrast to “virtual”/software source. pub const HARDWARE: SourceFlagSet = SourceFlagSet::HARDWARE; /// Is a networked source of some kind. pub const NETWORK: SourceFlagSet = SourceFlagSet::NETWORK; /// Supports hardware mute control. This is a dynamic flag and may change at runtime after the /// source has initialized. pub const HW_MUTE_CTRL: SourceFlagSet = SourceFlagSet::HW_MUTE_CTRL; /// Volume can be translated to dB with the `From` based conversions between [`Volume`], /// [`VolumeLinear`] and [`VolumeDB`] types. This is a dynamic flag and may change at runtime /// after the source has initialized. /// /// [`Volume`]: crate::volume::Volume /// [`VolumeDB`]: crate::volume::VolumeDB /// [`VolumeLinear`]: crate::volume::VolumeLinear pub const DECIBEL_VOLUME: SourceFlagSet = SourceFlagSet::DECIBEL_VOLUME; /// The latency can be adjusted dynamically depending on the needs of the connected streams. pub const DYNAMIC_LATENCY: SourceFlagSet = SourceFlagSet::DYNAMIC_LATENCY; /// This source is in flat volume mode, i.e. always the maximum of the volume of all connected /// outputs. pub const FLAT_VOLUME: SourceFlagSet = SourceFlagSet::FLAT_VOLUME; } /// Source state. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] pub enum SourceState { /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate (C API) equivalent */ /// This state is used when the server does not support source state introspection. Invalid = -1, /// Running, source is recording and used by at least one non-corked source-output. Running = 0, /// When idle, the source is still recording but there is no non-corked source-output. Idle = 1, /// When suspended, actual source access can be closed, for instance. Suspended = 2, } /// Check is equal to `sys` equivalent #[test] fn source_state_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); // Check order and value of variants match // No point checking conversions in both directions since both are a transmute assert_eq!(SourceState::Invalid, SourceState::from(capi::pa_source_state_t::Invalid)); assert_eq!(SourceState::Running, SourceState::from(capi::pa_source_state_t::Running)); assert_eq!(SourceState::Idle, SourceState::from(capi::pa_source_state_t::Idle)); assert_eq!(SourceState::Suspended, SourceState::from(capi::pa_source_state_t::Suspended)); } impl From for capi::pa_source_state_t { #[inline] fn from(s: SourceState) -> Self { unsafe { std::mem::transmute(s) } } } impl From for SourceState { #[inline] fn from(s: capi::pa_source_state_t) -> Self { unsafe { std::mem::transmute(s) } } } impl SourceState { /// Checks if source is recording: running or idle. #[inline] pub fn is_opened(self) -> bool { self == SourceState::Running || self == SourceState::Idle } /// Checks if source is running. #[inline] pub fn is_running(self) -> bool { self == SourceState::Running } } libpulse-binding-2.28.1/src/direction.rs000064400000000000000000000037151046102023000162700ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Utilities for direction. #[cfg(any(doc, feature = "pa_v6"))] use std::ffi::CStr; use bitflags::bitflags; bitflags! { /// Flag set. #[repr(transparent)] pub struct FlagSet: i32 { /// Output flag. const OUTPUT = capi::PA_DIRECTION_OUTPUT; /// Input flag. const INPUT = capi::PA_DIRECTION_INPUT; } } /// Available flags for [`FlagSet`]. #[deprecated(since = "2.20.0", note = "Use the associated constants on `FlagSet`.")] pub mod flags { use super::FlagSet; /// Output flag. pub const OUTPUT: FlagSet = FlagSet::OUTPUT; /// Input flag. pub const INPUT: FlagSet = FlagSet::INPUT; } impl FlagSet { /// Checks whether direction is valid (either input, output or bidirectional). #[inline] #[cfg(any(doc, feature = "pa_v6"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v6")))] pub fn is_valid(self) -> bool { unsafe { capi::pa_direction_valid(self.bits()) != 0 } } /// Gets a textual representation of the direction. #[inline] #[cfg(any(doc, feature = "pa_v6"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v6")))] pub fn to_string(self) -> String { unsafe { CStr::from_ptr(capi::pa_direction_to_string(self.bits())).to_string_lossy().into_owned() } } } libpulse-binding-2.28.1/src/error.rs000064400000000000000000000264401046102023000154410ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Error management. use std::ffi::CStr; use num_traits::FromPrimitive as FromPrimitiveTrait; use num_derive::{FromPrimitive, ToPrimitive}; /// A wrapper around integer errors returned by PulseAudio. Can be converted to a [`Code`] variant /// for comparison purposes if desired. #[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct PAErr(pub i32); /// These represent errors returned by many of the underlying PulseAudio C functions. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] #[allow(non_camel_case_types)] pub enum Code { /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate (C API) equivalent */ /// No error. Ok = 0, /// Access failure. Access, /// Unknown command. Command, /// Invalid argument. Invalid, /// Entity exists. Exist, /// No such entity. NoEntity, /// Connection refused. ConnectionRefused, /// Protocol error. Protocol, /// Timeout. Timeout, /// No authentication key. AuthKey, /// Internal. Internal, /// Connection terminated. ConnectionTerminated, /// Entity killed. Killed, /// Invalid server. InvalidServer, /// Module init failed. ModInitFailed, /// Bad state. BadState, /// No data. NoData, /// Incompatible protocol version. Version, /// Data too large. TooLarge, /// Operation not supported. NotSupported, /// The error code was unknown to the client. Unknown, /// Extension does not exist. NoExtension, /// Obsolete functionality. Obsolete, /// Missing implementation. NotImplemented, /// The caller forked without calling execve() and tried to reuse the context. Forked, /// An IO error happened. IO, /// Device or resource busy. Busy, } /// Check is equal to `sys` equivalent #[test] fn code_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); // Check order and value of variants match // No point checking conversions in both directions since both are a transmute assert_eq!(Code::Ok, Code::from(capi::pa_error_code_t::Ok)); assert_eq!(Code::Access, Code::from(capi::pa_error_code_t::Access)); assert_eq!(Code::Command, Code::from(capi::pa_error_code_t::Command)); assert_eq!(Code::Invalid, Code::from(capi::pa_error_code_t::Invalid)); assert_eq!(Code::Exist, Code::from(capi::pa_error_code_t::Exist)); assert_eq!(Code::NoEntity, Code::from(capi::pa_error_code_t::NoEntity)); assert_eq!(Code::ConnectionRefused, Code::from(capi::pa_error_code_t::ConnectionRefused)); assert_eq!(Code::Protocol, Code::from(capi::pa_error_code_t::Protocol)); assert_eq!(Code::Timeout, Code::from(capi::pa_error_code_t::Timeout)); assert_eq!(Code::AuthKey, Code::from(capi::pa_error_code_t::AuthKey)); assert_eq!(Code::Internal, Code::from(capi::pa_error_code_t::Internal)); assert_eq!(Code::ConnectionTerminated, Code::from(capi::pa_error_code_t::ConnectionTerminated)); assert_eq!(Code::Killed, Code::from(capi::pa_error_code_t::Killed)); assert_eq!(Code::InvalidServer, Code::from(capi::pa_error_code_t::InvalidServer)); assert_eq!(Code::ModInitFailed, Code::from(capi::pa_error_code_t::ModInitFailed)); assert_eq!(Code::BadState, Code::from(capi::pa_error_code_t::BadState)); assert_eq!(Code::NoData, Code::from(capi::pa_error_code_t::NoData)); assert_eq!(Code::Version, Code::from(capi::pa_error_code_t::Version)); assert_eq!(Code::TooLarge, Code::from(capi::pa_error_code_t::TooLarge)); assert_eq!(Code::NotSupported, Code::from(capi::pa_error_code_t::NotSupported)); assert_eq!(Code::Unknown, Code::from(capi::pa_error_code_t::Unknown)); assert_eq!(Code::NoExtension, Code::from(capi::pa_error_code_t::NoExtension)); assert_eq!(Code::Obsolete, Code::from(capi::pa_error_code_t::Obsolete)); assert_eq!(Code::NotImplemented, Code::from(capi::pa_error_code_t::NotImplemented)); assert_eq!(Code::Forked, Code::from(capi::pa_error_code_t::Forked)); assert_eq!(Code::IO, Code::from(capi::pa_error_code_t::IO)); assert_eq!(Code::Busy, Code::from(capi::pa_error_code_t::Busy)); } impl From for capi::pa_error_code_t { #[inline] fn from(c: Code) -> Self { unsafe { std::mem::transmute(c) } } } impl From for Code { #[inline] fn from(c: capi::pa_error_code_t) -> Self { unsafe { std::mem::transmute(c) } } } impl PAErr { /// Converts an integer error value, as returned by many PA C API functions, to a human readable /// string. pub fn to_string(&self) -> Option { let ptr = unsafe { capi::pa_strerror(self.0) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }), true => None, } } } impl std::error::Error for PAErr {} impl std::fmt::Display for PAErr { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self.to_string() { Some(s) => write!(f, "{}", s), None => write!(f, ""), } } } impl Code { /// Converts a `Code` to a human readable string. #[inline] pub fn to_string(self) -> Option { PAErr::from(self).to_string() } } impl std::error::Error for Code {} impl std::fmt::Display for Code { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match (*self).to_string() { Some(s) => write!(f, "{}", s), None => write!(f, ""), } } } impl From for PAErr { #[inline] fn from(c: Code) -> Self { // Error codes are negative, `Code` enum variants are positive PAErr(-(c as i32)) } } impl TryFrom for Code { type Error = (); /// Attempts to convert the wrapped integer error value to a `Code` variant. /// /// Returns `Err(())` if the value cannot be mapped. #[inline] fn try_from(e: PAErr) -> Result { // Error codes are negative, `Code` enum variants are positive let abs = -(e.0); Code::from_i32(abs).ok_or(()) } } /// Check `PAErr` <=> `Code` conversions #[test] fn check_code_paerr_conversions() { assert_eq!(Ok(Code::Ok), Code::try_from(PAErr(0))); assert_eq!(Ok(Code::Access), Code::try_from(PAErr(-1))); assert_eq!(Ok(Code::Command), Code::try_from(PAErr(-2))); assert_eq!(Ok(Code::Invalid), Code::try_from(PAErr(-3))); assert_eq!(Ok(Code::Exist), Code::try_from(PAErr(-4))); assert_eq!(Ok(Code::NoEntity), Code::try_from(PAErr(-5))); assert_eq!(Ok(Code::ConnectionRefused), Code::try_from(PAErr(-6))); assert_eq!(Ok(Code::Protocol), Code::try_from(PAErr(-7))); assert_eq!(Ok(Code::Timeout), Code::try_from(PAErr(-8))); assert_eq!(Ok(Code::AuthKey), Code::try_from(PAErr(-9))); assert_eq!(Ok(Code::Internal), Code::try_from(PAErr(-10))); assert_eq!(Ok(Code::ConnectionTerminated), Code::try_from(PAErr(-11))); assert_eq!(Ok(Code::Killed), Code::try_from(PAErr(-12))); assert_eq!(Ok(Code::InvalidServer), Code::try_from(PAErr(-13))); assert_eq!(Ok(Code::ModInitFailed), Code::try_from(PAErr(-14))); assert_eq!(Ok(Code::BadState), Code::try_from(PAErr(-15))); assert_eq!(Ok(Code::NoData), Code::try_from(PAErr(-16))); assert_eq!(Ok(Code::Version), Code::try_from(PAErr(-17))); assert_eq!(Ok(Code::TooLarge), Code::try_from(PAErr(-18))); assert_eq!(Ok(Code::NotSupported), Code::try_from(PAErr(-19))); assert_eq!(Ok(Code::Unknown), Code::try_from(PAErr(-20))); assert_eq!(Ok(Code::NoExtension), Code::try_from(PAErr(-21))); assert_eq!(Ok(Code::Obsolete), Code::try_from(PAErr(-22))); assert_eq!(Ok(Code::NotImplemented), Code::try_from(PAErr(-23))); assert_eq!(Ok(Code::Forked), Code::try_from(PAErr(-24))); assert_eq!(Ok(Code::IO), Code::try_from(PAErr(-25))); assert_eq!(Ok(Code::Busy), Code::try_from(PAErr(-26))); assert_eq!(Err(()), Code::try_from(PAErr(-27))); assert_eq!(Err(()), Code::try_from(PAErr(1))); assert_eq!(PAErr::from(Code::Ok), PAErr(0)); assert_eq!(PAErr::from(Code::Access), PAErr(-1)); assert_eq!(PAErr::from(Code::Command), PAErr(-2)); assert_eq!(PAErr::from(Code::Invalid), PAErr(-3)); assert_eq!(PAErr::from(Code::Exist), PAErr(-4)); assert_eq!(PAErr::from(Code::NoEntity), PAErr(-5)); assert_eq!(PAErr::from(Code::ConnectionRefused), PAErr(-6)); assert_eq!(PAErr::from(Code::Protocol), PAErr(-7)); assert_eq!(PAErr::from(Code::Timeout), PAErr(-8)); assert_eq!(PAErr::from(Code::AuthKey), PAErr(-9)); assert_eq!(PAErr::from(Code::Internal), PAErr(-10)); assert_eq!(PAErr::from(Code::ConnectionTerminated), PAErr(-11)); assert_eq!(PAErr::from(Code::Killed), PAErr(-12)); assert_eq!(PAErr::from(Code::InvalidServer), PAErr(-13)); assert_eq!(PAErr::from(Code::ModInitFailed), PAErr(-14)); assert_eq!(PAErr::from(Code::BadState), PAErr(-15)); assert_eq!(PAErr::from(Code::NoData), PAErr(-16)); assert_eq!(PAErr::from(Code::Version), PAErr(-17)); assert_eq!(PAErr::from(Code::TooLarge), PAErr(-18)); assert_eq!(PAErr::from(Code::NotSupported), PAErr(-19)); assert_eq!(PAErr::from(Code::Unknown), PAErr(-20)); assert_eq!(PAErr::from(Code::NoExtension), PAErr(-21)); assert_eq!(PAErr::from(Code::Obsolete), PAErr(-22)); assert_eq!(PAErr::from(Code::NotImplemented), PAErr(-23)); assert_eq!(PAErr::from(Code::Forked), PAErr(-24)); assert_eq!(PAErr::from(Code::IO), PAErr(-25)); assert_eq!(PAErr::from(Code::Busy), PAErr(-26)); } libpulse-binding-2.28.1/src/format.rs000064400000000000000000000625011046102023000155760ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Utility functions for handling a stream or sink format. //! //! # Note //! //! Clients using an [`Info`] structure must remember to at least set the encoding attribute, which //! can be done through the [`Info::set_encoding()`] method. use std::os::raw::{c_char, c_void}; use std::ffi::{CStr, CString}; use std::ptr::{null, null_mut}; use std::borrow::Cow; use num_derive::{FromPrimitive, ToPrimitive}; use crate::{sample, channelmap}; use crate::error::PAErr; use crate::proplist::{Proplist, ProplistInternal}; pub use capi::pa_prop_type_t as PropType; /// Represents the type of encoding used in a stream or accepted by a sink. #[repr(C)] #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] #[allow(non_camel_case_types)] pub enum Encoding { /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate (C API) equivalent */ /// Any encoding format, PCM or compressed. Any, /// Any PCM format. PCM, /// AC3 data encapsulated in IEC 61937 header/padding. AC3_IEC61937, /// EAC3 data encapsulated in IEC 61937 header/padding. EAC3_IEC61937, /// MPEG-1 or MPEG-2 (Part 3, not AAC) data encapsulated in IEC 61937 header/padding. MPEG_IEC61937, /// DTS data encapsulated in IEC 61937 header/padding. DTS_IEC61937, /// MPEG-2 AAC data encapsulated in IEC 61937 header/padding. MPEG2_AAC_IEC61937, /// Dolby TrueHD data encapsulated in IEC 61937 header/padding. #[cfg(any(doc, feature = "pa_v13"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))] TRUEHD_IEC61937, /// DTS-HD Master Audio encapsulated in IEC 61937 header/padding. #[cfg(any(doc, feature = "pa_v13"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))] DTSHD_IEC61937, /// Represents an invalid encoding. Invalid = -1, } /// Check is equal to `sys` equivalent #[test] fn enc_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); // Check order and value of variants match // No point checking conversions in both directions since both are a transmute assert_eq!(Encoding::Any, Encoding::from(capi::pa_encoding_t::Any)); assert_eq!(Encoding::PCM, Encoding::from(capi::pa_encoding_t::PCM)); assert_eq!(Encoding::AC3_IEC61937, Encoding::from(capi::pa_encoding_t::AC3_IEC61937)); assert_eq!(Encoding::EAC3_IEC61937, Encoding::from(capi::pa_encoding_t::EAC3_IEC61937)); assert_eq!(Encoding::MPEG_IEC61937, Encoding::from(capi::pa_encoding_t::MPEG_IEC61937)); assert_eq!(Encoding::DTS_IEC61937, Encoding::from(capi::pa_encoding_t::DTS_IEC61937)); assert_eq!(Encoding::MPEG2_AAC_IEC61937, Encoding::from(capi::pa_encoding_t::MPEG2_AAC_IEC61937)); #[cfg(any(doc, feature = "pa_v13"))] assert_eq!(Encoding::TRUEHD_IEC61937, Encoding::from(capi::pa_encoding_t::TRUEHD_IEC61937)); #[cfg(any(doc, feature = "pa_v13"))] assert_eq!(Encoding::DTSHD_IEC61937, Encoding::from(capi::pa_encoding_t::DTSHD_IEC61937)); assert_eq!(Encoding::Invalid, Encoding::from(capi::pa_encoding_t::Invalid)); } impl From for capi::pa_encoding_t { #[inline] fn from(e: Encoding) -> Self { unsafe { std::mem::transmute(e) } } } impl From for Encoding { #[inline] fn from(e: capi::pa_encoding_t) -> Self { unsafe { std::mem::transmute(e) } } } impl Default for Encoding { #[inline(always)] fn default() -> Self { Encoding::Invalid } } /// Represents the format of data provided in a stream or processed by a sink. pub struct Info { /// The actual C object. pub(crate) ptr: *mut InfoInternal, /// Wrapped property list pointer. properties: Proplist, /// Used to avoid freeing the internal object when used as a weak wrapper in callbacks. weak: bool, } unsafe impl Send for Info {} unsafe impl Sync for Info {} /// The raw C structure (with documentation). #[repr(C)] pub(crate) struct InfoInternal { /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */ /// The encoding used for the format. pub encoding: Encoding, /// Additional encoding-specific properties such as sample rate, bitrate, etc. pub list: *mut ProplistInternal, } /// Test size is equal to `sys` equivalent (duplicated here for different documentation) #[test] fn info_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); } impl std::fmt::Debug for Info { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "Info {{ encoding: {:?}, properties: {:?} }}", self.get_encoding(), *self.get_properties()) } } impl Encoding { /// Returns a printable string representing the given encoding type. pub fn to_string(e: Self) -> Option> { let ptr = unsafe { capi::pa_encoding_to_string(e.into()) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }), true => None, } } /// Converts a string of the form returned by [`to_string()`](Self::to_string) back to an /// `Encoding`. #[cfg(any(doc, feature = "pa_v12"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v12")))] pub fn from_string(encoding: &str) -> Self { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_enc = CString::new(encoding.clone()).unwrap(); unsafe { capi::pa_encoding_from_string(c_enc.as_ptr()).into() } } } impl Info { /// Allocates a new `Info` structure. /// /// Clients must initialise at least the encoding field themselves. Returns `None` on failure. pub fn new() -> Option { let ptr = unsafe { capi::pa_format_info_new() }; match ptr.is_null() { false => Some(Self::from_raw(ptr as *mut InfoInternal)), true => None, } } /// Parses a human-readable string of the form generated by [`print()`](Self::print) into an /// `Info` structure. /// /// Returns `None` on failure. pub fn new_from_string(s: &str) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_str = CString::new(s.clone()).unwrap(); let ptr = unsafe { capi::pa_format_info_from_string(c_str.as_ptr()) }; match ptr.is_null() { false => Some(Self::from_raw(ptr as *mut InfoInternal)), true => None, } } /// Utility function to take a [`Spec`] and generate the corresponding `Info`. /// /// Note that if you want the server to choose some of the stream parameters, for example the /// sample rate, so that they match the device parameters, then you shouldn’t use this function. /// In order to allow the server to choose a parameter value, that parameter must be left /// unspecified in the `Info` object, and this function always specifies all parameters. An /// exception is the channel map: if you pass `None` for the channel map, then the channel map /// will be left unspecified, allowing the server to choose it. /// /// Returns `None` on failure. /// /// [`Spec`]: crate::sample::Spec pub fn new_from_sample_spec(ss: &sample::Spec, map: Option<&channelmap::Map>) -> Option { let p_map = map.map_or(null::(), |m| m.as_ref()); let ptr = unsafe { capi::pa_format_info_from_sample_spec(ss.as_ref(), p_map) }; match ptr.is_null() { false => Some(Self::from_raw(ptr as *mut InfoInternal)), true => None, } } /// Creates a new `Info` from an existing [`InfoInternal`] pointer. pub(crate) fn from_raw(ptr: *mut InfoInternal) -> Self { assert_eq!(false, ptr.is_null()); // Note, yes, this should be using `from_raw_weak()`, the ‘free’ function for a format info // object free’s its own proplist! unsafe { Self { ptr: ptr, properties: Proplist::from_raw_weak((*ptr).list), weak: false } } } /// Creates a new `Info` from an existing [`InfoInternal`] pointer. /// /// This is the ‘weak’ version, which avoids destroying the internal object when dropped. pub(crate) fn from_raw_weak(ptr: *mut InfoInternal) -> Self { assert_eq!(false, ptr.is_null()); unsafe { Self { ptr: ptr, properties: Proplist::from_raw_weak((*ptr).list), weak: true } } } /// Checks whether the `Info` structure is valid. #[inline] pub fn is_valid(&self) -> bool { unsafe { capi::pa_format_info_valid(self.ptr as *const capi::pa_format_info) != 0 } } /// Checks whether the `Info` structure represents a PCM (i.e. uncompressed data) format. #[inline] pub fn is_pcm(&self) -> bool { unsafe { capi::pa_format_info_is_pcm(self.ptr as *const capi::pa_format_info) != 0 } } /// Checks whether the format represented by self is a subset of the format represented by /// `with`. /// /// This means that `with` must have all the fields that self does, but the reverse need not be /// true. This is typically expected to be used to check if a stream’s format is compatible with /// a given sink. In such a case, self would be the sink’s format and `with` would be the /// streams. #[inline] pub fn is_compatible_with(&self, with: &Self) -> bool { unsafe { capi::pa_format_info_is_compatible(self.ptr as *const capi::pa_format_info, with.ptr as *const capi::pa_format_info) != 0 } } /// Gets a human-readable string representing the given format. pub fn print(&self) -> String { const PRINT_MAX: usize = capi::PA_FORMAT_INFO_SNPRINT_MAX; let mut tmp = Vec::with_capacity(PRINT_MAX); unsafe { capi::pa_format_info_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.ptr as *const capi::pa_format_info); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } /// Utility function to generate a [`Spec`] and [`Map`] corresponding to a given `Info`. /// /// The conversion for PCM formats is straight-forward. For non-PCM formats, if there is a fixed /// size-time conversion (i.e. all IEC61937-encapsulated formats), a “fake” sample spec whose /// size-time conversion corresponds to this format is provided and the channel map argument is /// ignored. For formats with variable size-time conversion, this function will fail. /// /// [`Spec`]: crate::sample::Spec /// [`Map`]: crate::channelmap::Map pub fn to_sample_spec(&self, ss: &mut sample::Spec, map: &mut channelmap::Map) -> Result<(), PAErr> { match unsafe { capi::pa_format_info_to_sample_spec( self.ptr as *const capi::pa_format_info, ss.as_mut(), map.as_mut()) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Gets the encoding. #[inline] pub fn get_encoding(&self) -> Encoding { unsafe { (*self.ptr).encoding } } /// Sets the encoding attribute. #[inline] pub fn set_encoding(&mut self, encoding: Encoding) { unsafe { (*self.ptr).encoding = encoding }; } /// Gets an immutable reference to the property list. #[inline] pub fn get_properties(&self) -> &Proplist { &self.properties } /// Gets a mutable reference to the property list. #[inline] pub fn get_properties_mut(&mut self) -> &mut Proplist { &mut self.properties } /// Gets the type of property key. pub fn get_prop_type(&self, key: &str) -> PropType { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); unsafe { capi::pa_format_info_get_prop_type(self.ptr as *const capi::pa_format_info, c_key.as_ptr()) } } /// Gets an integer property. pub fn get_prop_int(&self, key: &str) -> Result { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let mut i: i32 = 0; let c_key = CString::new(key.clone()).unwrap(); match unsafe { capi::pa_format_info_get_prop_int(self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut i) } { 0 => Ok(i), e => Err(PAErr(e)), } } /// Gets an integer range property. On success, returns min-max tuple. pub fn get_prop_int_range(&self, key: &str) -> Result<(i32, i32), PAErr> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let mut min: i32 = 0; let mut max: i32 = 0; let c_key = CString::new(key.clone()).unwrap(); match unsafe { capi::pa_format_info_get_prop_int_range( self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut min, &mut max) } { 0 => Ok((min, max)), e => Err(PAErr(e)), } } /// Gets an integer array property. /// /// Returns `None` on error. pub fn get_prop_int_array(&self, key: &str) -> Option> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); let mut count: i32 = 0; let mut p_ints = null_mut::(); let result = unsafe { capi::pa_format_info_get_prop_int_array( self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut p_ints, &mut count) }; if result != 0 { return None; } // Clone each int in the array let mut values: Vec = Vec::with_capacity(count as usize); for i in 0..count { values.push(unsafe { *p_ints.offset(i as isize) }); } // Free the PA allocated array unsafe { capi::pa_xfree(p_ints as *mut c_void) }; // Return vector of ints Some(values) } /// Gets a string property. pub fn get_prop_string(&self, key: &str) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); let mut p_str = null_mut::(); let result = unsafe { capi::pa_format_info_get_prop_string( self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut p_str) }; if result != 0 || p_str.is_null() { return None; } unsafe { let ret = Some(CStr::from_ptr(p_str).to_string_lossy().into_owned()); capi::pa_xfree(p_str as *mut c_void); ret } } /// Gets a string array property. pub fn get_prop_string_array(&self, key: &str) -> Option> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); let mut count: i32 = 0; let mut pp_str = null_mut::<*mut c_char>(); let result = unsafe { capi::pa_format_info_get_prop_string_array( self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut pp_str, &mut count) }; if result != 0 || pp_str.is_null() { return None; } // Clone each string in the array to owned String let mut values: Vec = Vec::with_capacity(count as usize); for i in 0..count { let p_str = unsafe { *pp_str.offset(i as isize) }; if !p_str.is_null() { values.push(unsafe { CStr::from_ptr(p_str).to_string_lossy().into_owned() }); } } // Free all PA internally allocated strings unsafe { capi::pa_format_info_free_string_array(pp_str, count) }; // Return vector of Strings Some(values) } /// Gets the sample format stored in the format info. /// /// Returns `Err` if the sample format property is not set at all, or is invalid. #[cfg(any(doc, feature = "pa_v13"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))] pub fn get_sample_format(&self) -> Result { let mut sf: capi::pa_sample_format_t = capi::PA_SAMPLE_INVALID; match unsafe { capi::pa_format_info_get_sample_format( self.ptr as *const capi::pa_format_info, &mut sf) } { 0 => Ok(crate::sample::Format::from(sf)), e => Err(PAErr(e)), } } /// Gets the sample rate stored in the format info. /// /// Returns `Err` if the sample rate property is not set at all, or is invalid. #[cfg(any(doc, feature = "pa_v13"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))] pub fn get_rate(&self) -> Result { let mut rate: u32 = 0; match unsafe { capi::pa_format_info_get_rate(self.ptr as *const capi::pa_format_info, &mut rate) } { 0 => Ok(rate), e => Err(PAErr(e)), } } /// Gets the channel count stored in the format info. /// /// Returns `Err` if the channels property is not set at all, or is invalid. #[cfg(any(doc, feature = "pa_v13"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))] pub fn get_channel_count(&self) -> Result { let mut channels: u8 = 0; match unsafe { capi::pa_format_info_get_channels(self.ptr as *const capi::pa_format_info, &mut channels) } { 0 => Ok(channels), e => Err(PAErr(e)), } } /// Gets the channel map stored in the format info. /// /// Returns `Err` if the channel map property is not set at all, or if the string form it is /// stored in within the property set fails to parse successfully. #[cfg(any(doc, feature = "pa_v13"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))] pub fn get_channel_map(&self) -> Result { // Returning the entire struct written to here may be a little less efficient than taking a // pointer like the C API, but we avoid the possibility of leaving the user with an // incomplete struct; parsing from string form is inefficient anyway, and we thus should not // expect this to be done frequently anyway. let mut map: capi::pa_channel_map = capi::pa_channel_map::default(); match unsafe { capi::pa_format_info_get_channel_map( self.ptr as *const capi::pa_format_info, &mut map) } { 0 => Ok(crate::channelmap::Map::from(map)), e => Err(PAErr(e)), } } /// Sets an integer property. pub fn set_prop_int(&mut self, key: &str, value: i32) { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); unsafe { capi::pa_format_info_set_prop_int(self.ptr as *mut capi::pa_format_info, c_key.as_ptr(), value); } } /// Sets a property with a list of integer values. pub fn set_prop_int_array(&mut self, key: &str, values: &[i32]) { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); unsafe { capi::pa_format_info_set_prop_int_array(self.ptr as *mut capi::pa_format_info, c_key.as_ptr(), values.as_ptr(), values.len() as i32); } } /// Sets a property which can have any value in a given integer range. pub fn set_prop_int_range(&mut self, key: &str, min: i32, max: i32) { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); unsafe { capi::pa_format_info_set_prop_int_range(self.ptr as *mut capi::pa_format_info, c_key.as_ptr(), min, max); } } /// Sets a string property. pub fn set_prop_string(&mut self, key: &str, value: &str) { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); let c_value = CString::new(value.clone()).unwrap(); unsafe { capi::pa_format_info_set_prop_string(self.ptr as *mut capi::pa_format_info, c_key.as_ptr(), c_value.as_ptr()); } } /// Sets a property with a list of string values. pub fn set_prop_string_array(&mut self, key: &str, values: &[&str]) { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); let mut c_values: Vec = Vec::with_capacity(values.len()); for v in values { c_values.push(CString::new(*v).unwrap()); } // Capture array of pointers to the above CString values let mut c_value_ptrs: Vec<*const c_char> = Vec::with_capacity(c_values.len()); for v in &c_values { c_value_ptrs.push(v.as_ptr()); } unsafe { capi::pa_format_info_set_prop_string_array(self.ptr as *mut capi::pa_format_info, c_key.as_ptr(), c_value_ptrs.as_ptr(), c_value_ptrs.len() as i32); } } /// Convenience method to set the sample format as a property. /// /// Note for PCM: If the sample format is left unspecified in the `Info` object, then the server /// will select the stream sample format. In that case the stream sample format will most likely /// match the device sample format, meaning that sample format conversion will be avoided. #[inline] pub fn set_sample_format(&mut self, sf: sample::Format) { unsafe { capi::pa_format_info_set_sample_format(self.ptr as *mut capi::pa_format_info, sf.into()); } } /// Convenience method to set the sampling rate as a property. /// /// Note for PCM: If the sample rate is left unspecified in the `Info` object, then the server /// will select the stream sample rate. In that case the stream sample rate will most likely /// match the device sample rate, meaning that sample rate conversion will be avoided. #[inline] pub fn set_rate(&mut self, rate: i32) { unsafe { capi::pa_format_info_set_rate(self.ptr as *mut capi::pa_format_info, rate) } } /// Convenience method to set the number of channels as a property. /// /// Note for PCM: If the channel count is left unspecified in the `Info` object, then the server /// will select the stream channel count. In that case the stream channel count will most likely /// match the device channel count, meaning that up/downmixing will be avoided. #[inline] pub fn set_channels(&mut self, channels: u32) { debug_assert!(channels <= std::i32::MAX as u32); unsafe { capi::pa_format_info_set_channels(self.ptr as *mut capi::pa_format_info, channels as i32) } } /// Convenience method to set the channel map as a property. /// /// Note for PCM: If the channel map is left unspecified in the `Info` object, then the server /// will select the stream channel map. In that case the stream channel map will most likely /// match the device channel map, meaning that remixing will be avoided. #[inline] pub fn set_channel_map(&mut self, map: &channelmap::Map) { unsafe { capi::pa_format_info_set_channel_map(self.ptr as *mut capi::pa_format_info, map.as_ref()) } } } impl Drop for Info { fn drop(&mut self) { if !self.weak { unsafe { capi::pa_format_info_free(self.ptr as *mut capi::pa_format_info) }; } } } impl Clone for Info { /// Returns a new `Info` struct and representing the same format. If this is called on a ‘weak’ /// instance, a non-weak object is returned. fn clone(&self) -> Self { let ptr = unsafe { capi::pa_format_info_copy(self.ptr as *const capi::pa_format_info) }; assert_eq!(false, ptr.is_null()); Self::from_raw(ptr as *mut InfoInternal) } } libpulse-binding-2.28.1/src/lib.rs000064400000000000000000000234421046102023000150550ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! A binding for the PulseAudio system library (`libpulse`). //! //! # About //! //! This binding enables Rust projects to make use of the [PulseAudio] client system library. It //! builds upon the [separate raw FFI crate][sys] to provide a more “Rusty” interface. //! //! The documentation below and throughout this crate has been largely copied (under fair-use terms) //! from those in the PulseAudio C API header files, and adjusted where appropriate to fit any //! differences, thus it should not be too unfamiliar to those of you already familiar with the C //! API. //! //!
//!

//! //! Warning //!

//!

//! The PulseAudio API, even in this Rust-ified form, is not the easiest thing to understand how to //! make use of. Furthermore, the somewhat complex underlying C API imposes certain limitations upon //! just how safe and simple an interface this binding can reasonably offer. One particularly //! notable example is the threaded mainloop locking mechanism, which uses a perfectly legitimate //! design, but one that happens to conflict with what is typically used in Rust code; it does not //! fit perfectly with the Rust borrow checking mechanism and thus you cannot rely upon the borrow //! checker to prevent unsafe use as much as is typical. //!

//!
//! //! # Introduction //! //! The PulseAudio API comes in two flavours to accommodate different styles of applications and //! different needs in complexity: //! //! * The complete but somewhat complicated to use asynchronous API. //! * The simplified, easy to use, but limited synchronous API. //! //! ## Simple API //! //! Use this if you develop your program in synchronous style and just need a way to play or record //! data on the sound server. This functionality is kept in the separate [`libpulse-simple-binding`] //! crate. See that for details. //! //! ## Asynchronous API //! //! Use this if you develop your programs in asynchronous, event loop based style or if you want to //! use the advanced features of the PulseAudio API. A guide can be found in the //! [`mainloop`](mod@mainloop) module. //! //! By using the built-in threaded main loop, it is possible to achieve a pseudo-synchronous API, //! which can be useful in synchronous applications where the simple API is insufficient. //! //! ## Threads //! //! The PulseAudio client libraries are not designed to be directly thread-safe. They are however //! designed to be re-entrant and thread-aware. //! //! To use the libraries in a threaded environment, you must assure that all objects are only used //! in one thread at a time. Normally, this means that all objects belonging to a single context //! must be accessed from the same thread. //! //! The included main loop implementation is also not thread safe. Take care to make sure event //! objects are not manipulated when any other code is using the main loop. //! //! Note that some objects implement the `Sync` trait, despite not truly being thread-safe. The //! reason for this is that when the threaded mainloop is used (the most common one), its lock //! provides the thread safety, and when such types are used behind that lock, they are then `Sync` //! safe. If you use the standard mainloop though, then you would have to add an `Arc` wrapper to //! make them safe. Not having `Sync` would force the threaded mainloop case to require unnecessary //! and undesirable `Arc` wrappers. This is an unfortunate compromise resulting from the //! complication of needing to support multiple mainloop designs, and the threaded mainloop design //! being built upon a non-wrapper lock that is not typical of Rust code. //! //! ## Logging //! //! You can configure different logging parameters for the PulseAudio client libraries. The //! following environment variables are recognized: //! //! * `PULSE_LOG`: Maximum log level required. Bigger values result in a more verbose logging //! output. The following values are recognized: //! * `0`: Error messages //! * `1`: Warning messages //! * `2`: Notice messages //! * `3`: Info messages //! * `4`: Debug messages //! * `PULSE_LOG_SYSLOG`: If defined, force all client libraries to log their output using the //! `syslog(3)` mechanism. Default behavior is to log all output to `stderr`. //! * `PULSE_LOG_JOURNAL`: If defined, force all client libraries to log their output using the //! systemd journal. If both `PULSE_LOG_JOURNAL` and `PULSE_LOG_SYSLOG` are defined, logging to //! the systemd journal takes a higher precedence. Each message originating library file name and //! function are included by default through the journal fields `CODE_FILE`, `CODE_FUNC`, and //! `CODE_LINE`. Any backtrace attached to the logging message is sent through the //! PulseAudio-specific journal field `PULSE_BACKTRACE`. This environment variable has no effect //! if PulseAudio was compiled without systemd journal support. //! * `PULSE_LOG_COLORS`: If defined, enables colored logging output. //! * `PULSE_LOG_TIME`: If defined, include timestamps with each message. //! * `PULSE_LOG_FILE`: If defined, include each message originating file name. //! * `PULSE_LOG_META`: If defined, include each message originating file name and path relative to //! the PulseAudio source tree root. //! * `PULSE_LOG_LEVEL`: If defined, include a log level prefix with each message. Respectively, the //! prefixes "E", "W", "N", "I", "D" stands for Error, Warning, Notice, Info, and Debugging. //! * `PULSE_LOG_BACKTRACE`: Number of functions to display in the backtrace. If this variable is //! not defined, or has a value of zero, no backtrace is shown. //! * `PULSE_LOG_BACKTRACE_SKIP`: Number of backtrace levels to skip, from the function printing the //! log message downwards. //! * `PULSE_LOG_NO_RATE_LIMIT`: If defined, do not rate limit the logging output. Rate limiting //! skips certain log messages when their frequency is considered too high. //! //! # Usage //! //! Start by adding a dependency on the crate in your program’s `Cargo.toml` file. Note that it is //! recommended that you rename the crate such that you can refer to it by a shorter name within //! your code (such as `pulse`, as used within examples within this crate’s documentation). Such //! renaming can be done [within your `Cargo.toml` file][rename] with cargo version 1.31 or newer, //! or otherwise with `extern crate` statements. //! //! See sub-modules for further information. //! //!
//!

//! //! Warning //!

//!

//! It is important that you read the COMPATIBILITY.md guide available in the //! code repository to understand the //! topic of compatibility with different versions of PulseAudio. //!

//!
//! //! [sys]: https://docs.rs/libpulse-sys //! [`libpulse-simple-binding`]: https://docs.rs/libpulse-simple-binding //! [PulseAudio]: https://en.wikipedia.org/wiki/PulseAudio //! [rename]: https://doc.rust-lang.org/1.31.0/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml //! [code repository]: https://github.com/jnqnfe/pulse-binding-rust #![doc( html_logo_url = "https://github.com/jnqnfe/pulse-binding-rust/raw/master/logo.svg", html_favicon_url = "https://github.com/jnqnfe/pulse-binding-rust/raw/master/favicon.ico" )] #![warn(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg))] extern crate libpulse_sys as capi; pub mod callbacks; pub mod channelmap; pub mod context; pub mod def; pub mod direction; pub mod error; pub mod format; pub mod mainloop; pub mod operation; pub mod proplist; pub mod sample; pub mod stream; pub mod time; pub mod utf8; pub mod util; pub mod version; pub mod volume; libpulse-binding-2.28.1/src/mainloop/api.rs000064400000000000000000000435621046102023000167030ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Main loop abstraction layer API. use std::os::raw::c_void; use std::rc::Rc; use libc::timeval; use crate::def; use super::events; use super::events::io::{IoEvent, IoEventRef, IoEventInternal, FlagSet as IoEventFlagSet}; use super::events::timer::{TimeEvent, TimeEventRef, TimeEventInternal}; use super::events::deferred::{DeferEvent, DeferEventRef, DeferEventInternal}; use crate::time::{UnixTs, MonotonicTs, Timeval, MicroSeconds}; use crate::callbacks::{get_su_capi_params, get_su_callback}; pub(crate) use capi::pa_mainloop_api as ApiInternal; /// This enables generic type enforcement with the opaque C objects. pub trait MainloopInternalType {} /// This enables generic type enforcement with MainloopInner objects, and describes mandatory /// accessors for the internal pointers, allowing access to these pointers across the generic /// implementations to work. pub trait MainloopInnerType { /// Internal mainloop type. type I: MainloopInternalType; /// Create a new instance #[inline(always)] unsafe fn new(ptr: *mut Self::I, api: *const MainloopApi, dropfn: fn(&mut MainloopInner), supports_rtclock: bool) -> MainloopInner:: { MainloopInner:: { ptr, api, dropfn, supports_rtclock } } /// Return opaque main loop object pointer. /// /// **Warning**: The pointer is only valid for the lifetime of this object. fn get_ptr(&self) -> *mut Self::I; /// Return raw API object pointer. /// /// **Warning**: The pointer is only valid for the lifetime of this object. fn get_api_ptr(&self) -> *const MainloopApi; /// Return main loop API object pointer. fn get_api(&self) -> &MainloopApi; /// Returns `true` if the mainloop implementation supports monotonic based time events. fn supports_rtclock(&self) -> bool; } /// Mainloop inner wrapper. /// /// This contains the actual main loop object pointers, holding both the pointer to the actual /// opaque main loop C object, and the pointer to the associated API vtable. /// /// An instance of this type will be held, in an `Rc` ref counted wrapper both in an outer Mainloop /// wrapper, and by all event objects. With event objects holding a ref-counted copy, this both /// gives event objects access to the API pointer, which they need, and also it allows us to ensure /// that event objects do not outlive the main loop object (which internally owns the API object), /// and thus ensures correct destruction order of event and main loop objects. pub struct MainloopInner where T: MainloopInternalType { /// An opaque main loop object. ptr: *mut T, /// The abstract main loop API vtable for the GLIB main loop object. No need to free this API as /// it is owned by the loop and is destroyed when the loop is freed. api: *const MainloopApi, /// All implementations must provide a drop method, to be called from an actual drop call, which /// should free the mainloop object. dropfn: fn(&mut MainloopInner), /// Whether or not the implementation supports monotonic based time events. (`true` if so). supports_rtclock: bool, } impl Drop for MainloopInner where T: MainloopInternalType { fn drop(&mut self) { (self.dropfn)(self); self.ptr = std::ptr::null_mut::< as MainloopInnerType>::I>(); self.api = std::ptr::null::(); } } /// This is the actual implementation of the ‘inner type’ trait. /// /// It is not possible to replace this with ‘default’ method implementations within the trait itself /// since the trait does not know about the existence of the struct attributes being accessed. impl MainloopInnerType for MainloopInner where T: MainloopInternalType { type I = T; /// Gets opaque main loop object pointer. /// /// **Warning**: The pointer is only valid for the lifetime of this object. #[inline(always)] fn get_ptr(&self) -> *mut T { self.ptr } /// Gets raw API object pointer. /// /// **Warning**: The pointer is only valid for the lifetime of this object. #[inline(always)] fn get_api_ptr(&self) -> *const MainloopApi { self.api } /// Gets main loop API object pointer. #[inline(always)] fn get_api(&self) -> &MainloopApi { assert!(!self.api.is_null()); unsafe { &*self.api } } #[inline(always)] fn supports_rtclock(&self) -> bool { self.supports_rtclock } } /// Mainloop trait, to be implemented by the different types of mainloops. pub trait Mainloop { /// Inner mainloop type. type MI: MainloopInnerType; /// Get inner mainloop. fn inner(&self) -> Rc; /// Creates a new IO event. /// /// **Note**: You must ensure that the returned event object lives for as long as you want its /// event(s) to fire, as its `Drop` implementation destroys the event source. I.e. if you create /// a new event, but then immediately drop the object returned here, no event will fire! /// /// The given callback must accept three parameters, an [`IoEventRef`] object, a copy of the /// given file descriptor, and an event flag set, indicating the event(s) that occurred. The /// [`DeferEventRef`] object gives you some opportunity to manage the event source from within /// it’s callback execution. fn new_io_event(&mut self, fd: i32, events: IoEventFlagSet, mut callback: Box, i32, IoEventFlagSet) + 'static>) -> Option> { let inner_for_wrapper = self.inner(); let wrapper_cb = Box::new(move |ptr, fd, flags| { let ref_obj = IoEventRef::::from_raw(ptr, Rc::clone(&inner_for_wrapper)); callback(ref_obj, fd, flags); }); let to_save = events::io::EventCb::new(Some(wrapper_cb)); let (cb_fn, cb_data) = to_save.get_capi_params(events::io::event_cb_proxy); let inner = self.inner(); let api = inner.get_api(); let fn_ptr = api.io_new.unwrap(); let ptr = fn_ptr(api, fd, events, cb_fn, cb_data); match ptr.is_null() { false => Some(IoEvent::::from_raw(ptr, Rc::clone(&inner), to_save)), true => None, } } /// Creates a new timer event. /// /// **Note**: You must ensure that the returned event object lives for as long as you want its /// event(s) to fire, as its `Drop` implementation destroys the event source. I.e. if you create /// a new event, but then immediately drop the object returned here, no event will fire! /// /// The callback must take a [`TimeEventRef`] object, which gives you some opportunity to /// manage the event source from within it’s callback execution. /// /// Example event set to fire in five seconds time: /// /// ```rust,ignore /// use libpulse_binding::time::{UnixTs, MicroSeconds}; /// let _t_event = mainloop.new_timer_event( /// &(UnixTs::now() + MicroSeconds::from_secs(5).unwrap()), /// Box::new(|_| { println!("Timer event fired!"); })); /// ``` fn new_timer_event(&mut self, tv: &UnixTs, mut callback: Box) + 'static>) -> Option> { let inner_for_wrapper = self.inner(); let wrapper_cb = Box::new(move |ptr| { let ref_obj = TimeEventRef::::from_raw(ptr, Rc::clone(&inner_for_wrapper)); callback(ref_obj); }); let to_save = events::timer::EventCb::new(Some(wrapper_cb)); let (cb_fn, cb_data) = to_save.get_capi_params(events::timer::event_cb_proxy); let inner = self.inner(); let api = inner.get_api(); let fn_ptr = api.time_new.unwrap(); let ptr = fn_ptr(api, &(tv.0).0, cb_fn, cb_data); match ptr.is_null() { false => Some(TimeEvent::::from_raw(ptr, Rc::clone(&inner), to_save)), true => None, } } /// Creates a new monotonic-based timer event. /// /// Asserts that `t` is not `MicroSeconds::INVALID`. /// /// This is an alternative to the `new_timer_event` method, taking a monotonic based time value. /// /// **Note**: You must ensure that the returned event object lives for as long as you want its /// event(s) to fire, as its `Drop` implementation destroys the event source. I.e. if you create /// a new event, but then immediately drop the object returned here, no event will fire! /// /// The callback must take a [`TimeEventRef`] object, which gives you some opportunity to /// manage the event source from within it’s callback execution. /// /// Example event set to fire in five seconds time: /// /// ```rust,ignore /// use libpulse_binding::time::{MonotonicTs, MicroSeconds}; /// let _t_event = mainloop.new_timer_event_rt( /// MonotonicTs::now() + MicroSeconds::from_secs(5).unwrap(), /// Box::new(|_| { println!("Timer event fired!"); })); /// ``` fn new_timer_event_rt(&mut self, t: MonotonicTs, mut callback: Box) + 'static>) -> Option> { assert_ne!(t.0, MicroSeconds::INVALID); let inner_for_wrapper = self.inner(); let wrapper_cb = Box::new(move |ptr| { let ref_obj = TimeEventRef::::from_raw(ptr, Rc::clone(&inner_for_wrapper)); callback(ref_obj); }); let to_save = events::timer::EventCb::new(Some(wrapper_cb)); let (cb_fn, cb_data) = to_save.get_capi_params(events::timer::event_cb_proxy); let inner = self.inner(); let mut tv = Timeval::new_zero(); tv.set_rt(t.0, inner.supports_rtclock()); let api = inner.get_api(); let fn_ptr = api.time_new.unwrap(); let ptr = fn_ptr(api, &tv.0, cb_fn, cb_data); match ptr.is_null() { false => Some(TimeEvent::::from_raw(ptr, Rc::clone(&inner), to_save)), true => None, } } /// Creates a new deferred event. /// /// **Note**: You must ensure that the returned event object lives for as long as you want its /// event(s) to fire, as its `Drop` implementation destroys the event source. I.e. if you create /// a new event, but then immediately drop the object returned here, no event will fire! /// /// The callback must take a [`DeferEventRef`] object, which gives you some opportunity to /// manage the event source from within it’s callback execution. fn new_deferred_event(&mut self, mut callback: Box) + 'static>) -> Option> { let inner_for_wrapper = self.inner(); let wrapper_cb = Box::new(move |ptr| { let ref_obj = DeferEventRef::::from_raw(ptr, Rc::clone(&inner_for_wrapper)); callback(ref_obj); }); let to_save = events::deferred::EventCb::new(Some(wrapper_cb)); let (cb_fn, cb_data) = to_save.get_capi_params(events::deferred::event_cb_proxy); let inner = self.inner(); let api = inner.get_api(); let fn_ptr = api.defer_new.unwrap(); let ptr = fn_ptr(api, cb_fn, cb_data); match ptr.is_null() { false => Some(DeferEvent::::from_raw(ptr, Rc::clone(&inner), to_save)), true => None, } } /// Runs the specified callback once from the main loop using an anonymous defer event. /// /// If the mainloop runs in a different thread, you need to follow the mainloop implementation’s /// rules regarding how to safely create defer events. In particular, if you’re using /// [`mainloop::threaded`](mod@crate::mainloop::threaded), you must lock the mainloop before /// calling this function. fn once_event(&mut self, callback: Box) { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(Some(callback), once_cb_proxy); let inner = self.inner(); let api = inner.get_api(); unsafe { capi::pa_mainloop_api_once(api.as_ref(), cb_fn, cb_data) }; } /// Calls quit fn quit(&mut self, retval: def::Retval) { let inner = self.inner(); let api = inner.get_api(); let fn_ptr = api.quit.unwrap(); fn_ptr(api, retval.0); } } /// An IO event callback prototype. pub type IoEventCb = extern "C" fn(a: *const MainloopApi, e: *mut IoEventInternal, fd: i32, events: IoEventFlagSet, userdata: *mut c_void); /// A IO event destroy callback prototype. pub type IoEventDestroyCb = extern "C" fn(a: *const MainloopApi, e: *mut IoEventInternal, userdata: *mut c_void); /// A time event callback prototype. pub type TimeEventCb = extern "C" fn(a: *const MainloopApi, e: *mut TimeEventInternal, tv: *const timeval, userdata: *mut c_void); /// A time event destroy callback prototype. pub type TimeEventDestroyCb = extern "C" fn(a: *const MainloopApi, e: *mut TimeEventInternal, userdata: *mut c_void); /// A defer event callback prototype. pub type DeferEventCb = extern "C" fn(a: *const MainloopApi, e: *mut DeferEventInternal, userdata: *mut c_void); /// A defer event destroy callback prototype. pub type DeferEventDestroyCb = extern "C" fn(a: *const MainloopApi, e: *mut DeferEventInternal, userdata: *mut c_void); /// An abstract mainloop API vtable #[repr(C)] pub struct MainloopApi { /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */ /// A pointer to some private, arbitrary data of the main loop implementation. pub userdata: *mut c_void, /// Creates a new IO event source object. pub io_new: Option, userdata: *mut c_void) -> *mut IoEventInternal>, /// Enables or disables IO events on this object. pub io_enable: Option, /// Frees a IO event source object. pub io_free: Option, /// Sets a function that is called when the IO event source is destroyed. Use this to free the /// `userdata` argument if required. pub io_set_destroy: Option)>, /// Creates a new timer event source object for the specified Unix time. pub time_new: Option, userdata: *mut c_void) -> *mut TimeEventInternal>, /// Restarts a running or expired timer event source with a new Unix time. pub time_restart: Option, /// Frees a deferred timer event source object. pub time_free: Option, /// Sets a function that is called when the timer event source is destroyed. Use this to free /// the `userdata` argument if required. pub time_set_destroy: Option)>, /// Creates a new deferred event source object. pub defer_new: Option, userdata: *mut c_void) -> *mut DeferEventInternal>, /// Enables or disables a deferred event source temporarily. pub defer_enable: Option, /// Frees a deferred event source object. pub defer_free: Option, /// Sets a function that is called when the deferred event source is /// destroyed. Use this to free the `userdata` argument if required. pub defer_set_destroy: Option)>, /// Exits the main loop and return the specified retval. pub quit: Option, } /// Test size is equal to `sys` equivalent (duplicated here for different documentation) #[test] fn api_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); } impl AsRef for MainloopApi { #[inline] fn as_ref(&self) -> &capi::pa_mainloop_api { unsafe { &*(self as *const Self as *const capi::pa_mainloop_api) } } } impl<'a> From<*const ApiInternal> for &'a MainloopApi { #[inline] fn from(a: *const ApiInternal) -> Self { unsafe { std::mem::transmute(a) } } } impl<'a> From<&'a MainloopApi> for *const ApiInternal { #[inline] fn from(a: &'a MainloopApi) -> Self { unsafe { std::mem::transmute(a) } } } /// Proxy for anonymous ‘once’ deferred event callbacks. /// /// Warning: This is for single-use cases only! It destroys the actual closure callback. extern "C" fn once_cb_proxy(_: *const ApiInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = get_su_callback::(userdata); (callback)(); }); } libpulse-binding-2.28.1/src/mainloop/events/deferred.rs000064400000000000000000000071351046102023000212120ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Main loop deferred events. use std::os::raw::c_void; use std::rc::Rc; use crate::mainloop::api::{MainloopApi, MainloopInnerType}; use crate::callbacks::MultiUseCallback; pub use capi::pa_defer_event as DeferEventInternal; /// A deferred event source. pub struct DeferEvent where T: MainloopInnerType { /// Internal object pointer ptr: *mut DeferEventInternal, /// Source mainloop. owner: Rc, /// Saved callback closure, for later destruction. _saved_cb: EventCb, } /// A reference to a deferred event source, provided to the callback, allowing modification within /// the callback itself. pub struct DeferEventRef where T: MainloopInnerType { /// Internal object pointer ptr: *mut DeferEventInternal, /// Source mainloop. owner: Rc, } pub(crate) type EventCb = MultiUseCallback; impl DeferEvent where T: MainloopInnerType { #[inline] pub(crate) fn from_raw(ptr: *mut DeferEventInternal, mainloop_inner: Rc, callback: EventCb) -> Self { assert_eq!(false, ptr.is_null()); Self { ptr: ptr, owner: mainloop_inner, _saved_cb: callback } } /// Enables this event source temporarily. #[inline] pub fn enable(&mut self) { let fn_ptr = (*self.owner).get_api().defer_enable.unwrap(); fn_ptr(self.ptr, 1); } /// Disables this event source temporarily. #[inline] pub fn disable(&mut self) { let fn_ptr = (*self.owner).get_api().defer_enable.unwrap(); fn_ptr(self.ptr, 0); } } impl DeferEventRef where T: MainloopInnerType { #[inline] pub(crate) fn from_raw(ptr: *mut DeferEventInternal, mainloop_inner: Rc) -> Self { assert_eq!(false, ptr.is_null()); Self { ptr: ptr, owner: mainloop_inner } } /// Enables this event source temporarily. #[inline] pub fn enable(&mut self) { let fn_ptr = (*self.owner).get_api().defer_enable.unwrap(); fn_ptr(self.ptr, 1); } /// Disables this event source temporarily. #[inline] pub fn disable(&mut self) { let fn_ptr = (*self.owner).get_api().defer_enable.unwrap(); fn_ptr(self.ptr, 0); } } impl Drop for DeferEvent where T: MainloopInnerType { fn drop(&mut self) { let fn_ptr = (*self.owner).get_api().defer_free.unwrap(); fn_ptr(self.ptr); } } /// Proxy for the event callback. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. pub(crate) extern "C" fn event_cb_proxy(_: *const MainloopApi, e: *mut DeferEventInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = EventCb::get_callback(userdata); (callback)(e); }); } libpulse-binding-2.28.1/src/mainloop/events/io.rs000064400000000000000000000103721046102023000200360ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Main loop IO events. use std::os::raw::c_void; use std::rc::Rc; use bitflags::bitflags; use crate::mainloop::api::{MainloopApi, MainloopInnerType}; use crate::callbacks::MultiUseCallback; pub use capi::pa_io_event as IoEventInternal; bitflags! { /// IO event flag set. #[repr(transparent)] pub struct FlagSet: u32 { /// No event. const NULL = capi::PA_IO_EVENT_NULL; /// Input event. const INPUT = capi::PA_IO_EVENT_INPUT; /// Output event. const OUTPUT = capi::PA_IO_EVENT_OUTPUT; /// Hangup event. const HANGUP = capi::PA_IO_EVENT_HANGUP; /// Error event. const ERROR = capi::PA_IO_EVENT_ERROR; } } /// Flags for [`FlagSet`]. #[deprecated(since = "2.20.0", note = "Use the associated constants on `FlagSet`.")] pub mod flags { use super::FlagSet; /// No event. pub const NULL: FlagSet = FlagSet::NULL; /// Input event. pub const INPUT: FlagSet = FlagSet::INPUT; /// Output event. pub const OUTPUT: FlagSet = FlagSet::OUTPUT; /// Hangup event. pub const HANGUP: FlagSet = FlagSet::HANGUP; /// Error event. pub const ERROR: FlagSet = FlagSet::ERROR; } /// An IO event source pub struct IoEvent where T: MainloopInnerType { /// Internal object pointer ptr: *mut IoEventInternal, /// Source mainloop. owner: Rc, /// Saved callback closure, for later destruction. _saved_cb: EventCb, } /// A reference to an IO event source, provided to the callback, allowing modification within the /// callback itself. pub struct IoEventRef where T: MainloopInnerType { /// Internal object pointer ptr: *mut IoEventInternal, /// Source mainloop. owner: Rc, } pub(crate) type EventCb = MultiUseCallback; impl IoEvent where T: MainloopInnerType { #[inline] pub(crate) fn from_raw(ptr: *mut IoEventInternal, mainloop_inner: Rc, callback: EventCb) -> Self { assert_eq!(false, ptr.is_null()); Self { ptr: ptr, owner: mainloop_inner, _saved_cb: callback } } /// Enables or disables IO events on this object. #[inline] pub fn enable(&mut self, events: FlagSet) { let fn_ptr = (*self.owner).get_api().io_enable.unwrap(); fn_ptr(self.ptr, events); } } impl IoEventRef where T: MainloopInnerType { #[inline] pub(crate) fn from_raw(ptr: *mut IoEventInternal, mainloop_inner: Rc) -> Self { assert_eq!(false, ptr.is_null()); Self { ptr: ptr, owner: mainloop_inner } } /// Enables or disables IO events on this object. #[inline] pub fn enable(&mut self, events: FlagSet) { let fn_ptr = (*self.owner).get_api().io_enable.unwrap(); fn_ptr(self.ptr, events); } } impl Drop for IoEvent where T: MainloopInnerType { fn drop(&mut self) { let fn_ptr = (*self.owner).get_api().io_free.unwrap(); fn_ptr(self.ptr); } } /// Proxy for the event callback. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. pub(crate) extern "C" fn event_cb_proxy(_: *const MainloopApi, e: *mut IoEventInternal, fd: i32, events: FlagSet, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = EventCb::get_callback(userdata); (callback)(e, fd, events); }); } libpulse-binding-2.28.1/src/mainloop/events/mod.rs000064400000000000000000000013711046102023000202050ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Main loop events. pub mod io; pub mod timer; pub mod deferred; libpulse-binding-2.28.1/src/mainloop/events/timer.rs000064400000000000000000000112451046102023000205470ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Main loop timer events. //! //! # Notes //! //! Time events may be created (or reset) with either Unix time values or real-time (monotonic) //! based values (though if the mainloop does not support monotonic time value, they may be silently //! converted to unix time). //! //! Note that time events created with one form of time value can be freely restarted with the other //! form of time value. use std::os::raw::c_void; use std::rc::Rc; use libc::timeval; use crate::mainloop::api::{MainloopApi, MainloopInnerType}; use crate::time::{UnixTs, MonotonicTs, Timeval, MicroSeconds}; use crate::callbacks::MultiUseCallback; pub use capi::pa_time_event as TimeEventInternal; /// A timer event source pub struct TimeEvent where T: MainloopInnerType { /// Internal object pointer ptr: *mut TimeEventInternal, /// Source mainloop. owner: Rc, /// Saved callback closure, for later destruction. _saved_cb: EventCb, } /// A reference to a timer event source, provided to the callback, allowing modification within the /// callback itself. pub struct TimeEventRef where T: MainloopInnerType { /// Internal object pointer ptr: *mut TimeEventInternal, /// Source mainloop owner: Rc, } pub(crate) type EventCb = MultiUseCallback; impl TimeEvent where T: MainloopInnerType { #[inline] pub(crate) fn from_raw(ptr: *mut TimeEventInternal, mainloop_inner: Rc, callback: EventCb) -> Self { assert_eq!(false, ptr.is_null()); Self { ptr: ptr, owner: mainloop_inner, _saved_cb: callback } } /// Restarts this timer event source (whether still running or already expired) with a new Unix /// time. #[inline] pub fn restart(&mut self, t: &UnixTs) { let fn_ptr = (*self.owner).get_api().time_restart.unwrap(); fn_ptr(self.ptr, &(t.0).0); } /// Restarts this timer event source (whether still running or already expired) with a new /// monotonic time. pub fn restart_rt(&mut self, t: MonotonicTs) { assert_ne!(t.0, MicroSeconds::INVALID); let mut tv = Timeval::new_zero(); tv.set_rt(t.0, (*self.owner).supports_rtclock()); let fn_ptr = (*self.owner).get_api().time_restart.unwrap(); fn_ptr(self.ptr, &tv.0); } } impl TimeEventRef where T: MainloopInnerType { pub(crate) fn from_raw(ptr: *mut TimeEventInternal, mainloop_inner: Rc) -> Self { assert_eq!(false, ptr.is_null()); Self { ptr: ptr, owner: mainloop_inner } } /// Restarts this timer event source (whether still running or already expired) with a new Unix /// time. #[inline] pub fn restart(&mut self, t: &UnixTs) { let fn_ptr = (*self.owner).get_api().time_restart.unwrap(); fn_ptr(self.ptr, &(t.0).0); } /// Restarts this timer event source (whether still running or already expired) with a new /// monotonic time. pub fn restart_rt(&mut self, t: MonotonicTs) { assert_ne!(t.0, MicroSeconds::INVALID); let mut tv = Timeval::new_zero(); tv.set_rt(t.0, (*self.owner).supports_rtclock()); let fn_ptr = (*self.owner).get_api().time_restart.unwrap(); fn_ptr(self.ptr, &tv.0); } } impl Drop for TimeEvent where T: MainloopInnerType { fn drop(&mut self) { let fn_ptr = (*self.owner).get_api().time_free.unwrap(); fn_ptr(self.ptr); } } /// Proxy for the event callback. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. pub(crate) extern "C" fn event_cb_proxy(_: *const MainloopApi, e: *mut TimeEventInternal, _: *const timeval, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = EventCb::get_callback(userdata); (callback)(e); }); } libpulse-binding-2.28.1/src/mainloop/mod.rs000064400000000000000000000123711046102023000167030ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Main loop abstraction layer. //! //! # Main Loop Abstraction //! //! Both the PulseAudio core and the PulseAudio client library use a main loop abstraction layer. //! Due to this it is possible to embed PulseAudio into other applications easily. //! //! This abstraction contains three basic elements: //! //! * Deferred events: Events that will trigger as soon as possible. Note that some implementations //! may block all other events when a deferred event is active. //! * I/O events: Events that trigger on file descriptor activities. //! * Timer events: Events that trigger after a fixed amount of time. //! //! The abstraction is represented as a number of function pointers in the //! [`MainloopApi`](self::api::MainloopApi) structure. //! //! To actually be able to use these functions, an implementation needs to be coupled to the //! abstraction. There are three of these shipped with PulseAudio, but any other can be used with a //! minimal amount of work, provided it supports the three basic events listed above. //! //! The implementations shipped with PulseAudio are: //! //! * [Standard](mod@standard): A minimal but fast implementation based on the C library’s poll() //! function. //! * [Threaded](mod@threaded): A special version of the previous implementation where all of //! PulseAudio’s internal handling runs in a separate thread. //! * ‘Glib’: A wrapper around GLib’s main loop. This is provided in the separate //! `libpulse_glib_binding` crate. //! //! UNIX signals may be hooked to a main loop using the functionality from the //! [`signal`](mod@signal) mod. This relies only on the main loop abstraction and can therefore be //! used with any of the implementations. //! //! # Callback Notes //! //! ## Execution //! //! As described in the [standard mainloop] documentation], there are three phases to mainloop //! execution, and the third - ‘dispatch’ - is when user callbacks get executed. //! //! It is important to understand that while it is *typical* that user callbacks are executed //! by the mainloop’s dispatcher, callback execution is not exclusively done there; in some cases //! callbacks get executed directly in synchronous function execution. For instance, if you set up //! a context state change callback, then try to connect the context object, execution of the //! ‘connect’ function call involves (internally within the PulseAudio client library) direct //! execution of this callback in setting the initial connection state. After returning, the //! callback is then on only executed asynchronously from the mainloop’s dispatcher. //! //! While execution using the [standard mainloop] is entirely synchronous, the [threaded mainloop] //! implementation runs the standard mainloop in a separate thread and callback execution occurs //! asynchronously, requiring careful use of the mainloop’s [`lock()`] method. When writing //! callbacks with the threaded mainloop, users must beware the potential that in a few cases the //! callback may be executed in two different scenarios, and with different threads. Note that the //! threaded mainloop has an [`in_thread()`] method for determining whether or not the thread it is //! executed from is the special event loop thread. //! //! ## Queued Events and Changing Callbacks //! //! It is also worth understanding that any events that get queued for dispatch do **not** hold //! cached copies of user callback parameters. Where applicable, you can thus freely and safely //! change the set callback, with that change taking effect immediately to all future event //! dispatching. //! //! ## Threading and `Rc` //! //! Normally when holding multiple references to objects across threads in Rust you would use an //! [`Arc`] wrapper. However, with the [threaded mainloop], you may be able to get away with using //! just an `Rc` wrapper. Remember that with the [threaded mainloop] you **must** use its //! [`lock()`] method to synchronise access to objects, and so you know that at any one moment //! either your thread (when you take the lock) **or** the event loop thread hold the lock, never //! both, and thus only one thread is ever working with objects at any one time, and since Rust //! actually has no idea that more than one thread is involved (hidden in the C library’s //! implementation), you can safely get away with using `Rc`. //! //! [standard mainloop]: mod@standard //! [threaded mainloop]: mod@self::threaded //! [`lock()`]: self::threaded::Mainloop::lock //! [`in_thread()`]: self::threaded::Mainloop::in_thread //! [`Arc`]: std::sync::Arc pub mod api; pub mod events; pub mod signal; pub mod standard; pub mod threaded; libpulse-binding-2.28.1/src/mainloop/signal.rs000064400000000000000000000067141046102023000174050ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! UNIX signal support for main loops. //! //! # Overview //! //! In contrast to other main loop event sources such as timer and IO events, UNIX signal support //! requires modification of the global process environment. Due to this the generic main loop //! abstraction layer as defined in [`mainloop::api`](mod@crate::mainloop::api) doesn’t have direct //! support for UNIX signals. However, you may hook signal support into an abstract main loop via //! the routines defined herein. use std::os::raw::c_void; use std::ptr::null_mut; use capi::pa_signal_event as EventInternal; use crate::error::PAErr; use crate::callbacks::MultiUseCallback; use crate::mainloop::api::{ApiInternal, MainloopInnerType, Mainloop as MainloopTrait}; /// An opaque UNIX signal event source object. /// /// Note: Saves a copy of the closure callbacks, which it frees on drop. pub struct Event { /// The actual C object. ptr: *mut EventInternal, /// Saved multi-use state callback closure, for later destruction. _signal_cb: SignalCb, } type SignalCb = MultiUseCallback; /// Trait with signal handling, for mainloops. pub trait MainloopSignals : MainloopTrait { /// Initializes the UNIX signal subsystem and bind it to the specified main loop. fn init_signals(&mut self) -> Result<(), PAErr> { let inner = self.inner(); let api = inner.get_api(); match unsafe { capi::pa_signal_init(api.into()) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Cleans up the signal subsystem. #[inline] fn signals_done(&self) { unsafe { capi::pa_signal_done(); } } } impl Event { /// Creates a new UNIX signal event source object. /// /// The callback must accept an integer which represents the signal. pub fn new(sig: i32, callback: F) -> Self where F: FnMut(i32) + 'static { let saved = SignalCb::new(Some(Box::new(callback))); let (cb_fn, cb_data) = saved.get_capi_params(signal_cb_proxy); let ptr = unsafe { capi::pa_signal_new(sig, cb_fn, cb_data) }; Self { ptr: ptr, _signal_cb: saved } } } impl Drop for Event { fn drop(&mut self) { unsafe { capi::pa_signal_free(self.ptr) }; self.ptr = null_mut::(); } } /// Proxy for signal callbacks. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn signal_cb_proxy(_api: *const ApiInternal, _e: *mut EventInternal, sig: i32, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = SignalCb::get_callback(userdata); (callback)(sig); }); } libpulse-binding-2.28.1/src/mainloop/standard.rs000064400000000000000000000374711046102023000177340ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Standard/minimal main loop implementation based on poll(). //! //! # Overview //! //! This ‘standard’ (minimal) main loop implementation is based on the poll() system call. It //! supports the functions defined in the main loop abstraction //! ([`mainloop::api`](mod@crate::mainloop::api)) and very little else. //! //! This implementation is thread safe as long as you access the main loop object from a single //! thread only. //! //! # Usage //! //! A [`Mainloop`] is created using [`Mainloop::new()`]. To get access to the main loop abstraction, //! [`Mainloop::get_api()`] is used. //! //! Destruction of the [`Mainloop`] object is done automatically when the object falls out of scope. //! (Rust’s `Drop` trait has been implemented and takes care of it). //! //! # Iteration //! //! The main loop is designed around the concept of iterations. Each iteration consists of three //! steps that repeat during the application’s entire lifetime: //! //! * Prepare - Build a list of file descriptors that need to be monitored and calculate the next //! timeout. //! * Poll - Execute the actual poll() system call. //! * Dispatch - Dispatch any events that have fired. //! //! When using the main loop, the application can either execute each iteration, one at a time, //! using [`Mainloop::iterate()`], or let the library iterate automatically using //! [`Mainloop::run()`]. //! //! # Threads //! //! The main loop functions are designed to be thread safe, but the objects are not. What this means //! is that multiple main loops can be used, but only one object per thread. //! //! # Example //! //! An example program using the standard mainloop: //! //! ```rust //! extern crate libpulse_binding as pulse; //! //! use std::sync::atomic; //! use std::rc::Rc; //! use std::cell::RefCell; //! use std::ops::Deref; //! use pulse::mainloop::standard::Mainloop; //! use pulse::context::{Context, FlagSet as ContextFlagSet}; //! use pulse::stream::{Stream, FlagSet as StreamFlagSet}; //! use pulse::sample::{Spec, Format}; //! use pulse::proplist::Proplist; //! use pulse::mainloop::standard::IterateResult; //! use pulse::def::Retval; //! //! fn main() { //! let spec = Spec { //! format: Format::S16NE, //! channels: 2, //! rate: 44100, //! }; //! assert!(spec.is_valid()); //! //! let mut proplist = Proplist::new().unwrap(); //! proplist.set_str(pulse::proplist::properties::APPLICATION_NAME, "FooApp") //! .unwrap(); //! //! let mut mainloop = Rc::new(RefCell::new(Mainloop::new() //! .expect("Failed to create mainloop"))); //! //! let mut context = Rc::new(RefCell::new(Context::new_with_proplist( //! mainloop.borrow().deref(), //! "FooAppContext", //! &proplist //! ).expect("Failed to create new context"))); //! //! context.borrow_mut().connect(None, ContextFlagSet::NOFLAGS, None) //! .expect("Failed to connect context"); //! //! // Wait for context to be ready //! loop { //! match mainloop.borrow_mut().iterate(false) { //! IterateResult::Quit(_) | //! IterateResult::Err(_) => { //! eprintln!("Iterate state was not success, quitting..."); //! return; //! }, //! IterateResult::Success(_) => {}, //! } //! match context.borrow().get_state() { //! pulse::context::State::Ready => { break; }, //! pulse::context::State::Failed | //! pulse::context::State::Terminated => { //! eprintln!("Context state failed/terminated, quitting..."); //! return; //! }, //! _ => {}, //! } //! } //! //! let mut stream = Rc::new(RefCell::new(Stream::new( //! &mut context.borrow_mut(), //! "Music", //! &spec, //! None //! ).expect("Failed to create new stream"))); //! //! stream.borrow_mut().connect_playback(None, None, StreamFlagSet::START_CORKED, //! None, None).expect("Failed to connect playback"); //! //! // Wait for stream to be ready //! loop { //! match mainloop.borrow_mut().iterate(false) { //! IterateResult::Quit(_) | //! IterateResult::Err(_) => { //! eprintln!("Iterate state was not success, quitting..."); //! return; //! }, //! IterateResult::Success(_) => {}, //! } //! match stream.borrow().get_state() { //! pulse::stream::State::Ready => { break; }, //! pulse::stream::State::Failed | //! pulse::stream::State::Terminated => { //! eprintln!("Stream state failed/terminated, quitting..."); //! return; //! }, //! _ => {}, //! } //! } //! //! // Our main logic (to output a stream of audio data) //! # let mut count = 0; // For automatic unit tests, we’ll spin a few times //! let drained = Rc::new(RefCell::new(false)); //! loop { //! match mainloop.borrow_mut().iterate(false) { //! IterateResult::Quit(_) | //! IterateResult::Err(_) => { //! eprintln!("Iterate state was not success, quitting..."); //! return; //! }, //! IterateResult::Success(_) => {}, //! } //! //! // Write some data with stream.write() //! //! if stream.borrow().is_corked().unwrap() { //! stream.borrow_mut().uncork(None); //! } //! //! // Wait for our data to be played //! let _o = { //! let drain_state_ref = Rc::clone(&drained); //! stream.borrow_mut().drain(Some(Box::new(move |_success: bool| { //! *drain_state_ref.borrow_mut() = true; //! }))) //! }; //! while *drained.borrow_mut() == false { //! match mainloop.borrow_mut().iterate(false) { //! IterateResult::Quit(_) | //! IterateResult::Err(_) => { //! eprintln!("Iterate state was not success, quitting..."); //! return; //! }, //! IterateResult::Success(_) => {}, //! } //! } //! *drained.borrow_mut() = false; //! //! // Remember to break out of the loop once done writing all data (or whatever). //! # //! # // Hack: Stop test getting stuck in infinite loop! //! # count += 1; //! # if count == 3 { //! # break; //! # } //! } //! //! // Clean shutdown //! mainloop.borrow_mut().quit(Retval(0)); // uncertain whether this is necessary //! stream.borrow_mut().disconnect().unwrap(); //! } //! ``` use std::os::raw::{c_ulong, c_void}; use std::rc::Rc; #[cfg(not(windows))] use libc::pollfd; #[cfg(windows)] use winapi::um::winsock2::WSAPOLLFD as pollfd; use crate::def; use crate::error::{Code as ErrCode, PAErr}; use crate::mainloop::api::{MainloopInternalType, MainloopInner, MainloopInnerType, MainloopApi, Mainloop as MainloopTrait}; use crate::mainloop::signal::MainloopSignals; use crate::time::MicroSeconds; pub use capi::pa_mainloop as MainloopInternal; impl MainloopInternalType for MainloopInternal {} /// Generic prototype of a poll() like function. pub type PollFn = extern "C" fn(ufds: *mut pollfd, nfds: c_ulong, timeout: i32, userdata: *mut c_void) -> i32; /// Return type for [`Mainloop::iterate()`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum IterateResult { /// Success, with number of sources dispatched. Success(u32), /// Quit was called, with quit’s retval. Quit(def::Retval), /// An error occurred, with error value. Err(PAErr), } impl IterateResult { /// Checks if the result is a `Success` value (returns `true` if so). #[inline] pub fn is_success(&self) -> bool { match *self { IterateResult::Success(_) => true, _ => false, } } /// Checks if the result is a `Quit` value (returns `true` if so). #[inline] pub fn is_quit(&self) -> bool { match *self { IterateResult::Quit(_) => true, _ => false, } } /// Checks` if the result is an `Error` value (returns `true` if so). #[inline] pub fn is_error(&self) -> bool { match *self { IterateResult::Err(_) => true, _ => false, } } } /// This acts as a safe interface to the internal PA Mainloop. /// /// The mainloop object pointers are further enclosed here in a ref counted wrapper, allowing this /// outer wrapper to have clean methods for creating event objects, which can cleanly pass a copy of /// the inner ref counted mainloop object to them. Giving this to events serves two purposes, /// firstly because they need the API pointer, secondly, it ensures that event objects do not /// outlive the mainloop object. pub struct Mainloop { /// The ref-counted inner data. pub _inner: Rc>, } impl MainloopTrait for Mainloop { type MI = MainloopInner; #[inline(always)] fn inner(&self) -> Rc> { Rc::clone(&self._inner) } } impl MainloopSignals for Mainloop {} impl MainloopInner { #[inline(always)] fn drop_actual(&mut self) { unsafe { capi::pa_mainloop_free(self.get_ptr()) }; } } impl Mainloop { /// Allocates a new main loop object. pub fn new() -> Option { let ptr = unsafe { capi::pa_mainloop_new() }; if ptr.is_null() { return None; } let api_ptr = unsafe { capi::pa_mainloop_get_api(ptr) }; assert!(!api_ptr.is_null()); let ml_inner = unsafe { MainloopInner::::new(ptr, std::mem::transmute(api_ptr), MainloopInner::::drop_actual, true) }; Some(Self { _inner: Rc::new(ml_inner) }) } /// Prepares for a single iteration of the main loop. /// /// Returns `Err` on error or exit request. /// /// `timeout` specifies a maximum timeout for the subsequent poll. `None` requests blocking /// behaviour. /// /// Note, should the microseconds timeout value provided be too large to pass to the underlying /// C API (larger than [`std::i32::MAX`]), then the [`PAErr`] form of the [`Code::TooLarge`] /// error will be returned (within [`Result::Err`]). /// /// [`Code::TooLarge`]: crate::error::Code::TooLarge pub fn prepare(&mut self, timeout: Option) -> Result<(), PAErr> { let t: i32 = match timeout { // A negative value represents a request for 'blocking' behaviour in the C API None => -1, // This is just in case we ever changed `MicroSeconds` to hold unsigned values #[allow(unused_comparisons)] Some(MicroSeconds(i)) if i < 0 => unreachable!(), // Check value is no larger than i32::MAX considering API takes an i32 Some(MicroSeconds(i)) if i <= std::i32::MAX as u64 => i as i32, // If larger, we must error _ => return Err((ErrCode::TooLarge).into()), }; match unsafe { capi::pa_mainloop_prepare(self._inner.get_ptr(), t) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Executes the previously prepared poll. pub fn poll(&mut self) -> Result { match unsafe { capi::pa_mainloop_poll(self._inner.get_ptr()) } { e if e >= 0 => Ok(e as u32), e => Err(PAErr(e)), } } /// Dispatchs timeout, IO and deferred events from the previously executed poll. /// /// On success returns the number of source dispatched. pub fn dispatch(&mut self) -> Result { match unsafe { capi::pa_mainloop_dispatch(self._inner.get_ptr()) } { e if e >= 0 => Ok(e as u32), e => Err(PAErr(e)), } } /// Gets the return value as specified with the main loop’s [`quit()`](Self::quit) routine. #[inline] pub fn get_retval(&self) -> def::Retval { def::Retval(unsafe { capi::pa_mainloop_get_retval(self._inner.get_ptr()) }) } /// Runs a single iteration of the main loop. /// /// This is a convenience function for [`prepare()`], [`poll()`] and [`dispatch()`]. /// /// If `block` is `true`, block for events if none are queued. /// /// Returns an [`IterateResult`] variant: /// /// * On success, returns `IterateResult::Success` containing the number of sources dispatched /// in this iteration. /// * If exit was requested, returns `IterateResult::Quit` containing quit’s retval. /// * On error, returns `IterateResult::Err` containing error value. /// /// [`prepare()`]: Self::prepare /// [`poll()`]: Self::poll /// [`dispatch()`]: Self::dispatch pub fn iterate(&mut self, block: bool) -> IterateResult { let mut retval: i32 = 0; match unsafe { capi::pa_mainloop_iterate(self._inner.get_ptr(), block as i32, &mut retval) } { r if r >= 0 => IterateResult::Success(r as u32), -2 => IterateResult::Quit(def::Retval(retval)), e => IterateResult::Err(PAErr(e)), } } /// Runs unlimited iterations of the main loop object until the main loop’s /// [`quit()`](Self::quit) routine is called. /// /// On success, returns `Ok` containing quit’s return value. On error returns `Err` containing a /// tuple of the error value and quit’s return value. pub fn run(&mut self) -> Result { let mut retval: i32 = 0; match unsafe { capi::pa_mainloop_run(self._inner.get_ptr(), &mut retval) } { r if r >= 0 => Ok(def::Retval(retval)), r => Err((PAErr(r), def::Retval(retval))), } } /// Gets the abstract main loop abstraction layer vtable for this main loop. /// /// No need to free the API as it is owned by the loop and is destroyed when the loop is freed. /// /// Talking to PA directly with C requires fetching this pointer explicitly via this function. /// This is actually unnecessary through this binding. The pointer is retrieved automatically /// upon Mainloop creation, stored internally, and automatically obtained from it by functions /// that need it. #[inline] pub fn get_api<'a>(&self) -> &'a MainloopApi { let ptr = self._inner.get_api_ptr(); assert_eq!(false, ptr.is_null()); unsafe { &*ptr } } /// Shuts down the main loop with the specified return value. #[inline] pub fn quit(&mut self, retval: def::Retval) { unsafe { capi::pa_mainloop_quit(self._inner.get_ptr(), retval.0); } } /// Interrupts a running poll (for threaded systems). #[inline] pub fn wakeup(&mut self) { unsafe { capi::pa_mainloop_wakeup(self._inner.get_ptr()); } } /// Changes the poll() implementation. #[inline] pub fn set_poll_func(&mut self, poll_cb: (PollFn, *mut c_void)) { unsafe { capi::pa_mainloop_set_poll_func(self._inner.get_ptr(), Some(poll_cb.0), poll_cb.1); } } } libpulse-binding-2.28.1/src/mainloop/threaded.rs000064400000000000000000000531241046102023000177050ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! A variation of the standard main loop implementation, using a background thread. //! //! # Overview //! //! The threaded main loop implementation is a special version of the standard main loop //! implementation. For the basic design, see the standard main loop documentation //! ([`mainloop::standard`](mod@crate::mainloop::standard)). //! //! The added feature in the threaded main loop is that it spawns a new thread that runs the real //! main loop in the background. This allows a synchronous application to use the asynchronous API //! without risking stalling the PulseAudio library. A few synchronization primitives are available //! to access the objects attached to the event loop safely. //! //! # Creation //! //! A [`Mainloop`] object is created using [`Mainloop::new()`]. This will only allocate the required //! structures though, so to use it the thread must also be started. This is done through //! [`Mainloop::start()`], after which you can start using the main loop. //! //! # Destruction //! //! When the PulseAudio connection has been terminated, the thread must be stopped and the //! resources freed. Stopping the thread is done using [`Mainloop::stop()`], which must be called //! without the lock (see below) held. When that function returns, the thread is stopped and the //! [`Mainloop`] object can be destroyed. //! //! Destruction of the [`Mainloop`] object is done automatically when the object falls out of scope. //! (Rust’s `Drop` trait has been implemented and takes care of it). //! //! # Locking //! //! Since the PulseAudio API doesn’t allow concurrent accesses to objects, a locking scheme must be //! used to guarantee safe usage. The threaded main loop API provides such a scheme through the //! functions [`Mainloop::lock()`] and [`Mainloop::unlock()`]. //! //! The lock is recursive, so it’s safe to use it multiple times from the same thread. Just make //! sure you call [`Mainloop::unlock()`] the same number of times you called [`Mainloop::lock()`]. //! //! The lock needs to be held whenever you call any PulseAudio function that uses an object //! associated with this main loop. Those objects include the mainloop, context, stream and //! operation objects, and the various event objects (io, time, defer). Make sure you do not hold on //! to the lock more than necessary though, as the threaded main loop stops while the lock is held. //! //! Example: //! //! ```rust,no_run //! extern crate libpulse_binding as pulse; //! //! use std::rc::Rc; //! use std::cell::RefCell; //! use pulse::mainloop::threaded::Mainloop; //! use pulse::stream::{Stream, State}; //! //! fn check_stream(m: Rc>, s: Rc>) { //! m.borrow_mut().lock(); //! //! let state = s.borrow().get_state(); //! //! m.borrow_mut().unlock(); //! //! match state { //! State::Ready => { println!("Stream is ready!"); }, //! _ => { println!("Stream is not ready!"); }, //! } //! } //! ``` //! //! # Callbacks //! //! Callbacks in PulseAudio are asynchronous, so they require extra care when using them together //! with a threaded main loop. //! //! The easiest way to turn the callback based operations into synchronous ones, is to simply wait //! for the callback to be called and continue from there. This is the approach chosen in //! PulseAudio’s threaded API. //! //! ## Basic callbacks //! //! For the basic case, where all that is required is to wait for the callback to be invoked, the //! code should look something like this: //! //! Example: //! //! ```rust,no_run //! extern crate libpulse_binding as pulse; //! //! use std::rc::Rc; //! use std::cell::RefCell; //! use pulse::mainloop::threaded::Mainloop; //! use pulse::operation::State; //! use pulse::stream::Stream; //! //! fn drain_stream(m: Rc>, s: Rc>) { //! m.borrow_mut().lock(); //! //! // Drain //! let o = { //! let ml_ref = Rc::clone(&m); //! s.borrow_mut().drain(Some(Box::new(move |_success: bool| { //! unsafe { (*ml_ref.as_ptr()).signal(false); } //! }))) //! }; //! while o.get_state() != pulse::operation::State::Done { //! m.borrow_mut().wait(); //! } //! //! m.borrow_mut().unlock(); //! } //! ``` //! //! The function `drain_stream` will wait for the callback to be called using [`Mainloop::wait()`]. //! //! If your application is multi-threaded, then this waiting must be done inside a while loop. The //! reason for this is that multiple threads might be using [`Mainloop::wait()`] at the same time. //! Each thread must therefore verify that it was its callback that was invoked. Also the underlying //! OS synchronization primitives are usually not free of spurious wake-ups, so a //! [`Mainloop::wait()`] must be called within a loop even if you have only one thread waiting. //! //! The callback `my_drain_callback` indicates to the main function that it has been called using //! [`Mainloop::signal()`]. //! //! As you can see, [`Mainloop::wait()`] may only be called with the lock held. The same thing is //! true for [`Mainloop::signal()`], but as the lock is held before the callback is invoked, you do //! not have to deal with that. //! //! The functions will not dead lock because the wait function will release the lock before waiting //! and then regrab it once it has been signalled. For those of you familiar with threads, the //! behaviour is that of a condition variable. //! //! ## Data callbacks //! //! For many callbacks, simply knowing that they have been called is insufficient. The callback also //! receives some data that is desired. To access this data safely, we must extend our example a //! bit: //! //! ```rust,no_run //! extern crate libpulse_binding as pulse; //! //! use std::rc::Rc; //! use std::cell::RefCell; //! use std::sync::atomic::{AtomicBool, Ordering}; //! use pulse::mainloop::threaded::Mainloop; //! use pulse::stream::Stream; //! //! // A data structure to capture all our data in (currently just a pointer to a bool) //! struct DrainCbData(*mut bool); //! //! fn drain_stream(m: Rc>, s: Rc>) { //! m.borrow_mut().lock(); //! //! // For guarding against spurious wakeups //! // Possibly also needed for memory flushing and ordering control //! let mut guard = Rc::new(RefCell::new(AtomicBool::new(true))); //! //! let mut data: Rc>> = Rc::new(RefCell::new(None)); //! //! // Drain //! let o = { //! let ml_ref = Rc::clone(&m); //! let guard_ref = Rc::clone(&guard); //! let data_ref = Rc::clone(&data); //! s.borrow_mut().drain(Some(Box::new(move |mut success: bool| { //! unsafe { //! *data_ref.as_ptr() = Some(DrainCbData(&mut success)); //! (*guard_ref.as_ptr()).store(false, Ordering::Release); //! (*ml_ref.as_ptr()).signal(true); //! } //! }))) //! }; //! while guard.borrow().load(Ordering::Acquire) { //! m.borrow_mut().wait(); //! } //! //! assert!(!data.borrow().is_none()); //! let success = unsafe { *(data.borrow_mut().take().unwrap().0) }; //! //! // Allow callback to continue now //! m.borrow_mut().accept(); //! //! match success { //! false => { println!("Bitter defeat..."); }, //! true => { println!("Success!"); }, //! } //! //! m.borrow_mut().unlock(); //! } //! ``` //! //! The example is a bit silly as it would have been more simple to just copy the contents of //! `success`, but for larger data structures this can be wasteful. //! //! The difference here compared to the basic callback is the value `true` passed to //! [`Mainloop::signal()`] and the call to [`Mainloop::accept()`]. What will happen is that //! [`Mainloop::signal()`] will signal the main function and then wait. The main function is then //! free to use the data in the callback until [`Mainloop::accept()`] is called, which will allow //! the callback to continue. //! //! Note that [`Mainloop::accept()`] must be called some time between exiting the while loop and //! unlocking the main loop! Failure to do so will result in a race condition. I.e. it is not okay //! to release the lock and regrab it before calling [`Mainloop::accept()`]. //! //! ## Asynchronous callbacks //! //! PulseAudio also has callbacks that are completely asynchronous, meaning that they can be called //! at any time. The threaded main loop API provides the locking mechanism to handle concurrent //! accesses, but nothing else. Applications will have to handle communication from the callback to //! the main program through their own mechanisms. //! //! The callbacks that are completely asynchronous are: //! //! * State callbacks for contexts, streams, etc. //! * Subscription notifications. //! //! # Example //! //! An example program using the threaded mainloop: //! //! ```rust //! extern crate libpulse_binding as pulse; //! //! use std::rc::Rc; //! use std::cell::RefCell; //! use std::ops::Deref; //! use pulse::mainloop::threaded::Mainloop; //! use pulse::context::{Context, FlagSet as ContextFlagSet}; //! use pulse::stream::{Stream, FlagSet as StreamFlagSet}; //! use pulse::sample::{Spec, Format}; //! use pulse::proplist::Proplist; //! use pulse::mainloop::api::Mainloop as MainloopTrait; //Needs to be in scope //! //! fn main() { //! let spec = Spec { //! format: Format::S16NE, //! channels: 2, //! rate: 44100, //! }; //! assert!(spec.is_valid()); //! //! let mut proplist = Proplist::new().unwrap(); //! proplist.set_str(pulse::proplist::properties::APPLICATION_NAME, "FooApp") //! .unwrap(); //! //! let mut mainloop = Rc::new(RefCell::new(Mainloop::new() //! .expect("Failed to create mainloop"))); //! //! let mut context = Rc::new(RefCell::new(Context::new_with_proplist( //! mainloop.borrow().deref(), //! "FooAppContext", //! &proplist //! ).expect("Failed to create new context"))); //! //! // Context state change callback //! { //! let ml_ref = Rc::clone(&mainloop); //! let context_ref = Rc::clone(&context); //! context.borrow_mut().set_state_callback(Some(Box::new(move || { //! let state = unsafe { (*context_ref.as_ptr()).get_state() }; //! match state { //! pulse::context::State::Ready | //! pulse::context::State::Failed | //! pulse::context::State::Terminated => { //! unsafe { (*ml_ref.as_ptr()).signal(false); } //! }, //! _ => {}, //! } //! }))); //! } //! //! context.borrow_mut().connect(None, ContextFlagSet::NOFLAGS, None) //! .expect("Failed to connect context"); //! //! mainloop.borrow_mut().lock(); //! mainloop.borrow_mut().start().expect("Failed to start mainloop"); //! //! // Wait for context to be ready //! loop { //! match context.borrow().get_state() { //! pulse::context::State::Ready => { break; }, //! pulse::context::State::Failed | //! pulse::context::State::Terminated => { //! eprintln!("Context state failed/terminated, quitting..."); //! mainloop.borrow_mut().unlock(); //! mainloop.borrow_mut().stop(); //! return; //! }, //! _ => { mainloop.borrow_mut().wait(); }, //! } //! } //! context.borrow_mut().set_state_callback(None); //! //! let mut stream = Rc::new(RefCell::new(Stream::new( //! &mut context.borrow_mut(), //! "Music", //! &spec, //! None //! ).expect("Failed to create new stream"))); //! //! // Stream state change callback //! { //! let ml_ref = Rc::clone(&mainloop); //! let stream_ref = Rc::clone(&stream); //! stream.borrow_mut().set_state_callback(Some(Box::new(move || { //! let state = unsafe { (*stream_ref.as_ptr()).get_state() }; //! match state { //! pulse::stream::State::Ready | //! pulse::stream::State::Failed | //! pulse::stream::State::Terminated => { //! unsafe { (*ml_ref.as_ptr()).signal(false); } //! }, //! _ => {}, //! } //! }))); //! } //! //! stream.borrow_mut().connect_playback(None, None, StreamFlagSet::START_CORKED, //! None, None).expect("Failed to connect playback"); //! //! // Wait for stream to be ready //! loop { //! match stream.borrow().get_state() { //! pulse::stream::State::Ready => { break; }, //! pulse::stream::State::Failed | //! pulse::stream::State::Terminated => { //! eprintln!("Stream state failed/terminated, quitting..."); //! mainloop.borrow_mut().unlock(); //! mainloop.borrow_mut().stop(); //! return; //! }, //! _ => { mainloop.borrow_mut().wait(); }, //! } //! } //! stream.borrow_mut().set_state_callback(None); //! //! mainloop.borrow_mut().unlock(); //! //! // Our main logic (to output a stream of audio data) //! # let mut count = 0; // For automatic unit tests, we’ll spin a few times //! loop { //! mainloop.borrow_mut().lock(); //! //! // Write some data with stream.write() //! //! if stream.borrow().is_corked().unwrap() { //! stream.borrow_mut().uncork(None); //! } //! //! // Drain //! let o = { //! let ml_ref = Rc::clone(&mainloop); //! stream.borrow_mut().drain(Some(Box::new(move |_success: bool| { //! unsafe { (*ml_ref.as_ptr()).signal(false); } //! }))) //! }; //! while o.get_state() != pulse::operation::State::Done { //! mainloop.borrow_mut().wait(); //! } //! //! mainloop.borrow_mut().unlock(); //! //! // If done writing data, call `mainloop.borrow_mut().stop()` (with lock released), then //! // break! //! # //! # // Hack: Stop test getting stuck in infinite loop! //! # count += 1; //! # if count == 3 { //! # mainloop.borrow_mut().stop(); //! # break; //! # } //! } //! //! // Clean shutdown //! mainloop.borrow_mut().lock(); //! stream.borrow_mut().disconnect().unwrap(); //! mainloop.borrow_mut().unlock(); //! } //! ``` use std::rc::Rc; use std::ffi::CString; use crate::def; use crate::error::PAErr; use crate::mainloop::api::{MainloopInternalType, MainloopInner, MainloopInnerType, MainloopApi, Mainloop as MainloopTrait}; use crate::mainloop::signal::MainloopSignals; pub use capi::pa_threaded_mainloop as MainloopInternal; impl MainloopInternalType for MainloopInternal {} /// This acts as a safe interface to the internal PA Mainloop. /// /// The mainloop object pointers are further enclosed here in a ref counted wrapper, allowing this /// outer wrapper to have clean methods for creating event objects, which can cleanly pass a copy of /// the inner ref counted mainloop object to them. Giving this to events serves two purposes, /// firstly because they need the API pointer, secondly, it ensures that event objects do not /// outlive the mainloop object. pub struct Mainloop { /// The ref-counted inner data. pub _inner: Rc>, } impl MainloopTrait for Mainloop { type MI = MainloopInner; #[inline(always)] fn inner(&self) -> Rc> { Rc::clone(&self._inner) } } impl MainloopSignals for Mainloop {} impl MainloopInner { #[inline(always)] fn drop_actual(&mut self) { unsafe { capi::pa_threaded_mainloop_free(self.get_ptr()) }; } } impl Mainloop { /// Allocates a new threaded main loop object. /// /// You have to call [`start()`](Self::start) before the event loop thread starts running. pub fn new() -> Option { let ptr = unsafe { capi::pa_threaded_mainloop_new() }; if ptr.is_null() { return None; } let api_ptr = unsafe { capi::pa_threaded_mainloop_get_api(ptr) }; assert!(!api_ptr.is_null()); let ml_inner = unsafe { MainloopInner::::new(ptr, std::mem::transmute(api_ptr), MainloopInner::::drop_actual, true) }; Some(Self { _inner: Rc::new(ml_inner) }) } /// Starts the event loop thread. pub fn start(&mut self) -> Result<(), PAErr> { match unsafe { capi::pa_threaded_mainloop_start(self._inner.get_ptr()) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Terminates the event loop thread cleanly. /// /// Make sure to unlock the mainloop object before calling this function. #[inline] pub fn stop(&mut self) { unsafe { capi::pa_threaded_mainloop_stop(self._inner.get_ptr()); } } /// Locks the event loop object, effectively blocking the event loop thread from processing /// events. /// /// You can use this to enforce exclusive access to all objects attached to the event loop. This /// lock is recursive. This function may not be called inside the event loop thread. Events that /// are dispatched from the event loop thread are executed with this lock held. #[inline] pub fn lock(&mut self) { assert!(!self.in_thread(), "lock() can not be called from within the event loop thread!"); unsafe { capi::pa_threaded_mainloop_lock(self._inner.get_ptr()); } } /// Unlocks the event loop object, inverse of [`lock()`](Self::lock). #[inline] pub fn unlock(&mut self) { unsafe { capi::pa_threaded_mainloop_unlock(self._inner.get_ptr()); } } /// Waits for an event to be signalled by the event loop thread. /// /// You can use this to pass data from the event loop thread to the main thread in a /// synchronized fashion. This function may not be called inside the event loop thread. Prior to /// this call the event loop object needs to be locked using [`lock()`]. While waiting the lock /// will be released. Immediately before returning it will be acquired again. This function may /// spuriously wake up even without [`signal()`] being called. You need to make sure to handle /// that! /// /// [`lock()`]: Self::lock /// [`signal()`]: Self::signal #[inline] pub fn wait(&mut self) { unsafe { capi::pa_threaded_mainloop_wait(self._inner.get_ptr()); } } /// Signals all threads waiting for a signalling event in [`wait()`]. /// /// If `wait_for_accept` is `true`, do not return before the signal was accepted by an /// [`accept()`] call. While waiting for that condition the event loop object is unlocked. /// /// [`wait()`]: Self::wait /// [`accept()`]: Self::accept #[inline] pub fn signal(&mut self, wait_for_accept: bool) { unsafe { capi::pa_threaded_mainloop_signal(self._inner.get_ptr(), wait_for_accept as i32); } } /// Accepts a signal from the event thread issued with [`signal()`]. /// /// This call should only be used in conjunction with [`signal()`] with `wait_for_accept` as /// `true`. /// /// [`signal()`]: Self::signal #[inline] pub fn accept(&mut self) { unsafe { capi::pa_threaded_mainloop_accept(self._inner.get_ptr()); } } /// Gets the return value as specified with the main loop’s `quit` routine (used internally by /// threaded mainloop). #[inline] pub fn get_retval(&self) -> def::Retval { def::Retval(unsafe { capi::pa_threaded_mainloop_get_retval(self._inner.get_ptr()) }) } /// Gets the main loop abstraction layer vtable for this main loop. /// /// There is no need to free this object as it is owned by the loop and is destroyed when the /// loop is freed. /// /// Talking to PA directly with C requires fetching this pointer explicitly via this function. /// This is actually unnecessary through this binding. The pointer is retrieved automatically /// upon Mainloop creation, stored internally, and automatically obtained from it by functions /// that need it. #[inline] pub fn get_api<'a>(&self) -> &'a MainloopApi { let ptr = self._inner.get_api_ptr(); assert_eq!(false, ptr.is_null()); unsafe { &*ptr } } /// Checks whether or not we are in the event loop thread (returns `true` if so). #[inline] pub fn in_thread(&self) -> bool { unsafe { capi::pa_threaded_mainloop_in_thread(self._inner.get_ptr()) != 0 } } /// Sets the name of the thread. pub fn set_name(&mut self, name: &str) { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); unsafe { capi::pa_threaded_mainloop_set_name(self._inner.get_ptr(), c_name.as_ptr()); } } } libpulse-binding-2.28.1/src/operation.rs000064400000000000000000000124241046102023000163050ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Asynchronous operations. use std::os::raw::c_void; use std::ptr::null_mut; use crate::callbacks; use capi::pa_operation as OperationInternal; pub use capi::pa_operation_state_t as State; /// An asynchronous operation object. /// /// Note: Saves a copy of active multi-use closure callbacks, which it frees on drop. pub struct Operation { /// The actual C object. ptr: *mut OperationInternal, /// The operation’s associated closure callback. /// This is a copy of the callback userdata pointer given in the C API function call that /// generated the operation instance (except not cast to void). It is saved here in case the /// user tries to cancel execution of the callback (with the `cancel` method), in which case we /// need this in order to release the memory. saved_cb: Option<*mut Box>, /// Saved multi-use state callback closure, for later destruction. state_cb: NotifyCb, } unsafe impl Send for Operation {} unsafe impl Sync for Operation {} type NotifyCb = callbacks::MultiUseCallback; impl Operation { /// Creates a new `Operation` from an existing [`OperationInternal`] pointer. /// /// We also take a copy of the closure callback pointer, in order to free the memory on /// cancellation. pub(crate) fn from_raw(ptr: *mut OperationInternal, saved_cb: *mut Box) -> Self { assert!(!ptr.is_null()); let saved_cb_actual = match saved_cb.is_null() { true => Some(saved_cb), false => None, }; Self { ptr: ptr, saved_cb: saved_cb_actual, state_cb: Default::default() } } /// Cancels the operation. /// /// Beware! This will not necessarily cancel the execution of the operation on the server side. /// However it will make sure that the callback associated with this operation will not be /// called any more, effectively disabling the operation from the client side’s view. /// /// **Warning**, you should **never** attempt to use this to cancel a callback from within the /// execution of that callback itself. This should go without saying, since it makes absolutely /// no sense to try and do this, but be aware that this is not supported by the C API and /// **will** break things. pub fn cancel(&mut self) { unsafe { capi::pa_operation_cancel(self.ptr); } // Release the memory allocated for the closure. // Note, we `take()` here to help avoid issues if this function is mistakenly called more // than once. let callback = self.saved_cb.take(); if let Some(ptr) = callback { if !ptr.is_null() { drop(unsafe { Box::from_raw(ptr as *mut Box) }); } } } /// Gets the current status of the operation. #[inline] pub fn get_state(&self) -> State { unsafe { capi::pa_operation_get_state(self.ptr) } } /// Sets the callback function that is called when the operation state changes. /// /// Usually this is not necessary, since the functions that create `Operation` objects already /// take a callback that is called when the operation finishes. Registering a state change /// callback is mainly useful, if you want to get called back also if the operation gets /// cancelled. pub fn set_state_callback(&mut self, callback: Option>) { let saved = &mut self.state_cb; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy); unsafe { capi::pa_operation_set_state_callback(self.ptr, cb_fn, cb_data); } } } impl Drop for Operation { fn drop(&mut self) { // Note, we deliberately do not destroy the `saved_cb` closure here. That should only be // destroyed either separately by a callback proxy, or by the `Operation`’s `cancel` method. unsafe { capi::pa_operation_unref(self.ptr) }; self.ptr = null_mut::(); } } /// Proxy for notification callbacks. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn notify_cb_proxy(_: *mut OperationInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = NotifyCb::get_callback(userdata); (callback)(); }); } libpulse-binding-2.28.1/src/proplist.rs000064400000000000000000000550661046102023000161720ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Property list constants and functions. use std::os::raw::{c_char, c_void}; use std::ffi::{CStr, CString}; use std::ptr::{null, null_mut}; use std::marker::PhantomData; use crate::error::PAErr; pub(crate) use capi::pa_proplist as ProplistInternal; pub use capi::pa_update_mode_t as UpdateMode; /// Common properties. pub mod properties { use capi; pub use capi::PA_PROP_MEDIA_NAME as MEDIA_NAME; pub use capi::PA_PROP_MEDIA_TITLE as MEDIA_TITLE; pub use capi::PA_PROP_MEDIA_ARTIST as MEDIA_ARTIST; pub use capi::PA_PROP_MEDIA_COPYRIGHT as MEDIA_COPYRIGHT; pub use capi::PA_PROP_MEDIA_SOFTWARE as MEDIA_SOFTWARE; pub use capi::PA_PROP_MEDIA_LANGUAGE as MEDIA_LANGUAGE; pub use capi::PA_PROP_MEDIA_FILENAME as MEDIA_FILENAME; pub use capi::PA_PROP_MEDIA_ICON as MEDIA_ICON; pub use capi::PA_PROP_MEDIA_ICON_NAME as MEDIA_ICON_NAME; pub use capi::PA_PROP_MEDIA_ROLE as MEDIA_ROLE; pub use capi::PA_PROP_FILTER_WANT as FILTER_WANT; pub use capi::PA_PROP_EVENT_ID as EVENT_ID; pub use capi::PA_PROP_EVENT_DESCRIPTION as EVENT_DESCRIPTION; pub use capi::PA_PROP_EVENT_MOUSE_X as EVENT_MOUSE_X; pub use capi::PA_PROP_EVENT_MOUSE_Y as EVENT_MOUSE_Y; pub use capi::PA_PROP_EVENT_MOUSE_HPOS as EVENT_MOUSE_HPOS; pub use capi::PA_PROP_EVENT_MOUSE_VPOS as EVENT_MOUSE_VPOS; pub use capi::PA_PROP_EVENT_MOUSE_BUTTON as EVENT_MOUSE_BUTTON; pub use capi::PA_PROP_WINDOW_NAME as WINDOW_NAME; pub use capi::PA_PROP_WINDOW_ID as WINDOW_ID; pub use capi::PA_PROP_WINDOW_ICON as WINDOW_ICON; pub use capi::PA_PROP_WINDOW_ICON_NAME as WINDOW_ICON_NAME; pub use capi::PA_PROP_WINDOW_X as WINDOW_X; pub use capi::PA_PROP_WINDOW_Y as WINDOW_Y; pub use capi::PA_PROP_WINDOW_WIDTH as WINDOW_WIDTH; pub use capi::PA_PROP_WINDOW_HEIGHT as WINDOW_HEIGHT; pub use capi::PA_PROP_WINDOW_HPOS as WINDOW_HPOS; pub use capi::PA_PROP_WINDOW_VPOS as WINDOW_VPOS; pub use capi::PA_PROP_WINDOW_DESKTOP as WINDOW_DESKTOP; pub use capi::PA_PROP_WINDOW_X11_DISPLAY as WINDOW_X11_DISPLAY; pub use capi::PA_PROP_WINDOW_X11_SCREEN as WINDOW_X11_SCREEN; pub use capi::PA_PROP_WINDOW_X11_MONITOR as WINDOW_X11_MONITOR; pub use capi::PA_PROP_WINDOW_X11_XID as WINDOW_X11_XID; pub use capi::PA_PROP_APPLICATION_NAME as APPLICATION_NAME; pub use capi::PA_PROP_APPLICATION_ID as APPLICATION_ID; pub use capi::PA_PROP_APPLICATION_VERSION as APPLICATION_VERSION; pub use capi::PA_PROP_APPLICATION_ICON as APPLICATION_ICON; pub use capi::PA_PROP_APPLICATION_ICON_NAME as APPLICATION_ICON_NAME; pub use capi::PA_PROP_APPLICATION_LANGUAGE as APPLICATION_LANGUAGE; pub use capi::PA_PROP_APPLICATION_PROCESS_ID as APPLICATION_PROCESS_ID; pub use capi::PA_PROP_APPLICATION_PROCESS_BINARY as APPLICATION_PROCESS_BINARY; pub use capi::PA_PROP_APPLICATION_PROCESS_USER as APPLICATION_PROCESS_USER; pub use capi::PA_PROP_APPLICATION_PROCESS_HOST as APPLICATION_PROCESS_HOST; pub use capi::PA_PROP_APPLICATION_PROCESS_MACHINE_ID as APPLICATION_PROCESS_MACHINE_ID; pub use capi::PA_PROP_APPLICATION_PROCESS_SESSION_ID as APPLICATION_PROCESS_SESSION_ID; pub use capi::PA_PROP_DEVICE_STRING as DEVICE_STRING; pub use capi::PA_PROP_DEVICE_API as DEVICE_API; pub use capi::PA_PROP_DEVICE_DESCRIPTION as DEVICE_DESCRIPTION; pub use capi::PA_PROP_DEVICE_BUS_PATH as DEVICE_BUS_PATH; pub use capi::PA_PROP_DEVICE_SERIAL as DEVICE_SERIAL; pub use capi::PA_PROP_DEVICE_VENDOR_ID as DEVICE_VENDOR_ID; pub use capi::PA_PROP_DEVICE_VENDOR_NAME as DEVICE_VENDOR_NAME; pub use capi::PA_PROP_DEVICE_PRODUCT_ID as DEVICE_PRODUCT_ID; pub use capi::PA_PROP_DEVICE_PRODUCT_NAME as DEVICE_PRODUCT_NAME; pub use capi::PA_PROP_DEVICE_CLASS as DEVICE_CLASS; pub use capi::PA_PROP_DEVICE_FORM_FACTOR as DEVICE_FORM_FACTOR; pub use capi::PA_PROP_DEVICE_BUS as DEVICE_BUS; pub use capi::PA_PROP_DEVICE_ICON as DEVICE_ICON; pub use capi::PA_PROP_DEVICE_ICON_NAME as DEVICE_ICON_NAME; pub use capi::PA_PROP_DEVICE_ACCESS_MODE as DEVICE_ACCESS_MODE; pub use capi::PA_PROP_DEVICE_MASTER_DEVICE as DEVICE_MASTER_DEVICE; pub use capi::PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE as DEVICE_BUFFERING_BUFFER_SIZE; pub use capi::PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE as DEVICE_BUFFERING_FRAGMENT_SIZE; pub use capi::PA_PROP_DEVICE_PROFILE_NAME as DEVICE_PROFILE_NAME; pub use capi::PA_PROP_DEVICE_PROFILE_DESCRIPTION as DEVICE_PROFILE_DESCRIPTION; pub use capi::PA_PROP_MODULE_AUTHOR as MODULE_AUTHOR; pub use capi::PA_PROP_MODULE_DESCRIPTION as MODULE_DESCRIPTION; pub use capi::PA_PROP_MODULE_USAGE as MODULE_USAGE; pub use capi::PA_PROP_MODULE_VERSION as MODULE_VERSION; pub use capi::PA_PROP_FORMAT_RATE as FORMAT_RATE; pub use capi::PA_PROP_FORMAT_CHANNELS as FORMAT_CHANNELS; #[cfg(any(doc, feature = "pa_v15"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))] pub use capi::PA_PROP_CONTEXT_FORCE_DISABLE_SHM as CONTEXT_FORCE_DISABLE_SHM; #[cfg(any(doc, feature = "pa_v15"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))] pub use capi::PA_PROP_BLUETOOTH_CODEC as BLUETOOTH_CODEC; /* These need defining here, rather than `pub use`, in order to correctly link to other things * in their doc comments */ /// For streams: the name of a filter that is desired, e.g. “echo-cancel” or “equalizer-sink”. /// Differs from [`FILTER_WANT`] in that it forces PulseAudio to apply the filter, regardless of /// whether PulseAudio thinks it makes sense to do so or not. If this is set, [`FILTER_WANT`] is /// ignored. In other words, you almost certainly do not want to use this. pub const FILTER_APPLY: &str = capi::PA_PROP_FILTER_APPLY; /// For streams: the name of a filter that should specifically be suppressed (i.e. overrides /// [`FILTER_WANT`]). Useful for the times that [`FILTER_WANT`] is automatically added (e.g. /// echo-cancellation for phone streams when $VOIP_APP does its own, internal AEC). pub const FILTER_SUPPRESS: &str = capi::PA_PROP_FILTER_SUPPRESS; /// For devices: intended use. A space separated list of roles (see [`MEDIA_ROLE`]) this device /// is particularly well suited for, due to latency, quality or form factor. pub const DEVICE_INTENDED_ROLES: &str = capi::PA_PROP_DEVICE_INTENDED_ROLES; /// For PCM formats: the sample format used as returned by /// [`Format::to_string()`](crate::sample::Format::to_string). pub const FORMAT_SAMPLE_FORMAT: &str = capi::PA_PROP_FORMAT_SAMPLE_FORMAT; /// For PCM formats: the channel map of the stream as returned by /// [`Map::print()`](crate::channelmap::Map::print). pub const FORMAT_CHANNEL_MAP: &str = capi::PA_PROP_FORMAT_CHANNEL_MAP; } /// A property list object. Basically a dictionary with ASCII strings as keys and arbitrary data as /// values. pub struct Proplist(pub(crate) ProplistInner); unsafe impl Send for Proplist {} unsafe impl Sync for Proplist {} /// Inner type holding ownership over actual C object, necessary to guard against use-after-free /// issues with respect to the related `Iterator` object. pub(crate) struct ProplistInner { /// The actual C object. pub(crate) ptr: *mut ProplistInternal, /// Used to avoid freeing the internal object when used as a weak wrapper in callbacks. weak: bool, } impl std::fmt::Debug for Proplist { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "[{}]", self.to_string_sep(", ").unwrap()) } } /// Proplist iterator, used for iterating over the list’s keys. Returned by the /// [`Proplist::iter()`] method. /// /// Note, lifetime `'a` is used to tie an instance of this struct to the associated `Proplist`, and /// thus prevent a use-after-free issue that would otherwise occur should the `Proplist` be /// destroyed first. Conversion from a `Proplist` via `into_iter` is okay though as responsibility /// for destruction is transfered to it. //XXX: Do **NOT** derive `Clone` for this, it will introduce a use-afer-free. To implement `Clone` properly would require an `Rc` wrapper around `ProplistInner`, but then if that would apply to `Proplist` also, that affects the `Send`+`Sync` properties of `Proplist` and anything using it. pub struct Iterator<'a> { /// The actual C proplist object. pl_ref: ProplistInner, /// State tracker, used by underlying C function. state: *mut c_void, /// Use lifetime `'a`. phantom: PhantomData<&'a ProplistInner>, } impl<'a> Iterator<'a> { fn new(pl: *mut ProplistInternal) -> Self { Self { pl_ref: ProplistInner { ptr: pl, weak: true }, state: null_mut::(), phantom: PhantomData, } } } impl<'a> std::iter::Iterator for Iterator<'a> { type Item = String; fn next(&mut self) -> Option { let state_actual = &mut self.state as *mut *mut c_void; let key_ptr = unsafe { capi::pa_proplist_iterate(self.pl_ref.ptr, state_actual) }; if key_ptr.is_null() { return None; } // We assume key_ptr will never be null at this point Some(unsafe { CStr::from_ptr(key_ptr).to_string_lossy().into_owned() }) } } impl IntoIterator for Proplist { type Item = String; type IntoIter = Iterator<'static>; fn into_iter(mut self) -> Self::IntoIter { let mut iter = Iterator::new(self.0.ptr); // Move responsibility for destruction, if it has it (is not weak itself) iter.pl_ref.weak = self.0.weak; self.0.weak = true; iter } } impl PartialEq for Proplist { #[inline] fn eq(&self, other: &Self) -> bool { unsafe { capi::pa_proplist_equal(self.0.ptr, other.0.ptr) != 0 } } } impl Proplist { /// Allocates a property list. pub fn new() -> Option { let ptr = unsafe { capi::pa_proplist_new() }; match ptr.is_null() { false => Some(Self::from_raw(ptr)), true => None, } } /// Allocates a new property list and assigns key/value from a human readable string. pub fn new_from_string(s: &str) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_str = CString::new(s.clone()).unwrap(); let ptr = unsafe { capi::pa_proplist_from_string(c_str.as_ptr()) }; match ptr.is_null() { false => Some(Self::from_raw(ptr)), true => None, } } /// Creates a new `Proplist` from an existing [`ProplistInternal`] pointer. #[inline] pub(crate) fn from_raw(ptr: *mut ProplistInternal) -> Self { assert_eq!(false, ptr.is_null()); Proplist(ProplistInner { ptr: ptr, weak: false }) } /// Creates a new `Proplist` from an existing [`ProplistInternal`] pointer. /// /// This is the ‘weak’ version, which avoids destroying the internal object when dropped. #[inline] pub(crate) fn from_raw_weak(ptr: *mut ProplistInternal) -> Self { assert_eq!(false, ptr.is_null()); Proplist(ProplistInner { ptr: ptr, weak: true }) } /// Checks if the key is valid. pub fn key_is_valid(key: &str) -> bool { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); unsafe { capi::pa_proplist_key_valid(c_key.as_ptr()) != 0 } } /// Appends a new string entry to the property list, possibly overwriting an already existing /// entry with the same key. /// /// An internal copy is made of the provided string. pub fn set_str(&mut self, key: &str, value: &str) -> Result<(), ()> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); let c_value = CString::new(value.clone()).unwrap(); match unsafe { capi::pa_proplist_sets(self.0.ptr, c_key.as_ptr(), c_value.as_ptr()) } { 0 => Ok(()), _ => Err(()), } } /// Appends a new string entry to the property list, possibly overwriting an already existing /// entry with the same key. /// /// This is similar to [`set_str()`](Self::set_str), however here the provided key and value /// are combined into a single string, separated by an `=`. An internal copy is made of the /// provided string. pub fn set_pl(&mut self, pair: &str) -> Result<(), ()> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_pair = CString::new(pair.clone()).unwrap(); match unsafe { capi::pa_proplist_setp(self.0.ptr, c_pair.as_ptr()) } { 0 => Ok(()), _ => Err(()), } } /// Appends a new arbitrary data entry to the property list, possibly overwriting an already /// existing entry with the same key. /// /// An internal copy of the provided data is made. pub fn set(&mut self, key: &str, data: &[u8]) -> Result<(), ()> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); match unsafe { capi::pa_proplist_set(self.0.ptr, c_key.as_ptr(), data.as_ptr() as *mut c_void, data.len()) } { 0 => Ok(()), _ => Err(()), } } /// Gets a string entry for the specified key. /// /// Will return `None` if the key does not exist or if data is not valid UTF-8. pub fn get_str(&self, key: &str) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); let ptr = unsafe { capi::pa_proplist_gets(self.0.ptr, c_key.as_ptr()) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }), true => None, } } /// Gets the value for the specified key. /// /// For string entries, the value store will be NUL-terminated. /// /// The caller should make a copy of the data before any subsequent modification or destruction /// of the property list. /// /// Returns a slice formed from the data pointer and the length of the data. /// Returns `None` if key does not exist. pub fn get(&self, key: &str) -> Option<&[u8]> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); let mut data_ptr = null::(); let mut nbytes: usize = 0; if unsafe { capi::pa_proplist_get(self.0.ptr, c_key.as_ptr(), &mut data_ptr, &mut nbytes) } != 0 { return None; } if data_ptr.is_null() { return None; } Some(unsafe { std::slice::from_raw_parts(data_ptr as *const u8, nbytes) }) } /// Merges property list “other” into self, adhering to the merge mode specified. #[inline] pub fn merge(&mut self, other: &Self, mode: UpdateMode) { unsafe { capi::pa_proplist_update(self.0.ptr, mode, other.0.ptr); } } /// Removes a single entry from the property list, identified by the specified key name. pub fn unset(&mut self, key: &str) -> Result<(), PAErr> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); match unsafe { capi::pa_proplist_unset(self.0.ptr, c_key.as_ptr()) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Similar to [`unset()`](Self::unset) but takes an array of keys to remove. /// /// Returns `None` on failure, otherwise the number of entries actually removed (which might /// even be 0, if there were no matching entries to remove). pub fn unset_many(&mut self, keys: &[&str]) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let mut c_keys: Vec = Vec::with_capacity(keys.len()); for k in keys { c_keys.push(CString::new(*k).unwrap()); } // Capture array of pointers to the above CString values. // We also add a NULL pointer entry on the end, as expected by the C function called here. let mut c_keys_ptrs: Vec<*const c_char> = Vec::with_capacity(c_keys.len() + 1); for k in &c_keys { c_keys_ptrs.push(k.as_ptr()); } c_keys_ptrs.push(null()); match unsafe { capi::pa_proplist_unset_many(self.0.ptr, c_keys_ptrs.as_ptr()) } { r if r < 0 => None, r => Some(r as u32), } } /// Gets an immutable iterator over the list’s keys. /// /// The property list should not be modified during iteration through the list, with the /// exception of deleting the current entry. The keys in the property list do not have any /// particular order. /// /// ```rust /// # extern crate libpulse_binding as pulse; /// # use pulse::proplist::Proplist; /// # /// # fn main() { /// # let mut my_props = Proplist::new().unwrap(); /// # my_props.set_str(pulse::proplist::properties::APPLICATION_NAME, "FooApp").unwrap(); /// # /// for key in my_props.iter() { /// //do something with it /// println!("key: {}", key); /// } /// # } /// ``` #[inline] pub fn iter(&self) -> Iterator<'_> { Iterator::new(self.0.ptr) } /// Formats the property list nicely as a human readable string. /// /// This works very much like [`to_string_sep()`](Self::to_string_sep) and uses a newline as /// separator and appends one final one. pub fn to_string(&self) -> Option { let ptr = unsafe { capi::pa_proplist_to_string(self.0.ptr) }; if ptr.is_null() { return None; } // Note, copying string on behalf of user here, and freeing that returned by PA, as // documentation instructs, saving the user from having to remember. unsafe { let ret = Some(CStr::from_ptr(ptr).to_string_lossy().into_owned()); capi::pa_xfree(ptr as *mut c_void); ret } } /// Formats the property list nicely as a human readable string, choosing the separator used. pub fn to_string_sep(&self, sep: &str) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_sep = CString::new(sep.clone()).unwrap(); let ptr = unsafe { capi::pa_proplist_to_string_sep(self.0.ptr, c_sep.as_ptr()) }; if ptr.is_null() { return None; } // Note, copying string on behalf of user here, and freeing that returned by PA, as // documentation instructs, saving the user from having to remember. unsafe { let ret = Some(CStr::from_ptr(ptr).to_string_lossy().into_owned()); capi::pa_xfree(ptr as *mut c_void); ret } } /// Checks if this contains an entry with the given key. /// /// Returns `true` if an entry for the specified key exists in the property list. Returns `None` /// on error. pub fn contains(&self, key: &str) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_key = CString::new(key.clone()).unwrap(); match unsafe { capi::pa_proplist_contains(self.0.ptr, c_key.as_ptr()) } { 0 => Some(false), 1 => Some(true), _ => None, } } /// Removes all entries from the property list object. #[inline] pub fn clear(&mut self) { unsafe { capi::pa_proplist_clear(self.0.ptr); } } /// Gets the number of entries in the property list. #[inline] pub fn len(&self) -> u32 { unsafe { capi::pa_proplist_size(self.0.ptr) } } /// Checks if the proplist is empty. #[inline] pub fn is_empty(&self) -> bool { unsafe { capi::pa_proplist_isempty(self.0.ptr) == 0 } } } impl Drop for ProplistInner { fn drop(&mut self) { if !self.weak { unsafe { capi::pa_proplist_free(self.ptr) }; } self.ptr = null_mut::(); } } impl Clone for Proplist { /// Allocates a new property list and copy over every single entry from the specified list. /// /// If this is called on a ‘weak’ instance, a non-weak object is returned. #[inline] fn clone(&self) -> Self { Self::from_raw(unsafe { capi::pa_proplist_copy(self.0.ptr) }) } } #[cfg(test)] mod tests { use super::*; /// Test that you cannot create a use-after-free situation by destroying a `Proplist` before an /// associated `Iterator` (we avoid `Rc`/`Arc`). #[test] #[cfg(compile_fail)] fn proplist_iter_lifetime() { let iter = { let my_props = Proplist::new().unwrap(); my_props.iter() //Returning this should not compile! }; for key in iter { //do something with it println!("key: {}", key); } } /// Test that you can however return an iterator if you convert the `Proplist` into one #[test] fn proplist_iter_lifetime_conv() { let iter = { let my_props = Proplist::new().unwrap(); my_props.into_iter() }; for key in iter { //do something with it println!("key: {}", key); } } } libpulse-binding-2.28.1/src/sample.rs000064400000000000000000000455631046102023000156000ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Constants and routines for sample type handling. //! //! # Overview //! //! PulseAudio is capable of handling a multitude of sample formats, rates and channels, //! transparently converting and mixing them as needed. //! //! # Sample Formats //! //! PulseAudio supports the following sample formats: //! //! * `U8` - Unsigned 8 bit integer PCM. //! * `S16LE` - Signed 16 integer bit PCM, little endian. //! * `S16BE` - Signed 16 integer bit PCM, big endian. //! * `FLOAT32LE` - 32 bit IEEE floating point PCM, little endian. //! * `FLOAT32BE` - 32 bit IEEE floating point PCM, big endian. //! * `ALAW` - 8 bit a-Law. //! * `ULAW` - 8 bit mu-Law. //! * `S32LE` - Signed 32 bit integer PCM, little endian. //! * `S32BE` - Signed 32 bit integer PCM, big endian. //! * `S24LE` - Signed 24 bit integer PCM packed, little endian. //! * `S24BE` - Signed 24 bit integer PCM packed, big endian. //! * `S24_32LE` - Signed 24 bit integer PCM in LSB of 32 bit words, little endian. //! * `S24_32BE` - Signed 24 bit integer PCM in LSB of 32 bit words, big endian. //! //! The floating point sample formats have the range from `-1.0` to `1.0`. //! //! # Sample Rates //! //! PulseAudio supports any sample rate between 1 Hz and 192000 Hz. There is no point trying to //! exceed the sample rate of the output device though as the signal will only get downsampled, //! consuming CPU on the machine running the server. //! //! # Channels //! //! PulseAudio supports up to 32 individual channels. The order of the channels is up to the //! application, but they must be continuous. To map channels to speakers, see //! [`channelmap`](mod@crate::channelmap). //! //! # Calculations //! //! The PulseAudio library contains a number of convenience functions to do calculations on sample //! formats: //! //! * [`Spec::bytes_per_second()`]: The number of bytes one second of audio will take given a sample //! format. //! * [`Spec::frame_size()`]: The size, in bytes, of one frame (i.e. one set of samples, one for //! each channel). //! * [`Spec::sample_size()`]: The size, in bytes, of one sample. //! * [`Spec::bytes_to_usec()`]: Calculate the time it would take to play a buffer of a certain //! size. use std::ffi::{CStr, CString}; use std::borrow::Cow; use num_derive::{FromPrimitive, ToPrimitive}; use crate::time::MicroSeconds; /// Maximum number of allowed channels. #[deprecated(since = "2.20.0", note = "use associated constants on structs instead")] pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX; /// Maximum allowed sample rate. #[deprecated(since = "2.20.0", note = "use the associated constant on `Spec` instead")] pub const RATE_MAX: u32 = capi::PA_RATE_MAX; /// Sample format. /// /// Note, native-endian (endian-independent) associated constants are available on this type which /// should be preferred over direct use of the endian-specific variants, for improved flexibility /// and avoidance of mistakes. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] #[allow(non_camel_case_types)] pub enum Format { /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate (C API) equivalent */ /// Unsigned 8 Bit PCM. U8, /// 8 Bit a-Law. ALaw, /// 8 Bit mu-Law. ULaw, /// Signed 16 Bit PCM, little endian (PC). S16le, /// Signed 16 Bit PCM, big endian. S16be, /// 32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0. F32le, /// 32 Bit IEEE floating point, big endian, range -1.0 to 1.0. F32be, /// Signed 32 Bit PCM, little endian (PC). S32le, /// Signed 32 Bit PCM, big endian. S32be, /// Signed 24 Bit PCM packed, little endian (PC). S24le, /// Signed 24 Bit PCM packed, big endian. S24be, /// Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC). S24_32le, /// Signed 24 Bit PCM in LSB of 32 Bit words, big endian. S24_32be, /// An invalid value. Invalid = -1, } /// Check is equal to `sys` equivalent #[test] fn format_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); // Check order and value of variants match // No point checking conversions in both directions since both are a transmute assert_eq!(Format::U8, Format::from(capi::pa_sample_format_t::U8)); assert_eq!(Format::ALaw, Format::from(capi::pa_sample_format_t::ALaw)); assert_eq!(Format::ULaw, Format::from(capi::pa_sample_format_t::ULaw)); assert_eq!(Format::S16le, Format::from(capi::pa_sample_format_t::S16le)); assert_eq!(Format::S16be, Format::from(capi::pa_sample_format_t::S16be)); assert_eq!(Format::F32le, Format::from(capi::pa_sample_format_t::F32le)); assert_eq!(Format::F32be, Format::from(capi::pa_sample_format_t::F32be)); assert_eq!(Format::S32le, Format::from(capi::pa_sample_format_t::S32le)); assert_eq!(Format::S32be, Format::from(capi::pa_sample_format_t::S32be)); assert_eq!(Format::S24le, Format::from(capi::pa_sample_format_t::S24le)); assert_eq!(Format::S24be, Format::from(capi::pa_sample_format_t::S24be)); assert_eq!(Format::S24_32le, Format::from(capi::pa_sample_format_t::S24_32le)); assert_eq!(Format::S24_32be, Format::from(capi::pa_sample_format_t::S24_32be)); assert_eq!(Format::Invalid, Format::from(capi::pa_sample_format_t::Invalid)); } impl From for capi::pa_sample_format_t { #[inline] fn from(f: Format) -> Self { unsafe { std::mem::transmute(f) } } } impl From for Format { #[inline] fn from(f: capi::pa_sample_format_t) -> Self { unsafe { std::mem::transmute(f) } } } impl Default for Format { #[inline(always)] fn default() -> Self { Format::Invalid } } // The following are endian-independant format references. /// A shortcut for [`SAMPLE_FLOAT32NE`]. #[allow(deprecated)] #[deprecated(since = "2.20.0", note = "use the `FLOAT32NE` associated constant on `Format` instead")] pub const SAMPLE_FLOAT32: Format = SAMPLE_FLOAT32NE; /// Signed 16-bit PCM, native endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_S16NE: Format = self::ei_formats::SAMPLE_S16NE; /// 32-bit IEEE floating point, native endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_FLOAT32NE: Format = self::ei_formats::SAMPLE_FLOAT32NE; /// Signed 32-bit PCM, native endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_S32NE: Format = self::ei_formats::SAMPLE_S32NE; /// Signed 24-bit PCM packed, native endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_S24NE: Format = self::ei_formats::SAMPLE_S24NE; /// Signed 24-bit PCM in LSB of 32-bit words, native endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_S24_32NE: Format = self::ei_formats::SAMPLE_S24_32NE; /// Signed 16-bit PCM reverse endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_S16RE: Format = self::ei_formats::SAMPLE_S16RE; /// 32-bit IEEE floating point, reverse endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_FLOAT32RE: Format = self::ei_formats::SAMPLE_FLOAT32RE; /// Signed 32-bit PCM, reverse endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_S32RE: Format = self::ei_formats::SAMPLE_S32RE; /// Signed 24-bit PCM, packed reverse endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_S24RE: Format = self::ei_formats::SAMPLE_S24RE; /// Signed 24-bit PCM, in LSB of 32-bit words, reverse endian. #[deprecated(since = "2.20.0", note = "use the associated constant on `Format` instead")] pub const SAMPLE_S24_32RE: Format = self::ei_formats::SAMPLE_S24_32RE; /// Endian-independent format identifiers, for big-endian systems. #[cfg(target_endian = "big")] mod ei_formats { use super::Format; pub const SAMPLE_S16NE: Format = Format::S16be; pub const SAMPLE_FLOAT32NE: Format = Format::F32be; pub const SAMPLE_S32NE: Format = Format::S32be; pub const SAMPLE_S24NE: Format = Format::S24be; pub const SAMPLE_S24_32NE: Format = Format::S24_32be; pub const SAMPLE_S16RE: Format = Format::S16le; pub const SAMPLE_FLOAT32RE: Format = Format::F32le; pub const SAMPLE_S32RE: Format = Format::S32le; pub const SAMPLE_S24RE: Format = Format::S24le; pub const SAMPLE_S24_32RE: Format = Format::S24_32le; } /// Endian-independent format identifiers, for little-endian systems. #[cfg(target_endian = "little")] mod ei_formats { use super::Format; pub const SAMPLE_S16NE: Format = Format::S16le; pub const SAMPLE_FLOAT32NE: Format = Format::F32le; pub const SAMPLE_S32NE: Format = Format::S32le; pub const SAMPLE_S24NE: Format = Format::S24le; pub const SAMPLE_S24_32NE: Format = Format::S24_32le; pub const SAMPLE_S16RE: Format = Format::S16be; pub const SAMPLE_FLOAT32RE: Format = Format::F32be; pub const SAMPLE_S32RE: Format = Format::S32be; pub const SAMPLE_S24RE: Format = Format::S24be; pub const SAMPLE_S24_32RE: Format = Format::S24_32be; } /// A sample format and attribute specification. #[repr(C)] #[derive(Debug, Copy, Clone, Eq)] pub struct Spec { /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */ /// The sample format. pub format: Format, /// The sample rate. (e.g. 44100). pub rate: u32, /// Audio channels. (1 for mono, 2 for stereo, ...). pub channels: u8, } /// Test size is equal to `sys` equivalent #[test] fn spec_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); } impl AsRef for Spec { #[inline] fn as_ref(&self) -> &capi::pa_sample_spec { unsafe { &*(self as *const Self as *const capi::pa_sample_spec) } } } impl AsMut for Spec { #[inline] fn as_mut(&mut self) -> &mut capi::pa_sample_spec { unsafe { &mut *(self as *mut Self as *mut capi::pa_sample_spec) } } } impl AsRef for capi::pa_sample_spec { #[inline] fn as_ref(&self) -> &Spec { unsafe { &*(self as *const Self as *const Spec) } } } impl From for Spec { #[inline] fn from(s: capi::pa_sample_spec) -> Self { unsafe { std::mem::transmute(s) } } } impl PartialEq for Spec { #[inline] fn eq(&self, other: &Self) -> bool { unsafe { capi::pa_sample_spec_equal(self.as_ref(), other.as_ref()) != 0 } } } impl Spec { /// Maximum number of allowed channels. pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX; /// Maximum allowed sample rate. pub const RATE_MAX: u32 = capi::PA_RATE_MAX; /// Initializes the specified sample spec. /// /// The sample spec will have a defined state but [`is_valid()`](Self::is_valid) will fail for /// it. #[inline] pub fn init(&mut self) { unsafe { capi::pa_sample_spec_init(self.as_mut()); } } /// Checks if the whole sample type specification is valid. #[inline] pub fn is_valid(&self) -> bool { unsafe { capi::pa_sample_spec_valid(self.as_ref()) != 0 } } /// Checks only if the format attribute is valid. /// /// Or in other words that the client library running on the end user system accepts it. #[inline] pub fn format_is_valid(&self) -> bool { unsafe { capi::pa_sample_format_valid(self.format as u32) != 0 } } /// Checks only if the rate is within the supported range. #[inline] pub fn rate_is_valid(&self) -> bool { unsafe { capi::pa_sample_rate_valid(self.rate) != 0 } } /// Checks only if the channel count is within the supported range. #[inline] pub fn channels_are_valid(&self) -> bool { unsafe { capi::pa_channels_valid(self.channels) != 0 } } /// Gets the amount of bytes that constitute playback of one second of audio, with the specified /// sample type. #[inline] pub fn bytes_per_second(&self) -> usize { unsafe { capi::pa_bytes_per_second(self.as_ref()) } } /// Gets the size of a frame. #[inline] pub fn frame_size(&self) -> usize { unsafe { capi::pa_frame_size(self.as_ref()) } } /// Gets the size of a sample. #[inline] pub fn sample_size(&self) -> usize { unsafe { capi::pa_sample_size(self.as_ref()) } } /// Calculates the time it would take to play a buffer of the specified size. /// /// The return value will always be rounded down for non-integral return values. /// /// Note, the underlying calculation may overflow for very large values. #[inline] pub fn bytes_to_usec(&self, length: u64) -> MicroSeconds { MicroSeconds(unsafe { capi::pa_bytes_to_usec(length, self.as_ref()) }) } /// Calculates the size of a buffer required, for playback duration of the time specified. /// /// The return value will always be rounded down for non-integral return values. /// /// Note, the underlying calculation may overflow for very large values. #[inline] pub fn usec_to_bytes(&self, t: MicroSeconds) -> usize { unsafe { capi::pa_usec_to_bytes(t.0, self.as_ref()) } } /// Pretty prints a sample type specification to a string. pub fn print(&self) -> String { const PRINT_MAX: usize = capi::PA_SAMPLE_SPEC_SNPRINT_MAX; let mut tmp = Vec::with_capacity(PRINT_MAX); unsafe { capi::pa_sample_spec_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.as_ref()); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } } /// Pretty print a byte size value (i.e. “2.5 MiB”). pub fn bytes_print(bytes: u32) -> String { const PRINT_MAX: usize = capi::PA_BYTES_SNPRINT_MAX; let mut tmp = Vec::with_capacity(PRINT_MAX); unsafe { capi::pa_bytes_snprint(tmp.as_mut_ptr(), PRINT_MAX, bytes); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } impl Format { /// Signed 16-bit PCM, native endian. pub const S16NE: Self = self::ei_formats::SAMPLE_S16NE; /// 32-bit IEEE floating point, native endian. pub const FLOAT32NE: Self = self::ei_formats::SAMPLE_FLOAT32NE; /// Signed 32-bit PCM, native endian. pub const S32NE: Self = self::ei_formats::SAMPLE_S32NE; /// Signed 24-bit PCM packed, native endian. pub const S24NE: Self = self::ei_formats::SAMPLE_S24NE; /// Signed 24-bit PCM in LSB of 32-bit words, native endian. pub const S24_32NE: Self = self::ei_formats::SAMPLE_S24_32NE; /// Signed 16-bit PCM reverse endian. pub const S16RE: Self = self::ei_formats::SAMPLE_S16RE; /// 32-bit IEEE floating point, reverse endian. pub const FLOAT32RE: Self = self::ei_formats::SAMPLE_FLOAT32RE; /// Signed 32-bit PCM, reverse endian. pub const S32RE: Self = self::ei_formats::SAMPLE_S32RE; /// Signed 24-bit PCM, packed reverse endian. pub const S24RE: Self = self::ei_formats::SAMPLE_S24RE; /// Signed 24-bit PCM, in LSB of 32-bit words, reverse endian. pub const S24_32RE: Self = self::ei_formats::SAMPLE_S24_32RE; /// Similar to [`Spec::sample_size()`] but take a sample format instead of full sample spec. #[inline] pub fn size(&self) -> usize { unsafe { capi::pa_sample_size_of_format((*self).into()) } } /// Gets a descriptive string for the specified sample format. pub fn to_string(&self) -> Option> { let ptr = unsafe { capi::pa_sample_format_to_string((*self).into()) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }), true => None, } } /// Parses a sample format text. Inverse of [`to_string()`](Self::to_string). pub fn parse(format: &str) -> Self { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_format = CString::new(format.clone()).unwrap(); unsafe { capi::pa_parse_sample_format(c_format.as_ptr()).into() } } /// Checks if format is little endian. /// /// Returns `true` when the specified format is little endian, `false` if big endian. Returns /// `None` when endianness does not apply to this format, or if unknown. pub fn is_le(&self) -> Option { match unsafe { capi::pa_sample_format_is_le((*self).into()) } { 0 => Some(false), 1 => Some(true), _ => None, } } /// Checks if format is big endian. /// /// Returns `true` when the specified format is big endian, `false` if little endian. Returns /// `None` when endianness does not apply to this format, or if unknown. pub fn is_be(&self) -> Option { match unsafe { capi::pa_sample_format_is_be((*self).into()) } { 0 => Some(false), 1 => Some(true), _ => None, } } /// Checks if format is native endian. /// /// Returns `true` when the specified format is native endian, `false` when not. Returns `None` /// when endianness does not apply to the specified format, or endianness is unknown. #[inline] pub fn is_ne(&self) -> Option { #[cfg(target_endian = "big")] { Format::is_be(self) } #[cfg(target_endian = "little")] { Format::is_le(self) } } /// Checks if format is reverse of native endian. /// /// Returns `true` when the specified format is reverse endian, `false` when not. Returns `None` /// when endianness does not apply to the specified format, or endianness is unknown. #[inline] pub fn is_re(&self) -> Option { self.is_ne().and_then(|b| Some(!b)) } } libpulse-binding-2.28.1/src/stream.rs000064400000000000000000002656251046102023000156150ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Audio streams for input, output and sample upload. //! //! # Overview //! //! Audio streams form the central functionality of the sound server. Data is routed, converted and //! mixed from several sources before it is passed along to a final output. Currently, there are //! three forms of audio streams: //! //! * Playback streams: Data flows from the client to the server. //! * Record streams: Data flows from the server to the client. //! * Upload streams: Similar to playback streams, but the data is stored in the sample cache. See //! [`context::scache`] for more information about controlling the sample cache. //! //! # Creating //! //! To access a stream, a [`Stream`] object must be created using [`Stream::new()`] or //! [`Stream::new_extended()`]. `new` is for PCM streams only, while `new_extended` can be used for //! both PCM and compressed audio streams. At this point the application must specify what stream //! format(s) it supports. See [`sample`] and [`channelmap`] for more information on the stream //! format parameters. //! //! **FIXME**: Those references only talk about PCM parameters, we should also have an overview //! page for how the [`Info`] based stream format configuration works. //! [Bug filed](https://bugs.freedesktop.org/show_bug.cgi?id=72265). //! //! This first step will only create a client-side object, representing the stream. To use the //! stream, a server-side object must be created and associated with the local object. Depending on //! which type of stream is desired, a different function is needed: //! //! * Playback stream: [`Stream::connect_playback()`] //! * Record stream: [`Stream::connect_record()`] //! * Upload stream: [`Stream::connect_upload()`] \(see [`context::scache`]) //! //! Similar to how connections are done in contexts, connecting a stream will not generate an //! [`Operation`] object. Also like contexts, the application should register a state change //! callback, using [`Stream::set_state_callback()`], and wait for the stream to enter an active //! state. //! //! Note: there is a user-controllable slider in mixer applications such as pavucontrol //! corresponding to each of the created streams. Multiple (especially identically named) volume //! sliders for the same application might confuse the user. Also, the server supports only a //! limited number of simultaneous streams. Because of this, it is not always appropriate to create //! multiple streams in one application that needs to output multiple sounds. The rough guideline //! is: if there is no use case that would require separate user-initiated volume changes for each //! stream, perform the mixing inside the application. //! //! # Buffer Attributes //! //! Playback and record streams always have a server-side buffer as part of the data flow. The size //! of this buffer needs to be chosen in a compromise between low latency and sensitivity for buffer //! overflows/underruns. //! //! The buffer metrics may be controlled by the application. They are described with a //! [`BufferAttr`] structure. //! //! If [`FlagSet::ADJUST_LATENCY`] is set, then the `tlength`/`fragsize` parameters of this //! structure will be interpreted slightly differently than otherwise when passed to //! [`Stream::connect_record()`] and [`Stream::connect_playback()`]: the overall latency that is //! comprised of both the server side playback buffer length, the hardware playback buffer length //! and additional latencies will be adjusted in a way that it matches `tlength` resp. `fragsize`. //! Set [`FlagSet::ADJUST_LATENCY`] if you want to control the overall playback latency for your //! stream. Unset it if you want to control only the latency induced by the server-side, rewritable //! playback buffer. The server will try to fulfill the client’s latency requests as good as //! possible. However if the underlying hardware cannot change the hardware buffer length or only in //! a limited range, the actually resulting latency might be different from what the client //! requested. Thus, for synchronization clients always need to check the actual measured latency //! via [`Stream::get_latency()`] or a similar call, and not make any assumptions about the latency //! available. The function [`Stream::get_buffer_attr()`] will always return the actual size of the //! server-side per-stream buffer in `tlength`/`fragsize`, regardless whether //! [`FlagSet::ADJUST_LATENCY`] is set or not. //! //! The server-side per-stream playback buffers are indexed by a write and a read index. The //! application writes to the write index and the sound device reads from the read index. The read //! index is increased monotonically, while the write index may be freely controlled by the //! application. Subtracting the read index from the write index will give you the current fill //! level of the buffer. The read/write indexes are 64bit values and measured in bytes, they will //! never wrap. The current read/write index may be queried using [`Stream::get_timing_info()`] //! \(see below for more information). In case of a buffer underrun the read index is equal or //! larger than the write index. Unless the `prebuf` value is `0`, PulseAudio will temporarily pause //! playback in such a case, and wait until the buffer is filled up to `prebuf` bytes again. If //! `prebuf` is `0`, the read index may be larger than the write index, in which case silence is //! played. If the application writes data to indexes lower than the read index, the data is //! immediately lost. //! //! # Transferring Data //! //! Once the stream is up, data can start flowing between the client and the server. Two different //! access models can be used to transfer the data: //! //! * Asynchronous: The application registers a callback using [`Stream::set_write_callback()`] and //! [`Stream::set_read_callback()`] to receive notifications that data can either be written or //! read. //! * Polled: Query the library for available data/space using [`Stream::writable_size()`] and //! [`Stream::readable_size()`] and transfer data as needed. The sizes are stored locally, in the //! client end, so there is no delay when reading them. //! //! It is also possible to mix the two models freely. //! //! Once there is data/space available, it can be transferred using either [`Stream::write()`] for //! playback, or [`Stream::peek()`] / [`Stream::discard()`] for record. Make sure you do not //! overflow the playback buffers as data will be dropped. //! //! # Buffer Control //! //! The transfer buffers can be controlled through a number of operations: //! //! * [`Stream::cork()`]: Stop the playback or recording. //! * [`Stream::uncork()`]: Start the playback or recording. //! * [`Stream::trigger()`]: Start playback immediately and do not wait for the buffer to fill up to //! the set trigger level. //! * [`Stream::prebuf()`]: Re-enable the playback trigger level. //! * [`Stream::drain()`]: Wait for the playback buffer to go empty. Will return an [`Operation`] //! object that will indicate when the buffer is completely drained. //! * [`Stream::flush()`]: Drop all data from the playback or record buffer. Do not wait for it to //! finish playing. //! //! # Seeking in the Playback Buffer //! //! A client application may freely seek in the playback buffer. To accomplish that the //! [`Stream::write()`] function takes a seek mode and an offset argument. The seek mode is one of: //! //! * [`SeekMode::Relative`]: seek relative to the current write index. //! * [`SeekMode::Absolute`]: seek relative to the beginning of the playback buffer, (i.e. the first //! that was ever played in the stream). //! * [`SeekMode::RelativeOnRead`]: seek relative to the current read index. Use this to write data //! to the output buffer that should be played as soon as possible. //! * [`SeekMode::RelativeEnd`]: seek relative to the last byte ever written. //! //! If an application just wants to append some data to the output buffer, [`SeekMode::Relative`] //! and an offset of `0` should be used. //! //! After a call to [`Stream::write()`] the write index will be left at the position right after the //! last byte of the written data. //! //! # Latency //! //! A major problem with networked audio is the increased latency caused by the network. To remedy //! this, PulseAudio supports an advanced system of monitoring the current latency. //! //! To get the raw data needed to calculate latencies, call [`Stream::get_timing_info()`]. This will //! give you a [`TimingInfo`] structure that contains everything that is known about the server side //! buffer transport delays and the backend active in the server. (Besides other things it contains //! the write and read index values mentioned above.) //! //! This structure is updated every time a [`Stream::update_timing_info()`] operation is executed. //! (i.e. before the first call to this function the timing information structure is not available!) //! Since it is a lot of work to keep this structure up-to-date manually, PulseAudio can do that //! automatically for you: if [`FlagSet::AUTO_TIMING_UPDATE`] is passed when connecting the stream //! PulseAudio will automatically update the structure every 100ms and every time a function is //! called that might invalidate the previously known timing data (such as [`Stream::write()`] or //! [`Stream::flush()`]). Please note however, that there always is a short time window when the //! data in the timing information structure is out-of-date. PulseAudio tries to mark these //! situations by setting the `write_index_corrupt` and `read_index_corrupt` fields accordingly. //! //! The raw timing data in the [`TimingInfo`] structure is usually hard to deal with. Therefore a //! simpler interface is available: you can call [`Stream::get_time()`] or //! [`Stream::get_latency()`]. The former will return the current playback time of the hardware //! since the stream has been started. The latter returns the overall time a sample that you write //! now takes to be played by the hardware. These two functions base their calculations on the same //! data that is returned by [`Stream::get_timing_info()`]. Hence the same rules for keeping the //! timing data up-to-date apply here. In case the write or read index is corrupted, these two //! functions will fail. //! //! Since updating the timing info structure usually requires a full network round trip and some //! applications monitor the timing very often PulseAudio offers a timing interpolation system. If //! [`FlagSet::INTERPOLATE_TIMING`] is passed when connecting the stream, [`Stream::get_time()`] and //! [`Stream::get_latency()`] will try to interpolate the current playback time/latency by //! estimating the number of samples that have been played back by the hardware since the last //! regular timing update. It is especially useful to combine this option with //! [`FlagSet::AUTO_TIMING_UPDATE`], which will enable you to monitor the current playback //! time/latency very precisely and very frequently without requiring a network round trip every //! time. //! //! # Overflow and underflow //! //! Even with the best precautions, buffers will sometime over - or underflow. To handle this //! gracefully, the application can be notified when this happens. Callbacks are registered using //! [`Stream::set_overflow_callback()`] and [`Stream::set_underflow_callback()`]. //! //! # Synchronizing Multiple Playback Streams //! //! PulseAudio allows applications to fully synchronize multiple playback streams that are connected //! to the same output device. That means the streams will always be played back sample-by-sample //! synchronously. If stream operations like [`Stream::cork()`] are issued on one of the //! synchronized streams, they are simultaneously issued on the others. //! //! To synchronize a stream to another, just pass the “master” stream as the last argument to //! [`Stream::connect_playback()`]. To make sure that the freshly created stream doesn’t start //! playback right-away, make sure to pass [`FlagSet::START_CORKED`] and, after all streams have //! been created, uncork them all with a single call to [`Stream::uncork()`] for the master stream. //! //! To make sure that a particular stream doesn’t stop to play when a server side buffer underrun //! happens on it while the other synchronized streams continue playing and hence deviate, you need //! to pass a [`BufferAttr`] with `prebuf` set to `0` when connecting. //! //! # Disconnecting //! //! When a stream has served is purpose it must be disconnected with [`Stream::disconnect()`]. If //! you only unreference it, then it will live on and eat resources both locally and on the server //! until you disconnect the context. This is done automatically upon drop of the stream object. //! //! [`context::scache`]: mod@crate::context::scache //! [`sample`]: mod@crate::sample //! [`channelmap`]: mod@crate::channelmap //! [`Info`]: crate::format::Info //! [`BufferAttr`]: crate::def::BufferAttr //! [`TimingInfo`]: crate::def::TimingInfo use std::os::raw::{c_char, c_void}; use std::ffi::{CStr, CString}; use std::ptr::{null, null_mut}; use std::borrow::Cow; use bitflags::bitflags; use num_derive::{FromPrimitive, ToPrimitive}; use capi::pa_stream as StreamInternal; use crate::{channelmap, format, def, proplist, sample}; use crate::callbacks::{self, box_closure_get_capi_ptr, get_su_capi_params, get_su_callback}; use crate::error::{self, PAErr}; use crate::format::InfoInternal; use crate::proplist::{Proplist, ProplistInternal}; use crate::{context::Context, volume::ChannelVolumes, operation::Operation, time::MicroSeconds}; pub use capi::pa_seek_mode_t as SeekMode; pub use capi::pa_stream_direction_t as Direction; /// An opaque stream for playback or recording. /// /// Note: Saves a copy of active multi-use closure callbacks, which it frees on drop. pub struct Stream { /// The actual C object. ptr: *mut StreamInternal, /// Multi-use callback closure pointers. cb_ptrs: CallbackPointers, } unsafe impl Send for Stream {} unsafe impl Sync for Stream {} /// Holds copies of callback closure pointers, for those that are “multi-use” (may be fired multiple /// times), for freeing at the appropriate time. #[derive(Default)] struct CallbackPointers { read: RequestCb, write: RequestCb, set_state: NotifyCb, overflow: NotifyCb, underflow: NotifyCb, started: NotifyCb, latency_update: NotifyCb, moved: NotifyCb, suspended: NotifyCb, buffer_attr: NotifyCb, event: EventCb, } type RequestCb = callbacks::MultiUseCallback; type NotifyCb = callbacks::MultiUseCallback; type EventCb = callbacks::MultiUseCallback; /// Stream state. #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(FromPrimitive, ToPrimitive)] pub enum State { /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate (C API) equivalent */ /// The stream is not yet connected to any sink or source. Unconnected, /// The stream is being created. Creating, /// The stream is established, you may pass audio data to it now. Ready, /// An error occurred that made the stream invalid. Failed, /// The stream has been terminated cleanly. Terminated, } /// Check is equal to `sys` equivalent #[test] fn state_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); // Check order and value of variants match // No point checking conversions in both directions since both are a transmute assert_eq!(State::Unconnected, State::from(capi::pa_stream_state_t::Unconnected)); assert_eq!(State::Creating, State::from(capi::pa_stream_state_t::Creating)); assert_eq!(State::Ready, State::from(capi::pa_stream_state_t::Ready)); assert_eq!(State::Failed, State::from(capi::pa_stream_state_t::Failed)); assert_eq!(State::Terminated, State::from(capi::pa_stream_state_t::Terminated)); } impl From for capi::pa_stream_state_t { #[inline] fn from(s: State) -> Self { unsafe { std::mem::transmute(s) } } } impl From for State { #[inline] fn from(s: capi::pa_stream_state_t) -> Self { unsafe { std::mem::transmute(s) } } } impl State { /// Checks if the passed state is one of the connected states. #[inline] pub fn is_good(self) -> bool { self == State::Creating || self == State::Ready } } bitflags! { /// Flag set. #[repr(transparent)] pub struct FlagSet: u32 { /// Flag to pass when no specific options are needed. const NOFLAGS = capi::PA_STREAM_NOFLAGS; /// Create the stream corked, requiring an explicit [`Stream::uncork()`] call to uncork it. const START_CORKED = capi::PA_STREAM_START_CORKED; /// Interpolate the latency for this stream. When enabled, [`Stream::get_latency()`] and /// [`Stream::get_time()`] will try to estimate the current record/playback time based on /// the local time that passed since the last timing info update. Using this option has the /// advantage of not requiring a whole round trip when the current playback/recording time /// is needed. Consider using this option when requesting latency information frequently. /// This is especially useful on long latency network connections. It makes a lot of sense /// to combine this option with [`AUTO_TIMING_UPDATE`]. /// /// [`AUTO_TIMING_UPDATE`]: Self::AUTO_TIMING_UPDATE const INTERPOLATE_TIMING = capi::PA_STREAM_INTERPOLATE_TIMING; /// Don’t force the time to increase monotonically. If this option is enabled, /// [`Stream::get_time()`] will not necessarily return always monotonically increasing time /// values on each call. This may confuse applications which cannot deal with time going /// ‘backwards’, but has the advantage that bad transport latency estimations that caused /// the time to jump ahead can be corrected quickly, without the need to wait. const NOT_MONOTONIC = capi::PA_STREAM_NOT_MONOTONIC; /// If set timing update requests are issued periodically automatically. Combined with /// [`INTERPOLATE_TIMING`] you will be able to query the current time and latency with /// [`Stream::get_time()`] and [`Stream::get_latency()`] at all times without a packet round /// trip. /// /// [`INTERPOLATE_TIMING`]: Self::INTERPOLATE_TIMING const AUTO_TIMING_UPDATE = capi::PA_STREAM_AUTO_TIMING_UPDATE; /// Don’t remap channels by their name, instead map them simply by their index. Implies /// [`NO_REMIX_CHANNELS`](Self::NO_REMIX_CHANNELS). const NO_REMAP_CHANNELS = capi::PA_STREAM_NO_REMAP_CHANNELS; /// When remapping channels by name, don’t upmix or downmix them to related channels. Copy /// them into matching channels of the device 1:1. const NO_REMIX_CHANNELS = capi::PA_STREAM_NO_REMIX_CHANNELS; /// Use the sample format of the sink/device this stream is being connected to, and possibly /// ignore the format the sample spec contains -- but you still have to pass a valid value /// in it as a hint to PulseAudio what would suit your stream best. If this is used you /// should query the used sample format after creating the stream by using /// [`Stream::get_sample_spec()`]. Also, if you specified manual buffer metrics it is /// recommended to update them with [`Stream::set_buffer_attr()`] to compensate for the /// changed frame sizes. /// /// When creating streams with [`Stream::new_extended()`], this flag has no effect. If you /// specify a format with PCM encoding, and you want the server to choose the sample format, /// then you should leave the sample format unspecified in the [`Info`] object. This also /// means that you can’t use [`Info::new_from_sample_spec()`], because that function always /// sets the sample format. /// /// [`Info`]: crate::format::Info /// [`Info::new_from_sample_spec()`]: crate::format::Info::new_from_sample_spec const FIX_FORMAT = capi::PA_STREAM_FIX_FORMAT; /// Use the sample rate of the sink, and possibly ignore the rate the sample spec contains. /// Usage similar to [`FIX_FORMAT`]. /// /// When creating streams with [`Stream::new_extended()`], this flag has no effect. If you /// specify a format with PCM encoding, and you want the server to choose the sample rate, /// then you should leave the rate unspecified in the [`Info`] object. This also means that /// you can’t use [`Info::new_from_sample_spec()`], because that function always sets the /// sample rate. /// /// [`FIX_FORMAT`]: Self::FIX_FORMAT /// [`Info`]: crate::format::Info /// [`Info::new_from_sample_spec()`]: crate::format::Info::new_from_sample_spec const FIX_RATE = capi::PA_STREAM_FIX_RATE; /// Use the number of channels and the channel map of the sink, and possibly ignore the number /// of channels and the map the sample spec and the passed channel map contains. Usage similar /// to [`FIX_FORMAT`]. /// /// When creating streams with [`Stream::new_extended()`], this flag has no effect. If you /// specify a format with PCM encoding, and you want the server to choose the channel count /// and/or channel map, then you should leave the channels and/or the channel map /// unspecified in the [`Info`] object. This also means that you can’t use /// [`Info::new_from_sample_spec()`], because that function always sets the channel count /// (but if you only want to leave the channel map unspecified, then /// [`Info::new_from_sample_spec()`] works, because the channel map parameter is optional). /// /// [`FIX_FORMAT`]: Self::FIX_FORMAT /// [`Info`]: crate::format::Info /// [`Info::new_from_sample_spec()`]: crate::format::Info::new_from_sample_spec const FIX_CHANNELS = capi::PA_STREAM_FIX_CHANNELS; /// Don’t allow moving of this stream to another sink/device. Useful if you use any of the /// `Fix*` flags and want to make sure that resampling never takes place -- which might /// happen if the stream is moved to another sink/source with a different sample /// spec/channel map. const DONT_MOVE = capi::PA_STREAM_DONT_MOVE; /// Allow dynamic changing of the sampling rate during playback with /// [`Stream::update_sample_rate()`]. const VARIABLE_RATE = capi::PA_STREAM_VARIABLE_RATE; /// Find peaks instead of resampling. const PEAK_DETECT = capi::PA_STREAM_PEAK_DETECT; /// Create in muted state. If neither [`START_UNMUTED`] nor this is specified, it is left to /// the server to decide whether to create the stream in muted or in un-muted state. /// /// [`START_UNMUTED`]: Self::START_UNMUTED const START_MUTED = capi::PA_STREAM_START_MUTED; /// Try to adjust the latency of the sink/source based on the requested buffer metrics and /// adjust buffer metrics accordingly. Also see [`BufferAttr`]. This option may not be /// specified at the same time as [`EARLY_REQUESTS`]. /// /// [`EARLY_REQUESTS`]: Self::EARLY_REQUESTS /// [`BufferAttr`]: crate::def::BufferAttr const ADJUST_LATENCY = capi::PA_STREAM_ADJUST_LATENCY; /// Enable compatibility mode for legacy clients that rely on a “classic” hardware device /// fragment-style playback model. If this option is set, the `minreq` value of the buffer /// metrics gets a new meaning: instead of just specifying that no requests asking for less /// new data than this value will be made to the client it will also guarantee that requests /// are generated as early as this limit is reached. This flag should only be set in very /// few situations where compatibility with a fragment-based playback model needs to be kept /// and the client applications cannot deal with data requests that are delayed to the /// latest moment possible. (Usually these are programs that use usleep() or a similar call /// in their playback loops instead of sleeping on the device itself.) Also see /// [`BufferAttr`]. This option may not be specified at the same time as [`ADJUST_LATENCY`]. /// /// [`ADJUST_LATENCY`]: Self::ADJUST_LATENCY /// [`BufferAttr`]: crate::def::BufferAttr const EARLY_REQUESTS = capi::PA_STREAM_EARLY_REQUESTS; /// If set this stream won’t be taken into account when it is checked whether the device /// this stream is connected to should auto-suspend. const DONT_INHIBIT_AUTO_SUSPEND = capi::PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND; /// Create in unmuted state. If neither [`START_MUTED`] nor this is specified, it is left to /// the server to decide whether to create the stream in muted or in unmuted state. /// /// [`START_MUTED`]: Self::START_MUTED const START_UNMUTED = capi::PA_STREAM_START_UNMUTED; /// If the sink/source this stream is connected to is suspended during the creation of this /// stream, cause it to fail. If the sink/source is being suspended during creation of this /// stream, make sure this stream is terminated. const FAIL_ON_SUSPEND = capi::PA_STREAM_FAIL_ON_SUSPEND; /// If a volume is passed when this stream is created, consider it relative to the sink’s /// current volume, never as absolute device volume. If this is not specified the volume /// will be consider absolute when the sink is in flat volume mode, relative otherwise. const RELATIVE_VOLUME = capi::PA_STREAM_RELATIVE_VOLUME; /// Used to tag content that will be rendered by passthrough sinks. The data will be left as /// is and not reformatted, resampled. const PASSTHROUGH = capi::PA_STREAM_PASSTHROUGH; } } impl Default for FlagSet { fn default() -> Self { Self::NOFLAGS } } /// Some special flags for stream connections. #[deprecated(since = "2.20.0", note = "Use the associated constants on `FlagSet`.")] pub mod flags { use super::FlagSet; /// Flag to pass when no specific options are needed. pub const NOFLAGS: FlagSet = FlagSet::NOFLAGS; /// Create the stream corked, requiring an explicit [`Stream::uncork()`] call to uncork it. /// /// [`Stream::uncork()`]: ../struct.Stream.html#method.uncork pub const START_CORKED: FlagSet = FlagSet::START_CORKED; /// Interpolate the latency for this stream. When enabled, [`Stream::get_latency()`] and /// [`Stream::get_time()`] will try to estimate the current record/playback time based on the /// local time that passed since the last timing info update. Using this option has the /// advantage of not requiring a whole round trip when the current playback/recording time is /// needed. Consider using this option when requesting latency information frequently. This is /// especially useful on long latency network connections. It makes a lot of sense to combine /// this option with [`AUTO_TIMING_UPDATE`]. /// /// [`Stream::get_latency()`]: ../struct.Stream.html#method.get_latency /// [`Stream::get_time()`]: ../struct.Stream.html#method.get_time pub const INTERPOLATE_TIMING: FlagSet = FlagSet::INTERPOLATE_TIMING; /// Don’t force the time to increase monotonically. If this option is enabled, /// [`Stream::get_time()`] will not necessarily return always monotonically increasing time /// values on each call. This may confuse applications which cannot deal with time going /// ‘backwards’, but has the advantage that bad transport latency estimations that caused the /// time to jump ahead can be corrected quickly, without the need to wait. /// /// [`Stream::get_time()`]: super::Stream::get_time pub const NOT_MONOTONIC: FlagSet = FlagSet::NOT_MONOTONIC; /// If set timing update requests are issued periodically automatically. Combined with /// [`INTERPOLATE_TIMING`] you will be able to query the current time and latency with /// [`Stream::get_time()`] and [`Stream::get_latency()`] at all times without a packet round /// trip. /// /// [`Stream::get_time()`]: super::Stream::get_time /// [`Stream::get_latency()`]: super::Stream::get_latency pub const AUTO_TIMING_UPDATE: FlagSet = FlagSet::AUTO_TIMING_UPDATE; /// Don’t remap channels by their name, instead map them simply by their index. Implies /// [`NO_REMIX_CHANNELS`](constant.NO_REMIX_CHANNELS.html). pub const NO_REMAP_CHANNELS: FlagSet = FlagSet::NO_REMAP_CHANNELS; /// When remapping channels by name, don’t upmix or downmix them to related channels. Copy them /// into matching channels of the device 1:1. pub const NO_REMIX_CHANNELS: FlagSet = FlagSet::NO_REMIX_CHANNELS; /// Use the sample format of the sink/device this stream is being connected to, and possibly /// ignore the format the sample spec contains -- but you still have to pass a valid value in it /// as a hint to PulseAudio what would suit your stream best. If this is used you should query /// the used sample format after creating the stream by using [`Stream::get_sample_spec()`]. /// Also, if you specified manual buffer metrics it is recommended to update them with /// [`Stream::set_buffer_attr()`] to compensate for the changed frame sizes. /// /// When creating streams with [`Stream::new_extended()`], this flag has no effect. If you /// specify a format with PCM encoding, and you want the server to choose the sample format, /// then you should leave the sample format unspecified in the [`Info`] object. This also means /// that you can’t use [`Info::new_from_sample_spec()`], because that function always sets the /// sample format. /// /// [`Stream::get_sample_spec()`]: super::Stream::get_sample_spec /// [`Stream::set_buffer_attr()`]: super::Stream::set_buffer_attr /// [`Stream::new_extended()`]: super::Stream::new_extended /// [`Info`]: crate::format::Info /// [`Info::new_from_sample_spec()`]: crate::format::Info::new_from_sample_spec pub const FIX_FORMAT: FlagSet = FlagSet::FIX_FORMAT; /// Use the sample rate of the sink, and possibly ignore the rate the sample spec contains. /// Usage similar to [`FIX_FORMAT`]. /// /// When creating streams with [`Stream::new_extended()`], this flag has no effect. If you /// specify a format with PCM encoding, and you want the server to choose the sample rate, then /// you should leave the rate unspecified in the [`Info`] object. This also means that you can’t /// use [`Info::new_from_sample_spec()`], because that function always sets the sample rate. /// /// [`FIX_FORMAT`]: constant.FIX_FORMAT.html /// [`Stream::new_extended()`]: super::Stream::new_extended /// [`Info`]: crate::format::Info /// [`Info::new_from_sample_spec()`]: crate::format::Info::new_from_sample_spec pub const FIX_RATE: FlagSet = FlagSet::FIX_RATE; /// Use the number of channels and the channel map of the sink, and possibly ignore the number /// of channels and the map the sample spec and the passed channel map contains. Usage similar /// to [`FIX_FORMAT`]. /// /// When creating streams with [`Stream::new_extended()`], this flag has no effect. If you /// specify a format with PCM encoding, and you want the server to choose the channel count /// and/or channel map, then you should leave the channels and/or the channel map unspecified in /// the [`Info`] object. This also means that you can’t use [`Info::new_from_sample_spec()`], /// because that function always sets the channel count (but if you only want to leave the /// channel map unspecified, then [`Info::new_from_sample_spec()`] works, because the channel /// map parameter is optional). /// /// [`FIX_FORMAT`]: constant.FIX_FORMAT.html /// [`Stream::new_extended()`]: super::Stream::new_extended /// [`Info`]: crate::format::Info /// [`Info::new_from_sample_spec()`]: crate::format::Info::new_from_sample_spec pub const FIX_CHANNELS: FlagSet = FlagSet::FIX_CHANNELS; /// Don’t allow moving of this stream to another sink/device. Useful if you use any of the /// `Fix*` flags and want to make sure that resampling never takes place -- which might happen /// if the stream is moved to another sink/source with a different sample spec/channel map. pub const DONT_MOVE: FlagSet = FlagSet::DONT_MOVE; /// Allow dynamic changing of the sampling rate during playback with /// [`Stream::update_sample_rate()`]. /// /// [`Stream::update_sample_rate()`]: super::Stream::update_sample_rate pub const VARIABLE_RATE: FlagSet = FlagSet::VARIABLE_RATE; /// Find peaks instead of resampling. pub const PEAK_DETECT: FlagSet = FlagSet::PEAK_DETECT; /// Create in muted state. If neither [`START_UNMUTED`] nor this is specified, it is left to the /// server to decide whether to create the stream in muted or in un-muted state. pub const START_MUTED: FlagSet = FlagSet::START_MUTED; /// Try to adjust the latency of the sink/source based on the requested buffer metrics and /// adjust buffer metrics accordingly. Also see [`BufferAttr`]. This option may not be /// specified at the same time as [`EARLY_REQUESTS`]. /// /// [`BufferAttr`]: crate::def::BufferAttr pub const ADJUST_LATENCY: FlagSet = FlagSet::ADJUST_LATENCY; /// Enable compatibility mode for legacy clients that rely on a “classic” hardware device /// fragment-style playback model. If this option is set, the `minreq` value of the buffer /// metrics gets a new meaning: instead of just specifying that no requests asking for less new /// data than this value will be made to the client it will also guarantee that requests are /// generated as early as this limit is reached. This flag should only be set in very few /// situations where compatibility with a fragment-based playback model needs to be kept and the /// client applications cannot deal with data requests that are delayed to the latest moment /// possible. (Usually these are programs that use usleep() or a similar call in their playback /// loops instead of sleeping on the device itself.) Also see [`BufferAttr`]. This option may /// not be specified at the same time as [`ADJUST_LATENCY`]. /// /// [`BufferAttr`]: crate::def::BufferAttr pub const EARLY_REQUESTS: FlagSet = FlagSet::EARLY_REQUESTS; /// If set this stream won’t be taken into account when it is checked whether the device this /// stream is connected to should auto-suspend. pub const DONT_INHIBIT_AUTO_SUSPEND: FlagSet = FlagSet::DONT_INHIBIT_AUTO_SUSPEND; /// Create in unmuted state. If neither [`START_MUTED`] nor this is specified, it is left to the /// server to decide whether to create the stream in muted or in unmuted state. pub const START_UNMUTED: FlagSet = FlagSet::START_UNMUTED; /// If the sink/source this stream is connected to is suspended during the creation of this /// stream, cause it to fail. If the sink/source is being suspended during creation of this /// stream, make sure this stream is terminated. pub const FAIL_ON_SUSPEND: FlagSet = FlagSet::FAIL_ON_SUSPEND; /// If a volume is passed when this stream is created, consider it relative to the sink’s /// current volume, never as absolute device volume. If this is not specified the volume will be /// consider absolute when the sink is in flat volume mode, relative otherwise. pub const RELATIVE_VOLUME: FlagSet = FlagSet::RELATIVE_VOLUME; /// Used to tag content that will be rendered by passthrough sinks. The data will be left as is /// and not reformatted, resampled. pub const PASSTHROUGH: FlagSet = FlagSet::PASSTHROUGH; } /// Common event names supplied to the [`Stream::set_event_callback()`] callback. pub mod event_names { use capi; /// A stream policy/meta event requesting that an application should cork a specific stream. pub const EVENT_REQUEST_CORK: &str = capi::PA_STREAM_EVENT_REQUEST_CORK; /// A stream policy/meta event requesting that an application should cork a specific stream. pub const EVENT_REQUEST_UNCORK: &str = capi::PA_STREAM_EVENT_REQUEST_UNCORK; /// A stream event notifying that the stream is going to be disconnected because the underlying /// sink changed and no longer supports the format that was originally negotiated. Clients need /// to connect a new stream to renegotiate a format and continue playback. pub const EVENT_FORMAT_LOST: &str = capi::PA_STREAM_EVENT_FORMAT_LOST; } /// Result type for the [`Stream::peek()`] method. See documentation of the method itself for more /// information. #[derive(Debug)] pub enum PeekResult<'a> { /// No data (Null data pointer and size of 0 returned by PA). Empty, /// Data hole with given size (Null pointer with non-zero size returned by PA). Hole(usize), /// Data available, with slice into memory returned by PA. Data(&'a [u8]), } /// Result type for [`Stream::get_latency()`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Latency { /// No latency. None, /// A positive (greater than zero) amount of latency. Positive(MicroSeconds), /// A negative (less than zero) amount of latency. Negative(MicroSeconds), } impl Stream { /// Creates a new, unconnected stream with the specified name and sample type. /// /// It is recommended to use [`new_with_proplist()`] instead and specify some initial /// properties. /// /// # Params /// /// * `ctx`: The context to create this stream in /// * `name`: A name for this stream /// * `ss`: The desired sample format /// * `map`: The desired channel map, or `None` for default /// /// [`new_with_proplist()`]: Self::new_with_proplist pub fn new(ctx: &mut Context, name: &str, ss: &sample::Spec, map: Option<&channelmap::Map>) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let p_map = map.map_or(null::(), |m| m.as_ref()); let ptr = unsafe { capi::pa_stream_new(ctx.ptr, c_name.as_ptr(), ss.as_ref(), p_map) }; match ptr.is_null() { false => Some(Self::from_raw(ptr)), true => None, } } /// Creates a new, unconnected stream with the specified name and sample type, and specify the /// initial stream property list. /// /// # Params /// /// * `ctx`: The context to create this stream in /// * `name`: A name for this stream /// * `ss`: The desired sample format /// * `map`: The desired channel map, or `None` for default /// * `proplist`: The initial property list pub fn new_with_proplist(ctx: &mut Context, name: &str, ss: &sample::Spec, map: Option<&channelmap::Map>, proplist: &mut Proplist) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let p_map = map.map_or(null::(), |m| m.as_ref()); let ptr = unsafe { capi::pa_stream_new_with_proplist(ctx.ptr, c_name.as_ptr(), ss.as_ref(), p_map, proplist.0.ptr) }; match ptr.is_null() { false => Some(Self::from_raw(ptr)), true => None, } } /// Creates a new, unconnected stream with the specified name, the set of formats this client /// can provide, and an initial list of properties. /// /// While connecting, the server will select the most appropriate format which the client must /// then provide. /// /// # Params /// /// * `ctx`: The context to create this stream in /// * `name`: A name for this stream /// * `formats`: The list of formats that can be provided /// * `proplist`: The initial property list pub fn new_extended(ctx: &mut Context, name: &str, formats: &[&format::Info], proplist: &mut Proplist) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); // Create array of format::InfoInternal pointers from provided array of `Info` pointers. let mut info_ptrs: Vec<*const capi::pa_format_info> = Vec::with_capacity(formats.len()); for format in formats { info_ptrs.push(format.ptr as *const capi::pa_format_info); } let ptr = unsafe { capi::pa_stream_new_extended(ctx.ptr, c_name.as_ptr(), info_ptrs.as_ptr(), info_ptrs.len() as u32, proplist.0.ptr) }; match ptr.is_null() { false => Some(Self::from_raw(ptr)), true => None, } } /// Creates a new `Stream` from an existing [`StreamInternal`] pointer. #[inline] fn from_raw(ptr: *mut StreamInternal) -> Self { assert_eq!(false, ptr.is_null()); Self { ptr: ptr, cb_ptrs: Default::default() } } /// Gets the current state of the stream. #[inline] pub fn get_state(&self) -> State { unsafe { capi::pa_stream_get_state(self.ptr).into() } } /// Gets the sink input resp. source output index this stream is identified in the server with. /// /// This is useful with the introspection functions such as /// [`Introspector::get_sink_input_info()`] or [`Introspector::get_source_output_info()`]. /// /// [`Introspector::get_sink_input_info()`]: crate::context::introspect::Introspector::get_sink_input_info /// [`Introspector::get_source_output_info()`]: crate::context::introspect::Introspector::get_source_output_info pub fn get_index(&self) -> Option { match unsafe { capi::pa_stream_get_index(self.ptr) } { def::INVALID_INDEX => None, r => Some(r), } } /// Gets the index of the sink or source this stream is connected to in the server. /// /// This is useful with the introspection functions such as /// [`Introspector::get_sink_info_by_index()`] or [`Introspector::get_source_info_by_index()`]. /// /// Please note that streams may be moved between sinks/sources and thus it is recommended to /// use [`set_moved_callback()`] to be notified about this. /// /// [`set_moved_callback()`]: Self::set_moved_callback /// [`Introspector::get_sink_info_by_index()`]: crate::context::introspect::Introspector::get_sink_info_by_index /// [`Introspector::get_source_info_by_index()`]: crate::context::introspect::Introspector::get_source_info_by_index pub fn get_device_index(&self) -> Option { match unsafe { capi::pa_stream_get_device_index(self.ptr) } { def::INVALID_INDEX => None, r => Some(r), } } /// Gets the name of the sink or source this stream is connected to in the server. /// /// This is useful with the introspection functions such as /// [`Introspector::get_sink_info_by_name()`] or [`Introspector::get_source_info_by_name()`]. /// /// Please note that streams may be moved between sinks/sources and thus it is recommended to /// use [`set_moved_callback()`] to be notified about this. /// /// [`set_moved_callback()`]: Self::set_moved_callback /// [`Introspector::get_sink_info_by_name()`]: crate::context::introspect::Introspector::get_sink_info_by_name /// [`Introspector::get_source_info_by_name()`]: crate::context::introspect::Introspector::get_source_info_by_name pub fn get_device_name(&self) -> Option> { let ptr: *const c_char = unsafe { capi::pa_stream_get_device_name(self.ptr) }; match ptr.is_null() { false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }), true => None, } } /// Checks whether or not the sink or source this stream is connected to has been suspended. pub fn is_suspended(&self) -> Result { match unsafe { capi::pa_stream_is_suspended(self.ptr) } { 0 => Ok(false), 1 => Ok(true), e => Err(PAErr(e)), } } /// Checks whether or not this stream has been corked. pub fn is_corked(&self) -> Result { match unsafe { capi::pa_stream_is_corked(self.ptr) } { 0 => Ok(false), 1 => Ok(true), e => Err(PAErr(e)), } } /// Connects the stream to a sink. /// /// It is strongly recommended to pass `None` in both `dev` and `volume` and to set neither /// [`FlagSet::START_MUTED`] nor [`FlagSet::START_UNMUTED`] -- unless these options are directly /// dependent on user input or configuration. /// /// If you follow this rule then the sound server will have the full flexibility to choose the /// device, volume and mute status automatically, based on server-side policies, heuristics and /// stored information from previous uses. Also the server may choose to reconfigure audio /// devices to make other sinks/sources or capabilities available to be able to accept the /// stream. /// /// Before PA 0.9.20 it was not defined whether the ‘volume’ parameter was interpreted relative /// to the sink’s current volume or treated as an absolute device volume. Since PA 0.9.20 it is /// an absolute volume when the sink is in flat volume mode, and relative otherwise, thus making /// sure the volume passed here has always the same semantics as the volume passed to /// [`Introspector::set_sink_input_volume()`]. It is possible to figure out whether flat volume /// mode is in effect for a given sink by calling [`Introspector::get_sink_info_by_name()`]. /// /// Since PA 5.0, it’s possible to specify a single-channel volume even if the stream has /// multiple channels. In that case the same volume is applied to all channels. /// /// # Params /// /// * `dev`: Name of the sink to connect to, or `None` to let the server decide /// * `attr`: Buffering attributes, or `None` for default /// * `flags`: Additional flags, or `0` for default /// * `volume`: Initial volume, or `None` for default /// * `sync_stream`: Synchronize this stream with the specified one, or `None` for a standalone /// stream. /// /// [`Introspector::set_sink_input_volume()`]: crate::context::introspect::Introspector::set_sink_input_volume /// [`Introspector::get_sink_info_by_name()`]: crate::context::introspect::Introspector::get_sink_info_by_name pub fn connect_playback(&mut self, dev: Option<&str>, attr: Option<&def::BufferAttr>, flags: FlagSet, volume: Option<&ChannelVolumes>, sync_stream: Option<&mut Self>) -> Result<(), PAErr> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_dev = match dev { Some(dev) => CString::new(dev.clone()).unwrap(), None => CString::new("").unwrap(), }; let p_attr = attr.map_or(null::(), |a| a.as_ref()); let p_vol = volume.map_or(null::(), |v| v.as_ref()); let p_sync = sync_stream.map_or(null_mut::(), |s| s.ptr); let p_dev = dev.map_or(null::(), |_| c_dev.as_ptr() as *const c_char); let r = unsafe { capi::pa_stream_connect_playback(self.ptr, p_dev, p_attr, flags.bits(), p_vol, p_sync) }; match r { 0 => Ok(()), e => Err(PAErr(e)), } } /// Connects the stream to a source. /// /// # Params /// /// * `dev`: Name of the source to connect to, or `None` to let the server decide /// * `attr`: Buffering attributes, or `None` for default /// * `flags`: Additional flags, or `0` for default pub fn connect_record(&mut self, dev: Option<&str>, attr: Option<&def::BufferAttr>, flags: FlagSet) -> Result<(), PAErr> { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_dev = match dev { Some(dev) => CString::new(dev.clone()).unwrap(), None => CString::new("").unwrap(), }; let p_attr = attr.map_or(null::(), |a| a.as_ref()); let p_dev = dev.map_or(null::(), |_| c_dev.as_ptr() as *const c_char); match unsafe { capi::pa_stream_connect_record(self.ptr, p_dev, p_attr, flags.bits()) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Makes this stream a sample upload stream. /// /// (See [`scache`](mod@crate::context::scache)). pub fn connect_upload(&mut self, length: usize) -> Result<(), PAErr> { match unsafe { capi::pa_stream_connect_upload(self.ptr, length) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Finishes the sample upload, the stream name will become the sample name. /// /// You cancel a sample upload by issuing [`disconnect()`](Self::disconnect). pub fn finish_upload(&mut self) -> Result<(), PAErr> { match unsafe { capi::pa_stream_finish_upload(self.ptr) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Disconnects a stream from a source/sink. pub fn disconnect(&mut self) -> Result<(), PAErr> { match unsafe { capi::pa_stream_disconnect(self.ptr) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Prepares writing data to the server (for playback streams). /// /// This function may be used to optimize the number of memory copies when doing playback /// (“zero-copy”). It is recommended to call this function before each call to [`write()`]. It /// is used to obtain a chunk of PA internally allocated memory, into which you can directly /// write your data before calling [`write()`] to actually execute the write. /// /// This function should be called with `nbytes` set to the number of bytes you want to write, /// or `None`, in which case the size will be chosen automatically (which is recommended). /// /// The return value is a `Result` type, with the `Ok` variant wrapping an `Option`. `Err` will /// be returned if PA encountered an error; `Ok(None)` will be returned if it appeared to be /// successful, but the pointer returned was `NULL`, otherwise the buffer will be returned as /// `Ok(Some(_))`. /// /// After placing your data in the memory area returned, call [`write()`] with a sub-slice of /// it, to actually execute the write. **Note**, the buffer may only be used once, i.e. if you /// were thinking of getting a large buffer, placing a large chunk of data into it, then perform /// multiple small writes from it, you **cannot** do this. Any attempt at accessing the memory /// returned after the following [`write()`] or [`cancel_write()`] is invalid. /// /// If you want to cancel a previously called `begin_write()` without calling [`write()`] use /// [`cancel_write()`]. /// /// The memory should **not** be explicitly freed by the caller. /// /// An invocation of [`write()`] should “quickly” follow a `begin_write()`. It is not /// recommended letting an unbounded amount of time pass after calling `begin_write()` and /// before calling [`write()`]. Calling `begin_write()` twice without calling [`write()`] or /// [`cancel_write()`] in between will return exactly the same `data` pointer and `nbytes` /// values. /// /// [`write()`]: Self::write /// [`cancel_write()`]: Self::cancel_write pub fn begin_write<'a>(&mut self, nbytes: Option) -> Result, PAErr> { let mut data_ptr = null_mut::(); // If user asks for size to be automatically chosen by PA, we pass in std::usize::MAX // (-1 as size_t) to signal this. let mut nbytes_tmp = nbytes.unwrap_or(std::usize::MAX); match unsafe { capi::pa_stream_begin_write(self.ptr, &mut data_ptr, &mut nbytes_tmp) } { 0 => match data_ptr.is_null() { true => Ok(None), false => { let slice = unsafe { std::slice::from_raw_parts_mut(data_ptr as *mut u8, nbytes_tmp) }; Ok(Some(slice)) }, }, e => Err(PAErr(e)), } } /// Reverses the effect of [`begin_write()`] dropping any data that has already been placed in /// the memory area returned by [`begin_write()`]. /// /// Only valid to call after a call to [`begin_write()`] has been made, and neither /// `cancel_write()` nor [`write()`] have been called yet. Accessing the memory previously /// returned by [`begin_write()`] after calling this function is invalid. /// /// [`write()`]: Self::write /// [`begin_write()`]: Self::begin_write pub fn cancel_write(&mut self) -> Result<(), PAErr> { match unsafe { capi::pa_stream_cancel_write(self.ptr) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Writes some data to the server (for playback streams). /// /// If `free_cb` is provided, this routine is called when all data has been written out. An /// internal reference to the specified data is kept, the data is not copied. If `None`, the /// data is copied into an internal buffer. /// /// The client may freely seek around in the output buffer. For most applications it is typical /// to pass `0` and [`SeekMode::Relative`] as values for the arguments `offset` and `seek` /// respectively. After a successful write call the write index will be at the position after /// where this chunk of data has been written to. /// /// As an optimization for avoiding needless memory copies you may call [`begin_write()`] before /// this call and then place your audio data directly in the memory area returned by that call. /// Then, pass a pointer to that memory area to `write()`. After the invocation of `write()` the /// memory area may no longer be accessed. Any further explicit freeing of the memory area is /// not necessary. It is OK to write to the memory area returned by [`begin_write()`] only /// partially with this call, skipping bytes both at the end and at the beginning of the /// reserved memory area. /// /// # Params /// /// * `data`: The data to write. The length must be in multiples of the stream’s sample spec /// frame size. /// * `free_cb`: A cleanup routine for the data or `None` to request an internal copy of the /// data. /// * `offset`: Offset for seeking. Must be `0` for upload streams. Must be in multiples of the /// stream’s sample spec frame size. /// * `seek`: Seek mode. Must be [`SeekMode::Relative`] for upload streams. /// /// [`begin_write()`]: Self::begin_write pub fn write(&mut self, data: &[u8], free_cb: Option, offset: i64, seek: SeekMode) -> Result<(), PAErr> { debug_assert_eq!(0, data.len().checked_rem(self.get_sample_spec().unwrap().frame_size()) .unwrap()); let r = unsafe { capi::pa_stream_write(self.ptr, data.as_ptr() as *const c_void, data.len(), free_cb, offset, seek) }; match r { 0 => Ok(()), e => Err(PAErr(e)), } } /// Writes some data to the server (for playback streams). /// /// This function does exactly the same as [`write()`] as though `None` had been specified for /// the `free_cb` param. I.e. an internal copy will be made of the provided data. /// /// # Params /// /// * `data`: The data to write. The length must be in multiples of the stream’s sample spec /// frame size. /// * `offset`: Offset for seeking. Must be `0` for upload streams. Must be in multiples of the /// stream’s sample spec frame size. /// * `seek`: Seek mode. Must be [`SeekMode::Relative`] for upload streams. /// /// [`write()`]: Self::write #[inline(always)] pub fn write_copy(&mut self, data: &[u8], offset: i64, seek: SeekMode) -> Result<(), PAErr> { self.write(data, None, offset, seek) } /// Writes some data to the server (for playback streams). /// /// This function does exactly the same as [`write()`] with the only difference being that a /// void pointer is provided along with the `free_cb` callback pointer, and this void pointer /// will be passed to the callback instead of the `data` pointer. /// /// # Params /// /// * `data`: The data to write. The length must be in multiples of the stream’s sample spec /// frame size. /// * `free_cb`: A cleanup routine for the data or `None` to request an internal copy of the /// data. If provided, the accompanying data pointer will be supplied to the callback. /// * `offset`: Offset for seeking. Must be `0` for upload streams. /// * `seek`: Seek mode, must be [`SeekMode::Relative`] for upload streams. /// /// [`write()`]: Self::write #[cfg(any(doc, feature = "pa_v6"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v6")))] pub fn write_ext_free(&mut self, data: &[u8], free_cb: Option<(def::FreeCb, *mut c_void)>, offset: i64, seek: SeekMode) -> Result<(), PAErr> { let (cb_f, cb_d) = match free_cb { Some((f, d)) => (Some(f), d), None => (None, null_mut::()), }; debug_assert_eq!(0, data.len().checked_rem(self.get_sample_spec().unwrap().frame_size()) .unwrap()); let r = unsafe { capi::pa_stream_write_ext_free(self.ptr, data.as_ptr() as *const c_void, data.len(), cb_f, cb_d, offset, seek.into()) }; match r { 0 => Ok(()), e => Err(PAErr(e)), } } /// Reads the next fragment from the buffer (for recording streams). /// /// This function returns one of the [`PeekResult`] variants - either [`Empty`], [`Hole`] or /// [`Data`]: /// /// * If there is data at the current read index, the [`Data`] variant will be returned, which /// contains a slice giving a view of the data. (The length of this slice can be less or more /// than a complete fragment). This is pointing into an internal buffer, so obviously you /// must make a copy of it if you want to keep it. /// * If there is no data at the current read index, it means that either the buffer is empty /// or it contains a hole (that is, the write index is ahead of the read index but there’s no /// data where the read index points at). If the buffer is empty, the [`Empty`] result /// variant will be returned. If there is a hole, the [`Hole`] variant will be returned, /// containing the length of the hole in bytes. /// /// Use [`discard()`] to actually remove the data from the buffer and move the read index /// forward. [`discard()`] should not be called if the buffer is empty, but it should be called /// if there is a hole. /// /// [`Empty`]: PeekResult::Empty /// [`Hole`]: PeekResult::Hole /// [`Data`]: PeekResult::Data /// [`discard()`]: Self::discard pub fn peek<'a>(&mut self) -> Result, PAErr> { let mut data_ptr = null::(); let mut nbytes: usize = 0; // Note, C function returns an i32, but documentation does not mention any use of it, so we // discard it. match unsafe { capi::pa_stream_peek(self.ptr, &mut data_ptr, &mut nbytes) } { 0 => { if data_ptr.is_null() { match nbytes { 0 => Ok(PeekResult::Empty), _ => Ok(PeekResult::Hole(nbytes)), } } else { let slice = unsafe { std::slice::from_raw_parts(data_ptr as *const u8, nbytes) }; Ok(PeekResult::Data(slice)) } }, e => Err(PAErr(e)), } } /// Removes the current fragment on record streams. /// /// It is invalid to do this without first calling [`peek()`](Self::peek). /// /// Note: The original C function name used the term `drop`; We instead use `discard` here to /// avoid conflict with the Rust `Drop` trait! pub fn discard(&mut self) -> Result<(), PAErr> { match unsafe { capi::pa_stream_drop(self.ptr) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Gets the number of bytes requested by the server that have not yet been written. /// /// It is possible to write more than this amount, up to the stream’s [`buffer_attr.maxlength`] /// bytes. This is usually not desirable, though, as it would increase stream latency to be /// higher than requested ([`buffer_attr.tlength`]). /// /// [`buffer_attr.maxlength`]: crate::def::BufferAttr.maxlength /// [`buffer_attr.tlength`]: crate::def::BufferAttr.tlength pub fn writable_size(&self) -> Option { match unsafe { capi::pa_stream_writable_size(self.ptr) } { std::usize::MAX => None, r => Some(r), } } /// Gets the number of bytes that may be read using [`peek()`](Self::peek). /// /// Returns `None` on error. pub fn readable_size(&self) -> Option { match unsafe { capi::pa_stream_readable_size(self.ptr) } { std::usize::MAX => None, r => Some(r), } } /// Drains a playback stream. /// /// Use this for notification when the playback buffer is empty after playing all the audio in /// the buffer. Please note that only one drain operation per stream may be issued at a time. /// /// The optional callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn drain(&mut self, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, success_cb_proxy); let ptr = unsafe { capi::pa_stream_drain(self.ptr, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Requests a timing info structure update for a stream. /// /// Use [`get_timing_info()`] to get access to the raw timing data, or [`get_time()`] or /// [`get_latency()`] to get cleaned up values. /// /// The optional callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. /// /// [`get_timing_info()`]: Self::get_timing_info /// [`get_time()`]: Self::get_time /// [`get_latency()`]: Self::get_latency pub fn update_timing_info(&mut self, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, success_cb_proxy); let ptr = unsafe { capi::pa_stream_update_timing_info(self.ptr, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Sets the callback function that is called whenever the state of the stream changes. pub fn set_state_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.set_state; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy); unsafe { capi::pa_stream_set_state_callback(self.ptr, cb_fn, cb_data); } } /// Sets the callback function that is called when new data may be written to the stream. /// /// The callback accepts an argument giving the number of bytes. pub fn set_write_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.write; *saved = RequestCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(request_cb_proxy); unsafe { capi::pa_stream_set_write_callback(self.ptr, cb_fn, cb_data); } } /// Sets the callback function that is called when new data is available from the stream. /// /// The callback accepts an argument giving the number of bytes. pub fn set_read_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.read; *saved = RequestCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(request_cb_proxy); unsafe { capi::pa_stream_set_read_callback(self.ptr, cb_fn, cb_data); } } /// Sets the callback function that is called when a buffer overflow happens. (Only for playback /// streams). pub fn set_overflow_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.overflow; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy); unsafe { capi::pa_stream_set_overflow_callback(self.ptr, cb_fn, cb_data); } } /// Gets at what position the latest underflow occurred. /// /// `None` is returned if this information is not known (e.g. if no underflow has occurred). /// /// This can be used inside the underflow callback to get information about the current /// underflow. (Only for playback streams). pub fn get_underflow_index(&self) -> Option { match unsafe { capi::pa_stream_get_underflow_index(self.ptr) } { r if r < 0 => None, r => Some(r as u64), } } /// Sets the callback function that is called when a buffer underflow happens. /// /// (Only for playback streams). pub fn set_underflow_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.underflow; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy); unsafe { capi::pa_stream_set_underflow_callback(self.ptr, cb_fn, cb_data); } } /// Sets the callback function that is called when the server starts playback after an underrun /// or on initial startup. /// /// This only informs that audio is flowing again, it is no indication that audio started to /// reach the speakers already. (Only for playback streams). pub fn set_started_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.started; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy); unsafe { capi::pa_stream_set_started_callback(self.ptr, cb_fn, cb_data); } } /// Sets the callback function that is called whenever a latency information update happens. /// /// Useful on [`FlagSet::AUTO_TIMING_UPDATE`] streams only. pub fn set_latency_update_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.latency_update; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy); unsafe { capi::pa_stream_set_latency_update_callback(self.ptr, cb_fn, cb_data); } } /// Sets the callback function that is called whenever the stream is moved to a different /// sink/source. /// /// Use [`get_device_name()`] or [`get_device_index()`] to query the new sink/source. /// /// [`get_device_name()`]: Self::get_device_name /// [`get_device_index()`]: Self::get_device_index pub fn set_moved_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.moved; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy); unsafe { capi::pa_stream_set_moved_callback(self.ptr, cb_fn, cb_data); } } /// Sets the callback function that is called whenever the sink/source this stream is connected /// to is suspended or resumed. /// /// Use [`is_suspended()`] to query the new suspend status. Please note that the suspend status /// might also change when the stream is moved between devices. Thus if you call this function /// you very likely want to call [`set_moved_callback()`] too. /// /// [`is_suspended()`]: Self::is_suspended /// [`set_moved_callback()`]: Self::set_moved_callback pub fn set_suspended_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.suspended; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy); unsafe { capi::pa_stream_set_suspended_callback(self.ptr, cb_fn, cb_data); } } /// Sets the callback function that is called whenever a meta/policy control event is received. /// /// The callback is given a name which represents what event occurred. The set of defined events /// can be extended at any time. Also, server modules may introduce additional message types so /// make sure that your callback function ignores messages it doesn’t know. Some well known /// event names can be found in the [`event_names`](mod@self::event_names) submodule. It is also /// given an (owned) property list. pub fn set_event_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.event; *saved = EventCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(event_cb_proxy); unsafe { capi::pa_stream_set_event_callback(self.ptr, cb_fn, cb_data); } } /// Sets the callback function that is called whenever the buffer attributes on the server side /// change. /// /// Please note that the buffer attributes can change when moving a stream to a different /// sink/source too, hence if you use this callback you should use [`set_moved_callback()`] as /// well. /// /// [`set_moved_callback()`]: Self::set_moved_callback pub fn set_buffer_attr_callback(&mut self, callback: Option>) { let saved = &mut self.cb_ptrs.buffer_attr; *saved = NotifyCb::new(callback); let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy); unsafe { capi::pa_stream_set_buffer_attr_callback(self.ptr, cb_fn, cb_data); } } /// Pauses playback of this stream temporarily. /// /// Available on both playback and recording streams. /// /// The pause operation is executed as quickly as possible. If a cork is very quickly followed /// by an uncork, this might not actually have any effect on the stream that is output. You can /// use [`is_corked()`] to find out whether the stream is currently paused or not. Normally a /// stream will be created in uncorked state. If you pass [`FlagSet::START_CORKED`] as a flag /// when connecting the stream, it will be created in corked state. /// /// The optional callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. /// /// [`is_corked()`]: Self::is_corked pub fn cork(&mut self, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, success_cb_proxy); let ptr = unsafe { capi::pa_stream_cork(self.ptr, true as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Resumes playback of this stream. /// /// Available on both playback and recording streams. /// /// The un-pause operation is executed as quickly as possible. If an uncork is very quickly /// followed by a cork, this might not actually have any effect on the stream that is output. /// You can use [`is_corked()`] to find out whether the stream is currently paused or not. /// Normally a stream will be created in uncorked state. If you pass [`FlagSet::START_CORKED`] /// as a flag when connecting the stream, it will be created in corked state. /// /// The optional callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. /// /// [`is_corked()`]: Self::is_corked pub fn uncork(&mut self, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, success_cb_proxy); let ptr = unsafe { capi::pa_stream_cork(self.ptr, false as i32, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Flushes the playback or record buffer of this stream. /// /// This discards any audio data in the buffer. Most of the time you’re better off using the /// parameter `seek` of [`write()`](Self::write) instead of this function. /// /// The optional callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn flush(&mut self, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, success_cb_proxy); let ptr = unsafe { capi::pa_stream_flush(self.ptr, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Re-enables prebuffering if specified in the [`BufferAttr`] structure. /// /// Available for playback streams only. /// /// The optional callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. /// /// [`BufferAttr`]: crate::def::BufferAttr pub fn prebuf(&mut self, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, success_cb_proxy); let ptr = unsafe { capi::pa_stream_prebuf(self.ptr, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Requests immediate start of playback on this stream. /// /// This disables prebuffering temporarily if specified in the [`BufferAttr`] structure. /// Available for playback streams only. /// /// The optional callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. /// /// [`BufferAttr`]: crate::def::BufferAttr pub fn trigger(&mut self, callback: Option>) -> Operation { let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, success_cb_proxy); let ptr = unsafe { capi::pa_stream_trigger(self.ptr, cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Renames the stream. /// /// The optional callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn set_name(&mut self, name: &str, callback: Option>) -> Operation { // Warning: New CStrings will be immediately freed if not bound to a // variable, leading to as_ptr() giving dangling pointers! let c_name = CString::new(name.clone()).unwrap(); let (cb_fn, cb_data): (Option, _) = get_su_capi_params::<_, _>(callback, success_cb_proxy); let ptr = unsafe { capi::pa_stream_set_name(self.ptr, c_name.as_ptr(), cb_fn, cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Gets the current playback/recording time. /// /// This is based on the data in the timing info structure returned by [`get_timing_info()`]. /// The returned time is in the sound card clock domain, which usually runs at a slightly /// different rate than the system clock. /// /// This function will usually only return new data if a timing info update has been received. /// Only if timing interpolation has been requested ([`FlagSet::INTERPOLATE_TIMING`]) the data /// from the last timing update is used for an estimation of the current playback/recording time /// based on the local time that passed since the timing info structure has been acquired. /// /// The time value returned by this function is guaranteed to increase monotonically (the /// returned value is always greater or equal to the value returned by the last call). This /// behaviour can be disabled by using [`FlagSet::NOT_MONOTONIC`]. This may be desirable to /// better deal with bad estimations of transport latencies, but may have strange effects if the /// application is not able to deal with time going ‘backwards’. /// /// The time interpolator activated by [`FlagSet::INTERPOLATE_TIMING`] favours ‘smooth’ time /// graphs over accurate ones to improve the smoothness of UI operations that are tied to the /// audio clock. If accuracy is more important to you, you might need to estimate your timing /// based on the data from [`get_timing_info()`] yourself or not work with interpolated timing /// at all and instead always query the server side for the most up to date timing with /// [`update_timing_info()`]. /// /// If no timing information has been received yet this call will return `Ok(None)`. For more /// details see [`get_timing_info()`]. /// /// [`get_timing_info()`]: Self::get_timing_info /// [`update_timing_info()`]: Self::update_timing_info pub fn get_time(&self) -> Result, PAErr> { let mut r_usecs = MicroSeconds(0); match unsafe { capi::pa_stream_get_time(self.ptr, &mut r_usecs.0) } { 0 => Ok(Some(r_usecs)), e if e == PAErr::from(error::Code::NoData).0 => Ok(None), e => Err(PAErr(e)), } } /// Determines the total stream latency. /// /// This function is based on [`get_time()`]. The returned time is in the sound card clock /// domain, which usually runs at a slightly different rate than the system clock. /// /// In case the stream is a monitoring stream the result can be negative, i.e. the captured /// samples are not yet played, in which case `Ok(Latency::Negative(usecs))` will be returned /// instead of `Ok(Latency::Positive(usecs))` /// /// If no timing information has been received yet, this call will return `Ok(Latency::None)`. /// /// For more details see [`get_timing_info()`] and [`get_time()`]. /// /// [`get_time()`]: Self::get_time /// [`get_timing_info()`]: Self::get_timing_info pub fn get_latency(&self) -> Result { let mut r_usecs = MicroSeconds(0); let mut negative: i32 = 0; match unsafe { capi::pa_stream_get_latency(self.ptr, &mut r_usecs.0, &mut negative) } { 0 => match negative { 1 => Ok(Latency::Negative(r_usecs)), _ => Ok(Latency::Positive(r_usecs)), }, e if e == PAErr::from(error::Code::NoData).0 => Ok(Latency::None), e => Err(PAErr(e)), } } /// Gets the latest raw timing data structure. /// /// The returned pointer refers to an internal read-only instance of the timing structure. The /// user should make a copy of this structure if wanting to modify it. An in-place update to /// this data structure may be requested using [`update_timing_info()`]. /// /// If no timing information has been received before (i.e. by requesting /// [`update_timing_info()`] or by using [`FlagSet::AUTO_TIMING_UPDATE`]), this function will /// return `None` (as it will also if an error occurs). /// /// Please note that the `write_index` member field (and only this field) is updated on each /// [`write()`] call, not just when a timing update has been received. /// /// [`update_timing_info()`]: Self::update_timing_info /// [`write()`]: Self::write pub fn get_timing_info<'a>(&mut self) -> Option<&'a def::TimingInfo> { unsafe { let ptr = capi::pa_stream_get_timing_info(self.ptr); ptr.as_ref().map(|r| r.as_ref()) } } /// Gets a pointer to the stream’s sample specification. pub fn get_sample_spec<'a>(&mut self) -> Option<&'a sample::Spec> { unsafe { let ptr = capi::pa_stream_get_sample_spec(self.ptr); ptr.as_ref().map(|r| r.as_ref()) } } /// Gets a pointer to the stream’s channel map. pub fn get_channel_map<'a>(&mut self) -> Option<&'a channelmap::Map> { unsafe { let ptr = capi::pa_stream_get_channel_map(self.ptr); ptr.as_ref().map(|r| r.as_ref()) } } /// Gets a pointer to the stream’s format. pub fn get_format_info(&self) -> Option { let ptr = unsafe { capi::pa_stream_get_format_info(self.ptr) }; match ptr.is_null() { false => Some(format::Info::from_raw_weak(ptr as *mut InfoInternal)), true => None, } } /// Gets the per-stream server-side buffer metrics of the stream. /// /// Only valid after the stream has been connected successfully. This will return the actual /// configured buffering metrics, which may differ from what was requested during /// [`connect_record()`] or [`connect_playback()`]. This call will always return the actual /// per-stream server-side buffer metrics, regardless whether [`FlagSet::ADJUST_LATENCY`] is set /// or not. /// /// [`connect_record()`]: Self::connect_record /// [`connect_playback()`]: Self::connect_playback pub fn get_buffer_attr<'a>(&mut self) -> Option<&'a def::BufferAttr> { unsafe { let ptr = capi::pa_stream_get_buffer_attr(self.ptr); ptr.as_ref().map(|r| r.as_ref()) } } /// Changes the buffer metrics of the stream during playback. /// /// The server might have chosen different buffer metrics then requested. The selected metrics /// may be queried with [`get_buffer_attr()`] as soon as the callback is called. Only valid /// after the stream has been connected successfully. Please be aware of the slightly different /// semantics of the call depending whether [`FlagSet::ADJUST_LATENCY`] is set or not. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. /// /// [`get_buffer_attr()`]: Self::get_buffer_attr pub fn set_buffer_attr(&mut self, attr: &def::BufferAttr, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_stream_set_buffer_attr(self.ptr, attr.as_ref(), Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Changes the stream sampling rate during playback. /// /// You need to pass [`FlagSet::VARIABLE_RATE`] in the flags parameter of [`connect_playback()`] /// if you plan to use this function. Only valid after the stream has been connected /// successfully. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. /// /// [`connect_playback()`]: Self::connect_playback pub fn update_sample_rate(&mut self, rate: u32, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_stream_update_sample_rate(self.ptr, rate, Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Updates the property list of the sink input/source output of this stream, adding new /// entries. /// /// Please note that it is highly recommended to set as many properties initially via /// [`new_with_proplist()`] as possible instead a posteriori with this function, since that /// information may be used to route this stream to the right device. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. /// /// [`new_with_proplist()`]: Self::new_with_proplist pub fn update_proplist(&mut self, mode: proplist::UpdateMode, proplist: &mut Proplist, callback: F) -> Operation where F: FnMut(bool) + 'static { let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_stream_proplist_update(self.ptr, mode, proplist.0.ptr, Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// Updates the property list of the sink input/source output of this stream, removing entries. /// /// The callback must accept a `bool`, which indicates success. /// /// Panics if the underlying C function returns a null pointer. pub fn remove_proplist(&mut self, keys: &[&str], callback: F) -> Operation where F: FnMut(bool) + 'static { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let mut c_keys: Vec = Vec::with_capacity(keys.len()); for key in keys { c_keys.push(CString::new(*key).unwrap()); } // Capture array of pointers to the above CString values // We also add a null pointer entry on the end, as expected by the C function called here. let mut c_key_ptrs: Vec<*const c_char> = Vec::with_capacity(c_keys.len() + 1); for c_key in &c_keys { c_key_ptrs.push(c_key.as_ptr()); } c_key_ptrs.push(null()); let cb_data = box_closure_get_capi_ptr::(Box::new(callback)); let ptr = unsafe { capi::pa_stream_proplist_remove(self.ptr, c_key_ptrs.as_ptr(), Some(success_cb_proxy), cb_data) }; Operation::from_raw(ptr, cb_data as *mut Box) } /// For record streams connected to a monitor source: monitors only a very specific sink input /// of the sink. /// /// This function needs to be called before [`connect_record()`](Self::connect_record) is /// called. pub fn set_monitor_stream(&mut self, sink_input_index: u32) -> Result<(), PAErr> { match unsafe { capi::pa_stream_set_monitor_stream(self.ptr, sink_input_index) } { 0 => Ok(()), e => Err(PAErr(e)), } } /// Gets the sink input index previously set with [`set_monitor_stream()`]. /// /// [`set_monitor_stream()`]: Self::set_monitor_stream pub fn get_monitor_stream(&self) -> Option { match unsafe { capi::pa_stream_get_monitor_stream(self.ptr) } { def::INVALID_INDEX => None, r => Some(r), } } } impl Drop for Stream { fn drop(&mut self) { // Throw away the `Result` from disconnecting, it may legitimately be bad if stream failed. // See https://github.com/jnqnfe/pulse-binding-rust/issues/11 let _ = self.disconnect(); unsafe { capi::pa_stream_unref(self.ptr) }; self.ptr = null_mut::(); } } /// Proxy for completion success callbacks. /// /// Warning: This is for single-use cases only! It destroys the actual closure callback. extern "C" fn success_cb_proxy(_: *mut StreamInternal, success: i32, userdata: *mut c_void) { let success_actual = match success { 0 => false, _ => true }; let _ = std::panic::catch_unwind(|| { // Note, destroys closure callback after use - restoring outer box means it gets dropped let mut callback = get_su_callback::(userdata); (callback)(success_actual); }); } /// Proxy for request callbacks. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn request_cb_proxy(_: *mut StreamInternal, nbytes: usize, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = RequestCb::get_callback(userdata); (callback)(nbytes); }); } /// Proxy for notify callbacks. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn notify_cb_proxy(_: *mut StreamInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { let callback = NotifyCb::get_callback(userdata); (callback)(); }); } /// Proxy for event callbacks. /// /// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which /// must be accomplished separately to avoid a memory leak. extern "C" fn event_cb_proxy(_: *mut StreamInternal, name: *const c_char, proplist: *mut ProplistInternal, userdata: *mut c_void) { let _ = std::panic::catch_unwind(|| { assert!(!name.is_null()); let n = { let tmp = unsafe { CStr::from_ptr(name) }; tmp.to_string_lossy().into_owned() }; let pl = Proplist::from_raw_weak(proplist); let callback = EventCb::get_callback(userdata); (callback)(n, pl); }); } libpulse-binding-2.28.1/src/time/convert.rs000064400000000000000000000051011046102023000167150ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Implementation of conversions between the types. use super::*; // To `MicroSeconds` impl TryFrom for MicroSeconds { type Error = (); #[inline] fn try_from(t: Duration) -> Result { let secs_as_micros = t.as_secs().checked_mul(MICROS_PER_SEC).ok_or(())?; let total_micros = secs_as_micros.checked_add(t.subsec_micros() as u64).ok_or(())?; Ok(MicroSeconds(total_micros)) } } impl From for MicroSeconds { #[inline] fn from(t: Timeval) -> Self { MicroSeconds(unsafe { capi::pa_timeval_load(&t.0) }) } } // To `Duration` impl From for Duration { #[inline] fn from(t: MicroSeconds) -> Self { Duration::from_micros(t.0) } } impl From for Duration { #[inline] fn from(t: Timeval) -> Self { Duration::from_micros((MicroSeconds::from(t)).0) } } // To `Timeval` impl From for Timeval { #[inline] fn from(t: MicroSeconds) -> Self { let secs = (t.0 / MICROS_PER_SEC) as super::timeval::TvSecs; let micros = (t.0 % MICROS_PER_SEC) as super::timeval::TvUsecs; Timeval::new(secs, micros) } } impl From for Timeval { #[inline] fn from(t: Duration) -> Self { let secs = t.as_secs() as super::timeval::TvSecs; let micros = t.subsec_micros() as super::timeval::TvUsecs; Timeval::new(secs, micros) } } #[test] fn tests() { let micro = MicroSeconds(2_700_000); let duration = Duration::from_micros(2_700_000); let timeval = Timeval::new(2, 700_000); assert_eq!(MicroSeconds::try_from(duration).unwrap(), micro); assert_eq!(MicroSeconds::from(timeval), micro); assert_eq!(Duration::from(micro), duration); assert_eq!(Duration::from(timeval), duration); assert_eq!(Timeval::from(micro), timeval); assert_eq!(Timeval::from(duration), timeval); } libpulse-binding-2.28.1/src/time/microseconds.rs000064400000000000000000000655731046102023000177500ustar 00000000000000// Copyright 2018 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! MicroSeconds. use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign}; use std::time::Duration; use super::op_err; /// Microseconds. Represents a span of time like [`std::time::Duration`]. /// /// This is an unsigned 64-bit type, and thus represents absolute values only. #[repr(transparent)] #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] pub struct MicroSeconds(pub u64); impl MicroSeconds { /// `MicroSeconds` value representing an ‘invalid’ time. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::INVALID, MicroSeconds(std::u64::MAX)); /// ``` pub const INVALID: Self = Self(capi::PA_USEC_INVALID); /// One second in microseconds. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::SECOND, MicroSeconds(1_000_000)); /// ``` pub const SECOND: Self = Self(super::MICROS_PER_SEC); /// One millisecond in microseconds. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::MILLISECOND, MicroSeconds(1_000)); /// ``` pub const MILLISECOND: Self = Self(super::MICROS_PER_MILLI); /// Zero value. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::ZERO, MicroSeconds(0)); /// ``` pub const ZERO: Self = Self(0); /// Smallest _valid_ time value (zero). /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::MIN, MicroSeconds(0)); /// ``` pub const MIN: Self = Self(0); /// Largest _valid_ time value (largest integer value is reserved for representing ‘invalid’). /// /// Roughly equal to 5,124,095,576 hours, 213,503,982 days, or 584,542 years. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::MAX, MicroSeconds(std::u64::MAX - 1)); /// ``` pub const MAX: Self = Self(capi::PA_USEC_MAX); /// Get the inner `u64` value. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds(100).inner(), 100); /// ``` #[inline] pub const fn inner(&self) -> u64 { self.0 } /// Returns `true` so long as inner value is not [`Self::INVALID`]. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// assert_eq!(MicroSeconds::MIN.is_valid(), true); /// assert_eq!(MicroSeconds::MAX.is_valid(), true); /// assert_eq!(MicroSeconds::INVALID.is_valid(), false); /// assert_eq!(MicroSeconds::ZERO.is_valid(), true); /// assert_eq!(MicroSeconds(60 * MICROS_PER_SEC).is_valid(), true); /// ``` #[inline] pub const fn is_valid(&self) -> bool { self.0 != Self::INVALID.0 } /// Returns `true` so long as inner value is zero. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::ZERO.is_zero(), true); /// assert_eq!(MicroSeconds(0).is_zero(), true); /// assert_eq!(MicroSeconds(1).is_zero(), false); /// ``` #[inline] pub const fn is_zero(&self) -> bool { self.0 == 0 } /// Creates a new `MicroSeconds` from the specified number of whole seconds. Returns `None` on /// overflow. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::from_secs(2), Some(MicroSeconds(2_000_000))); /// assert_eq!(MicroSeconds::from_secs(0xffff_ffff_0000_0000), None); /// ``` #[inline] pub fn from_secs(secs: u64) -> Option { secs.checked_mul(super::MICROS_PER_SEC).and_then(|i| Some(Self(i))) } /// Creates a new `MicroSeconds` from the specified number of whole milliseconds. Returns `None` /// on overflow. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::from_millis(23), Some(MicroSeconds(23_000))); /// assert_eq!(MicroSeconds::from_millis(0xffff_ffff_0000_0000), None); /// ``` #[inline] pub fn from_millis(millis: u64) -> Option { millis.checked_mul(super::MICROS_PER_MILLI).and_then(|i| Some(Self(i))) } /// Returns the absolute difference with `other`. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds(0).diff(MicroSeconds(0)), MicroSeconds(0)); /// assert_eq!(MicroSeconds(100).diff(MicroSeconds(100)), MicroSeconds(0)); /// assert_eq!(MicroSeconds(200).diff(MicroSeconds(150)), MicroSeconds(50)); /// assert_eq!(MicroSeconds(150).diff(MicroSeconds(200)), MicroSeconds(50)); /// ``` #[inline] pub fn diff(self, other: Self) -> Self { match self >= other { true => Self(self.0 - other.0), false => Self(other.0 - self.0), } } /// Returns the total number of whole seconds. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds(2_300_000).as_secs(), 2); /// assert_eq!(MicroSeconds(2_800_000).as_secs(), 2); /// ``` #[inline] pub const fn as_secs(&self) -> u64 { self.0 / super::MICROS_PER_SEC } /// Returns the total number of whole milliseconds. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds(23_000_300).as_millis(), 23_000); /// assert_eq!(MicroSeconds(23_000_800).as_millis(), 23_000); /// ``` #[inline] pub const fn as_millis(&self) -> u64 { self.0 / super::MICROS_PER_MILLI } /// Creates a new `MicroSeconds` from the specified number of seconds represented as `f64`. /// /// **Panics** if `secs` is not finite, is negative, or the value overflows. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::from_secs_f64(0.5), MicroSeconds(500_000)); /// assert_eq!(MicroSeconds::from_secs_f64(2.3), MicroSeconds(2_300_000)); /// ``` /// /// These should panic. /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::from_secs_f64(std::f64::INFINITY); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::from_secs_f64(-0.5); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::from_secs_f64(std::f64::MAX); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::from_secs_f64(std::f64::NAN); /// ``` #[inline] pub fn from_secs_f64(secs: f64) -> Self { let duration = Duration::from_secs_f64(secs); Self::try_from(duration).expect("overflow during microseconds conversion") } /// Creates a new `MicroSeconds` from the specified number of seconds represented as `f32`. /// /// **Panics** if `secs` is not finite, is negative, or the value overflows. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds::from_secs_f32(0.5), MicroSeconds(500_000)); /// assert_eq!(MicroSeconds::from_secs_f32(2.3), MicroSeconds(2_299_999)); /// ``` /// /// These should panic. /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::from_secs_f32(std::f32::INFINITY); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::from_secs_f32(-0.5); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::from_secs_f32(std::f32::MAX); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::from_secs_f32(std::f32::NAN); /// ``` #[inline] pub fn from_secs_f32(secs: f32) -> Self { let duration = Duration::from_secs_f32(secs); Self::try_from(duration).expect("overflow during microseconds conversion") } /// Returns the number of seconds as `f64`. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds(2_300_000).as_secs_f64(), 2.3); /// assert_eq!(MicroSeconds(500_000).as_secs_f64(), 0.5); /// ``` #[inline] pub fn as_secs_f64(&self) -> f64 { (self.0 as f64) / (super::MICROS_PER_SEC as f64) } /// Returns the number of seconds as `f32`. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::MicroSeconds; /// assert_eq!(MicroSeconds(2_300_000).as_secs_f32(), 2.3); /// assert_eq!(MicroSeconds(500_000).as_secs_f32(), 0.5); /// ``` #[inline] pub fn as_secs_f32(&self) -> f32 { (self.0 as f32) / (super::MICROS_PER_SEC as f32) } /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred, /// using the inner integer’s [`checked_add()`](u64::checked_add) method. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// let quater_minute = MicroSeconds(15 * MICROS_PER_SEC); /// let half_minute = MicroSeconds(30 * MICROS_PER_SEC); /// let three_quater_minute = MicroSeconds(45 * MICROS_PER_SEC); /// /// assert_eq!(half_minute.checked_add(quater_minute), Some(three_quater_minute)); /// assert_eq!(MicroSeconds::MAX.checked_add(half_minute), None); /// ``` #[inline] pub fn checked_add(self, other: Self) -> Option { self.0.checked_add(other.0).and_then(|i| Some(Self(i))) } /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred, /// using the inner integer’s [`checked_add()`](u64::checked_add) method. /// /// # Examples /// /// ```rust /// # use std::time::Duration; /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC, NANOS_PER_MICRO}; /// let half_minute = MicroSeconds(30 * MICROS_PER_SEC); /// let duration1 = Duration::new(2, 5 * NANOS_PER_MICRO + 20); // 2s + 5us + 20ns /// let duration2 = Duration::new(MicroSeconds::MAX.inner() / MICROS_PER_SEC, 0); /// /// assert_eq!(half_minute.checked_add_duration(duration1), Some(MicroSeconds(32_000_005))); /// assert_eq!(half_minute.checked_add_duration(duration2), None); /// ``` #[inline] pub fn checked_add_duration(self, rhs: Duration) -> Option { let usecs = Self::try_from(rhs).ok()?; self.0.checked_add(usecs.0).and_then(|i| Some(Self(i))) } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred, /// using the inner integer’s [`checked_sub()`](u64::checked_sub) method. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// let quater_minute = MicroSeconds(15 * MICROS_PER_SEC); /// let three_quater_minute = MicroSeconds(45 * MICROS_PER_SEC); /// let whole_minute = MicroSeconds(60 * MICROS_PER_SEC); /// /// assert_eq!(whole_minute.checked_sub(quater_minute), Some(three_quater_minute)); /// assert_eq!(quater_minute.checked_sub(whole_minute), None); /// ``` #[inline] pub fn checked_sub(self, other: Self) -> Option { self.0.checked_sub(other.0).and_then(|i| Some(Self(i))) } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred, /// using the inner integer’s [`checked_sub()`](u64::checked_sub) method. /// /// # Examples /// /// ```rust /// # use std::time::Duration; /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC, NANOS_PER_MICRO}; /// let half_minute = MicroSeconds(30 * MICROS_PER_SEC); /// let duration1 = Duration::new(2, 5 * NANOS_PER_MICRO + 20); // 2s + 5us + 20ns /// let duration2 = Duration::new(45, 0); /// /// assert_eq!(half_minute.checked_sub_duration(duration1), Some(MicroSeconds(27_999_995))); /// assert_eq!(half_minute.checked_sub_duration(duration2), None); /// ``` #[inline] pub fn checked_sub_duration(self, rhs: Duration) -> Option { let usecs = Self::try_from(rhs).ok()?; self.0.checked_sub(usecs.0).and_then(|i| Some(Self(i))) } /// Checked integer multiplication. Computes `self * rhs`, returning `None` if overflow /// occurred, using the inner integer’s [`checked_mul()`](u64::checked_mul) method. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// let quater_minute = MicroSeconds(15 * MICROS_PER_SEC); /// let whole_minute = MicroSeconds(60 * MICROS_PER_SEC); /// /// assert_eq!(quater_minute.checked_mul(4), Some(whole_minute)); /// assert_eq!(MicroSeconds::MAX.checked_mul(2), None); /// ``` #[inline] pub fn checked_mul(self, rhs: u32) -> Option { self.0.checked_mul(rhs as u64).and_then(|i| Some(Self(i))) } /// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0`, using the /// inner integer’s [`checked_div()`](u64::checked_div) method. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// let quater_minute = MicroSeconds(15 * MICROS_PER_SEC); /// let whole_minute = MicroSeconds(60 * MICROS_PER_SEC); /// /// assert_eq!(whole_minute.checked_div(4), Some(quater_minute)); /// assert_eq!(whole_minute.checked_div(0), None); /// ``` #[inline] pub fn checked_div(self, rhs: u32) -> Option { self.0.checked_div(rhs as u64).and_then(|i| Some(Self(i))) } /// Checked integer remainder. Computes `self % rhs`, returning `None` if `rhs == 0`, using the /// inner integer’s [`checked_rem()`](u64::checked_rem) method. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// let quater_minute = MicroSeconds(15 * MICROS_PER_SEC); /// let whole_minute = MicroSeconds(60 * MICROS_PER_SEC); /// /// assert_eq!(whole_minute.checked_rem(4), Some(MicroSeconds::ZERO)); /// assert_eq!(whole_minute.checked_rem(7), Some(MicroSeconds(4))); /// assert_eq!(whole_minute.checked_rem(0), None); /// ``` #[inline] pub fn checked_rem(self, rhs: u32) -> Option { self.0.checked_rem(rhs as u64).and_then(|i| Some(Self(i))) } /// Multiplies `MicroSeconds` by `f64`. /// /// Converts to an `f64` representing seconds, multiplies by the given factor, then converts /// back to microseconds. /// /// **Panics** if `rhs` is not finite, is negative, or the value overflows. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// let micros = MicroSeconds(2_700_000_000); /// /// assert_eq!(micros.mul_f64(3.14), MicroSeconds(8_478_000_000)); /// assert_eq!(micros.mul_f64(3.14e5), MicroSeconds(847_800_000_000_000)); /// ``` /// /// These should panic. /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.mul_f64(std::f64::INFINITY); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.mul_f64(-0.5); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// MicroSeconds(2 * MICROS_PER_SEC).mul_f64(std::f64::MAX / 10.0); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.mul_f64(std::f64::NAN); /// ``` #[inline] pub fn mul_f64(self, rhs: f64) -> Self { // It is expected that overflow in the initial multiplication would result in `NaN`. // We rely upon the underlying `Duration::from_secs_f64()` to panic appropriately for // unsupported input and result values. Self::from_secs_f64(rhs * self.as_secs_f64()) } /// Multiplies `MicroSeconds` by `f32`. /// /// Converts to an `f32` representing seconds, multiplies by the given factor, then converts /// back to microseconds. /// /// **Panics** if `rhs` is not finite, is negative, or the value overflows. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// let micros = MicroSeconds(2_700_000_000); /// /// // Note the rounding errors that are clear here. /// assert_eq!(micros.mul_f32(3.14), MicroSeconds(8_478_000_000)); /// assert_eq!(micros.mul_f32(3.14e5), MicroSeconds(847_800_000_000_000)); /// ``` /// /// These should panic. /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.mul_f32(std::f32::INFINITY); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.mul_f32(-0.5); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// MicroSeconds(2 * MICROS_PER_SEC).mul_f32(std::f32::MAX / 10.0); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.mul_f32(std::f32::NAN); /// ``` #[inline] pub fn mul_f32(self, rhs: f32) -> Self { // It is expected that overflow in the initial multiplication would result in `NaN`. // We rely upon the underlying `Duration::from_secs_f64()` to panic appropriately for // unsupported input and result values. Self::from_secs_f32(rhs * self.as_secs_f32()) } /// Divides `MicroSeconds` by `f64`. /// /// Converts to an `f64` representing seconds, divides by the given factor, then converts back /// to microseconds. /// /// **Panics** if `rhs` is not finite, is negative, or the value overflows. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// let micros = MicroSeconds(2_700_000_000); /// /// assert_eq!(micros.div_f64(3.14), MicroSeconds(859_872_611)); /// assert_eq!(micros.div_f64(3.14e5), MicroSeconds(8_598)); /// ``` /// /// These should panic. /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.div_f64(-2.0); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// MicroSeconds::MAX.div_f64(0.5); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.div_f64(std::f64::NAN); /// ``` #[inline] pub fn div_f64(self, rhs: f64) -> Self { // Note that division by zero results in a ∞ or −∞ value, which will be handled by the // underlying `Duration::from_secs_f64()` which we rely upon to panic appropriately. Self::from_secs_f64(self.as_secs_f64() / rhs) } /// Divides `MicroSeconds` by `f32`. /// /// Converts to an `f32` representing seconds, divides by the given factor, then converts back /// to microseconds. /// /// **Panics** if `rhs` is not finite, is negative, or the value overflows. /// /// # Examples /// /// ```rust /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// let micros = MicroSeconds(2_700_000_000); /// /// assert_eq!(micros.div_f32(3.14), MicroSeconds(859_872_558)); /// assert_eq!(micros.div_f32(3.14e5), MicroSeconds(8_598)); /// ``` /// /// These should panic. /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.div_f32(-2.0); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::{MicroSeconds, MICROS_PER_SEC}; /// MicroSeconds::MAX.div_f32(0.5); /// ``` /// /// ```rust,should_panic /// # use libpulse_binding::time::MicroSeconds; /// MicroSeconds::SECOND.div_f32(std::f32::NAN); /// ``` #[inline] pub fn div_f32(self, rhs: f32) -> Self { // Note that division by zero results in a ∞ or −∞ value, which will be handled by the // underlying `Duration::from_secs_f64()` which we rely upon to panic appropriately. Self::from_secs_f32(self.as_secs_f32() / rhs) } } impl std::fmt::Display for MicroSeconds { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{} µs", self.0) } } impl Add for MicroSeconds { type Output = Self; #[track_caller] #[inline] fn add(self, other: Self) -> Self { self.checked_add(other).expect(op_err::ADD) } } impl AddAssign for MicroSeconds { #[track_caller] #[inline] fn add_assign(&mut self, rhs: Self) { *self = self.add(rhs); } } impl Sub for MicroSeconds { type Output = Self; #[track_caller] #[inline] fn sub(self, other: Self) -> Self { self.checked_sub(other).expect(op_err::SUB) } } impl SubAssign for MicroSeconds { #[track_caller] #[inline] fn sub_assign(&mut self, rhs: Self) { *self = self.sub(rhs); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // Operations with `Duration` //////////////////////////////////////////////////////////////////////////////////////////////////// impl Add for MicroSeconds { type Output = Self; #[track_caller] #[inline] fn add(self, rhs: Duration) -> Self { self.checked_add_duration(rhs).expect(op_err::ADD) } } impl AddAssign for MicroSeconds { #[track_caller] #[inline] fn add_assign(&mut self, rhs: Duration) { *self = self.add(rhs); } } impl Add for Duration { type Output = Self; #[track_caller] #[inline] fn add(self, rhs: MicroSeconds) -> Self { self.checked_add(Duration::from_micros(rhs.0)).expect(op_err::ADD) } } impl AddAssign for Duration { #[track_caller] #[inline] fn add_assign(&mut self, rhs: MicroSeconds) { *self = self.add(rhs); } } impl Sub for MicroSeconds { type Output = Self; #[track_caller] #[inline] fn sub(self, rhs: Duration) -> Self { self.checked_sub_duration(rhs).expect(op_err::SUB) } } impl SubAssign for MicroSeconds { #[track_caller] #[inline] fn sub_assign(&mut self, rhs: Duration) { *self = self.sub(rhs); } } impl Sub for Duration { type Output = Self; #[track_caller] #[inline] fn sub(self, rhs: MicroSeconds) -> Self { self.checked_sub(Duration::from_micros(rhs.0)).expect(op_err::SUB) } } impl SubAssign for Duration { #[track_caller] #[inline] fn sub_assign(&mut self, rhs: MicroSeconds) { *self = self.sub(rhs); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // Operations with primitives // // NOTE 1: We only implement `u32` here because: // - We do not expect operations will be needed for the larger `u64` range, otherwise we should // switch to that. // - Although implementing for the set of { `u8`, `u16`, `u32`, `u64 } is very easy with a macro, // and may avoid possible need for `as u32` for non-`u32` variables, it introduces ambiguity such // that the compiler does not know which type the `2` should be in the example use case of `2 * // MicroSeconds::SECOND` and so it goes with `i32`, and since we don't implement the ops for // `i32`, the user thus gets an error, forcing them to write instead `2u32 * // MicroSeconds::SECOND`. // // NOTE 2: Addition and subtraction deliberately not implemented, since allowing arbitrary such // operations would allow mistakes to be made that the `MicroSeconds` type exists to prevent. I.e. // allowing the following: // // ```rust // let a = 10u32; // let b = MicroSeconds(1) + a; // ``` // // ...would allow mistakes to be made around the form of `a`. We must force `a` to be wrapped in // `MicroSeconds`. // // NOTE 3: We support an integer being the Lhs of the operation for multiplicaton (for example // `2 * MicroSeconds`), but not for division/remainder, because dividing a generic integer by an // amount of microseconds makes no sense. // //////////////////////////////////////////////////////////////////////////////////////////////////// impl Mul for MicroSeconds { type Output = Self; #[track_caller] #[inline] fn mul(self, rhs: u32) -> Self { Self(self.0.checked_mul(rhs as u64).expect(op_err::MUL)) } } impl MulAssign for MicroSeconds { #[track_caller] #[inline] fn mul_assign(&mut self, rhs: u32) { *self = self.mul(rhs); } } impl Mul for u32 { type Output = MicroSeconds; #[track_caller] #[inline] fn mul(self, rhs: MicroSeconds) -> MicroSeconds { rhs.mul(self) } } impl Div for MicroSeconds { type Output = Self; #[track_caller] #[inline] fn div(self, rhs: u32) -> Self { Self(self.0.checked_div(rhs as u64).expect(op_err::DIV)) } } impl DivAssign for MicroSeconds { #[track_caller] #[inline] fn div_assign(&mut self, rhs: u32) { *self = self.div(rhs); } } impl Rem for MicroSeconds { type Output = Self; #[track_caller] #[inline] fn rem(self, rhs: u32) -> Self { Self(self.0.checked_rem(rhs as u64).expect(op_err::REM)) } } impl RemAssign for MicroSeconds { #[track_caller] #[inline] fn rem_assign(&mut self, rhs: u32) { *self = self.rem(rhs); } } libpulse-binding-2.28.1/src/time/mod.rs000064400000000000000000000043571046102023000160300ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Time handling functionality. mod convert; //private! mod microseconds; mod monotonic; mod timeval; mod unix; use std::time::Duration; pub use self::microseconds::*; pub use self::monotonic::*; pub use self::timeval::*; pub use self::unix::*; // (Copied constants from rust’s std/time/duration.rs) /// Nanoseconds per second. pub const NANOS_PER_SEC: u32 = 1_000_000_000; /// Nanoseconds per millisecond. pub const NANOS_PER_MILLI: u32 = 1_000_000; /// Nanoseconds per microsecond. pub const NANOS_PER_MICRO: u32 = 1_000; /// Microseconds per second. pub const MICROS_PER_SEC: u64 = 1_000_000; /// Microseconds per millisecond. pub const MICROS_PER_MILLI: u64 = 1_000; /// Milliseconds per second. pub const MILLIS_PER_SEC: u64 = 1_000; /// Invalid time. Microseconds value representing ‘invalid’. #[deprecated(since = "2.20.0", note = "use the associated constant on `MicroSeconds` instead")] pub const USEC_INVALID: MicroSeconds = MicroSeconds(capi::PA_USEC_INVALID); /// Largest valid time value in microseconds (largest integer value is reserved for representing /// ‘invalid’). #[deprecated(since = "2.20.0", note = "use the associated constant on `MicroSeconds` instead")] pub const USEC_MAX: MicroSeconds = MicroSeconds(capi::PA_USEC_MAX); /// Basic math operation errors. mod op_err { pub const ADD: &str = "attempt to add with overflow"; pub const SUB: &str = "attempt to subtract with overflow"; pub const MUL: &str = "attempt to multiply with overflow"; pub const DIV: &str = "attempt to divide by zero"; pub const REM: &str = DIV; } libpulse-binding-2.28.1/src/time/monotonic.rs000064400000000000000000000106141046102023000172470ustar 00000000000000// Copyright 2018 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Monotonic timestamps. use std::ops::{Add, AddAssign, Sub, SubAssign}; use std::time::Duration; use super::{MicroSeconds, op_err}; /// A monotonic timestamp. #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] pub struct MonotonicTs(pub(crate) MicroSeconds); impl MonotonicTs { /// Gets the current monotonic system time in microseconds. /// /// Note, if such a clock is not available then this will actually fall back to the wallclock /// time instead. No indication is available for whether or not this is the case, and the /// return value is still a `MonotonicTs` type. #[inline] pub fn now() -> Self { Self(MicroSeconds(unsafe { capi::pa_rtclock_now() })) } /// Returns `true` so long as inner value is not [`MicroSeconds::INVALID`]. #[inline] pub fn is_valid(&self) -> bool { self.0.is_valid() } /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred, /// using the inner [`MicroSeconds`]’s [`checked_add()`](MicroSeconds::checked_add) method. #[inline] pub fn checked_add(self, rhs: MicroSeconds) -> Option { self.0.checked_add(rhs).and_then(|us| Some(Self(us))) } /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred, /// using the inner integer’s `checked_add()` method. #[inline] pub fn checked_add_duration(self, rhs: Duration) -> Option { self.0.checked_add_duration(rhs).and_then(|i| Some(Self(i))) } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred, /// using the inner [`MicroSeconds`]’s [`checked_sub()`](MicroSeconds::checked_sub) method. #[inline] pub fn checked_sub(self, rhs: MicroSeconds) -> Option { self.0.checked_sub(rhs).and_then(|us| Some(Self(us))) } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred, /// using the inner integer’s `checked_sub()` method. #[inline] pub fn checked_sub_duration(self, rhs: Duration) -> Option { self.0.checked_sub_duration(rhs).and_then(|i| Some(Self(i))) } } impl std::fmt::Display for MonotonicTs { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.0) } } impl Add for MonotonicTs { type Output = Self; #[track_caller] #[inline] fn add(self, rhs: MicroSeconds) -> Self { self.checked_add(rhs).expect(op_err::ADD) } } impl AddAssign for MonotonicTs { #[track_caller] #[inline] fn add_assign(&mut self, rhs: MicroSeconds) { *self = self.add(rhs); } } impl Sub for MonotonicTs { type Output = Self; #[track_caller] #[inline] fn sub(self, rhs: MicroSeconds) -> Self { self.checked_sub(rhs).expect(op_err::SUB) } } impl SubAssign for MonotonicTs { #[track_caller] #[inline] fn sub_assign(&mut self, rhs: MicroSeconds) { *self = self.sub(rhs); } } impl Add for MonotonicTs { type Output = Self; #[track_caller] #[inline] fn add(self, rhs: Duration) -> Self { Self(self.0.add(rhs)) } } impl AddAssign for MonotonicTs { #[track_caller] #[inline] fn add_assign(&mut self, rhs: Duration) { *self = self.add(rhs); } } impl Sub for MonotonicTs { type Output = Self; #[track_caller] #[inline] fn sub(self, rhs: Duration) -> Self { Self(self.0.sub(rhs)) } } impl SubAssign for MonotonicTs { #[track_caller] #[inline] fn sub_assign(&mut self, rhs: Duration) { *self = self.sub(rhs); } } libpulse-binding-2.28.1/src/time/timeval.rs000064400000000000000000000263751046102023000167160ustar 00000000000000// Copyright 2018 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Timeval. use std::cmp::Ordering; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign}; use std::time::Duration; use super::{UnixTs, MonotonicTs, MicroSeconds, op_err}; #[cfg(not(windows))] pub(crate) type TvSecs = libc::time_t; #[cfg(not(windows))] pub(crate) type TvUsecs = libc::suseconds_t; #[cfg(windows)] pub(crate) type TvSecs = libc::c_long; #[cfg(windows)] pub(crate) type TvUsecs = libc::c_long; /// Wrapper for [`libc::timeval`], attaching various methods and trait implementations. #[repr(transparent)] #[derive(Copy, Clone)] pub struct Timeval(pub libc::timeval); // Warning, this must remain directly transmutable with the inner libc::timeval impl PartialEq for Timeval { fn eq(&self, other: &Self) -> bool { self.0.tv_sec == other.0.tv_sec && self.0.tv_usec == other.0.tv_usec } } impl Eq for Timeval {} impl Ord for Timeval { fn cmp(&self, other: &Self) -> Ordering { match unsafe { capi::pa_timeval_cmp(&self.0, &other.0) } { 0 => Ordering::Equal, r if r < 0 => Ordering::Less, _ => Ordering::Greater, } } } impl PartialOrd for Timeval { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Timeval { /// Bit to set in `tv_usec` attribute to mark that the `timeval` is in monotonic time. const RTCLOCK_BIT: TvUsecs = 1 << 30; /// Creates a new instance, with values provided. #[inline] pub const fn new(sec: TvSecs, usec: TvUsecs) -> Self { Self(libc::timeval { tv_sec: sec, tv_usec: usec }) } /// Creates a new instance, with value of zero. #[inline] pub const fn new_zero() -> Self { Self::new(0, 0) } /// Calculates the difference between the two specified timeval structs. #[inline] pub fn diff(a: &Self, b: &Self) -> MicroSeconds { MicroSeconds(unsafe { capi::pa_timeval_diff(&a.0, &b.0) }) } /// Gets the time difference between now and self. #[inline] pub fn age(&self) -> MicroSeconds { MicroSeconds(unsafe { capi::pa_timeval_age(&self.0) }) } /// Sets to the specified (monotonic) value. /// /// The `rtclock` boolean is used for indicating support of the rtclock (monotonic time). If /// `true` then the conversion from `MicroSeconds` to `Timeval` is done, and a special ‘rt’ flag /// bit is set in `Timeval`’s inner `tv_usec` attribute. If `false`, then instead the timestamp /// is converted to a Unix wallclock timestamp. /// /// Asserts that `v` is not `MicroSeconds::INVALID`. pub(crate) fn set_rt(&mut self, v: MicroSeconds, rtclock: bool) -> &mut Self { /* This is a copy of PA’s internal `pa_timeval_rtstore()` function */ assert_ne!(v, MicroSeconds::INVALID); *self = v.into(); if rtclock { self.0.tv_usec |= Self::RTCLOCK_BIT; } else { self.wallclock_from_rtclock(); } self } pub(crate) fn wallclock_from_rtclock(&mut self) -> &mut Self { /* This is a copy of PA’s internal `wallclock_from_rtclock()` function */ let wc_now = (UnixTs::now()).0; let rt_now = Timeval::from((MonotonicTs::now()).0); let _ = match rt_now.cmp(self) { Ordering::Less => { wc_now.add(Self::diff(self, &rt_now)) }, _ => { wc_now.sub(Self::diff(&rt_now, self)) }, }; *self = wc_now; self } /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred, /// using the inner integer’s `checked_add()` method. pub fn checked_add(self, other: Self) -> Option { let self_us = MicroSeconds::from(self); let other_us = MicroSeconds::from(other); self_us.checked_add(other_us).and_then(|i| Some(i.into())) } /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred, /// using the inner integer’s `checked_add()` method. pub fn checked_add_us(self, rhs: MicroSeconds) -> Option { let self_us = MicroSeconds::from(self); self_us.checked_add(rhs).and_then(|i| Some(i.into())) } /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred, /// using the inner integer’s `checked_add()` method. pub fn checked_add_duration(self, rhs: Duration) -> Option { let self_us = MicroSeconds::from(self); let rhs_us = MicroSeconds::try_from(rhs).ok()?; self_us.checked_add(rhs_us).and_then(|i| Some(i.into())) } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred, /// using the inner integer’s `checked_sub()` method. pub fn checked_sub(self, other: Self) -> Option { let self_us = MicroSeconds::from(self); let other_us = MicroSeconds::from(other); self_us.checked_sub(other_us).and_then(|i| Some(i.into())) } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred, /// using the inner integer’s `checked_sub()` method. pub fn checked_sub_us(self, rhs: MicroSeconds) -> Option { let self_us = MicroSeconds::from(self); self_us.checked_sub(rhs).and_then(|i| Some(i.into())) } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred, /// using the inner integer’s `checked_sub()` method. pub fn checked_sub_duration(self, rhs: Duration) -> Option { let self_us = MicroSeconds::from(self); let rhs_us = MicroSeconds::try_from(rhs).ok()?; self_us.checked_sub(rhs_us).and_then(|i| Some(i.into())) } /// Checked integer multiplication. Computes `self * rhs`, returning `None` if overflow /// occurred, using the inner integer’s `checked_mul()` method. pub fn checked_mul(self, rhs: u32) -> Option { let self_us = MicroSeconds::from(self); self_us.checked_mul(rhs).and_then(|i| Some(i.into())) } /// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0`, using the /// inner integer’s `checked_div()` method. pub fn checked_div(self, rhs: u32) -> Option { let self_us = MicroSeconds::from(self); self_us.checked_div(rhs).and_then(|i| Some(i.into())) } /// Checked integer remainder. Computes `self % rhs`, returning `None` if `rhs == 0`, using the /// inner integer’s `checked_rem()` method. pub fn checked_rem(self, rhs: u32) -> Option { let self_us = MicroSeconds::from(self); self_us.checked_rem(rhs).and_then(|i| Some(i.into())) } } impl std::fmt::Debug for Timeval { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "timeval {{ tv_sec: {}, tv_usec: {} }}", self.0.tv_sec, self.0.tv_usec) } } impl Add for Timeval { type Output = Self; #[track_caller] #[inline] fn add(self, other: Self) -> Self { self.checked_add(other).expect(op_err::ADD) } } impl AddAssign for Timeval { #[track_caller] #[inline] fn add_assign(&mut self, rhs: Self) { *self = self.add(rhs); } } impl Sub for Timeval { type Output = Self; #[track_caller] #[inline] fn sub(self, other: Self) -> Self { self.checked_sub(other).expect(op_err::SUB) } } impl SubAssign for Timeval { #[track_caller] #[inline] fn sub_assign(&mut self, rhs: Self) { *self = self.sub(rhs); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // Operations with `MicroSeconds` //////////////////////////////////////////////////////////////////////////////////////////////////// impl Add for Timeval { type Output = Self; #[track_caller] #[inline] fn add(self, rhs: MicroSeconds) -> Self { self.checked_add_us(rhs).expect(op_err::ADD) } } impl AddAssign for Timeval { #[track_caller] #[inline] fn add_assign(&mut self, rhs: MicroSeconds) { *self = self.add(rhs); } } impl Sub for Timeval { type Output = Self; #[track_caller] #[inline] fn sub(self, rhs: MicroSeconds) -> Self { self.checked_sub_us(rhs).expect(op_err::SUB) } } impl SubAssign for Timeval { #[track_caller] #[inline] fn sub_assign(&mut self, rhs: MicroSeconds) { *self = self.sub(rhs); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // Operations with `Duration` //////////////////////////////////////////////////////////////////////////////////////////////////// impl Add for Timeval { type Output = Self; #[track_caller] #[inline] fn add(self, rhs: Duration) -> Self { self.checked_add_duration(rhs).expect(op_err::ADD) } } impl AddAssign for Timeval { #[track_caller] #[inline] fn add_assign(&mut self, rhs: Duration) { *self = self.add(rhs); } } impl Sub for Timeval { type Output = Self; #[track_caller] #[inline] fn sub(self, rhs: Duration) -> Self { self.checked_sub_duration(rhs).expect(op_err::SUB) } } impl SubAssign for Timeval { #[track_caller] #[inline] fn sub_assign(&mut self, rhs: Duration) { *self = self.sub(rhs); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // Operations with primitives //////////////////////////////////////////////////////////////////////////////////////////////////// impl Mul for Timeval { type Output = Self; #[track_caller] #[inline] fn mul(self, rhs: u32) -> Self { self.checked_mul(rhs).expect(op_err::MUL) } } impl MulAssign for Timeval { #[track_caller] #[inline] fn mul_assign(&mut self, rhs: u32) { *self = self.mul(rhs); } } impl Div for Timeval { type Output = Self; #[track_caller] #[inline] fn div(self, rhs: u32) -> Self { self.checked_div(rhs).expect(op_err::DIV) } } impl DivAssign for Timeval { #[track_caller] #[inline] fn div_assign(&mut self, rhs: u32) { *self = self.div(rhs); } } impl Rem for Timeval { type Output = Self; #[track_caller] #[inline] fn rem(self, rhs: u32) -> Self { self.checked_rem(rhs).expect(op_err::REM) } } impl RemAssign for Timeval { #[track_caller] #[inline] fn rem_assign(&mut self, rhs: u32) { *self = self.rem(rhs); } } libpulse-binding-2.28.1/src/time/unix.rs000064400000000000000000000060441046102023000162270ustar 00000000000000// Copyright 2018 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Unix timestamps. use std::ops::{Add, AddAssign, Sub, SubAssign}; use super::{Timeval, MicroSeconds, op_err}; /// A Unix timestamp. #[repr(transparent)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct UnixTs(pub(crate) Timeval); impl UnixTs { /// Gets the current ‘time of day’. pub fn now() -> Self { let mut tv = Timeval::new_zero(); unsafe { capi::pa_gettimeofday(&mut tv.0) }; Self(tv) } /// Calculates the difference between the two specified timestamps. #[inline] pub fn diff(a: &Self, b: &Self) -> MicroSeconds { MicroSeconds(unsafe { capi::pa_timeval_diff(&(a.0).0, &(b.0).0) }) } /// Gets the time difference between now and self. #[inline] pub fn age(&self) -> MicroSeconds { MicroSeconds(unsafe { capi::pa_timeval_age(&(self.0).0) }) } /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred, /// using the inner integer’s [`checked_add()`](Timeval::checked_add) method. #[inline] pub fn checked_add(self, rhs: MicroSeconds) -> Option { self.0.checked_add_us(rhs).and_then(|us| Some(Self(us))) } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred, /// using the inner integer’s [`checked_sub()`](Timeval::checked_sub) method. #[inline] pub fn checked_sub(self, rhs: MicroSeconds) -> Option { self.0.checked_sub_us(rhs).and_then(|us| Some(Self(us))) } } impl std::fmt::Display for UnixTs { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{:?}", self.0) } } impl Add for UnixTs { type Output = Self; #[track_caller] #[inline] fn add(self, rhs: MicroSeconds) -> Self { self.checked_add(rhs).expect(op_err::ADD) } } impl AddAssign for UnixTs { #[track_caller] #[inline] fn add_assign(&mut self, rhs: MicroSeconds) { *self = self.add(rhs); } } impl Sub for UnixTs { type Output = Self; #[track_caller] #[inline] fn sub(self, rhs: MicroSeconds) -> Self { self.checked_sub(rhs).expect(op_err::SUB) } } impl SubAssign for UnixTs { #[track_caller] #[inline] fn sub_assign(&mut self, rhs: MicroSeconds) { *self = self.sub(rhs); } } libpulse-binding-2.28.1/src/utf8.rs000064400000000000000000000030211046102023000151640ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! UTF-8 validation functions. //! //! Bindings are not provided here for most of the PulseAudio UTF-8 functions since Rust has built //! in UTF-8 handling and thus they should be entirely unnecessary. use std::os::raw::{c_char, c_void}; use std::ffi::{CStr, CString}; /// Converts a UTF-8 string to the current locale. pub fn utf8_to_locale(s: &str) -> Option { // Warning: New CStrings will be immediately freed if not bound to a variable, leading to // as_ptr() giving dangling pointers! let c_str = CString::new(s.clone()).unwrap(); let tmp_ptr: *const c_char = unsafe { capi::pa_utf8_to_locale(c_str.as_ptr()) }; if tmp_ptr.is_null() { return None; } unsafe { let ret = Some(CStr::from_ptr(tmp_ptr).to_string_lossy().into_owned()); capi::pa_xfree(tmp_ptr as *mut c_void); ret } } libpulse-binding-2.28.1/src/util.rs000064400000000000000000000054201046102023000152600ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Assorted utility functions. use std::ffi::CStr; macro_rules! fn_string_with_buffer { ( $fn_call:ident, $l:ident ) => {{ let mut tmp = Vec::with_capacity($l); unsafe { // Need to check NULL return here because `get_binary_name` function is not // supported on all architectures and so may return NULL, and might as well check // NULL return for other uses anyway. let ptr = capi::$fn_call(tmp.as_mut_ptr(), $l); match ptr.is_null() { true => None, false => Some(CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()), } } }}; } /// Gets the current username. /// /// Returns `None` on failure. pub fn get_user_name(l: usize) -> Option { fn_string_with_buffer!(pa_get_user_name, l) } /// Gets the current hostname. /// /// Returns `None` on failure. pub fn get_host_name(l: usize) -> Option { fn_string_with_buffer!(pa_get_host_name, l) } /// Gets the fully qualified domain name. /// /// Returns `None` on failure. pub fn get_fqdn(l: usize) -> Option { fn_string_with_buffer!(pa_get_fqdn, l) } /// Gets the home directory of the current user. /// /// Returns `None` on failure. pub fn get_home_dir(l: usize) -> Option { fn_string_with_buffer!(pa_get_home_dir, l) } /// Gets the binary file name of the current process. /// /// Returns `None` on failure. /// This is not supported on all architectures (in which case `NULL` is returned). pub fn get_binary_name(l: usize) -> Option { fn_string_with_buffer!(pa_get_binary_name, l) } /// Makes the calling thread realtime if we can. /// /// On Linux, this uses RealTimeKit if available and POSIX APIs otherwise (the latter applies to /// other UNIX variants as well). This is also implemented for macOS and Windows. #[cfg(any(doc, feature = "pa_v13"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))] pub fn make_thread_realtime(rtprio: i32) -> Result<(), ()> { match unsafe { capi::pa_thread_make_realtime(rtprio) } { 0 => Ok(()), _ => Err(()), } } libpulse-binding-2.28.1/src/version.rs000064400000000000000000000243231046102023000157730ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Version related constants and functions. //! //! This module contains functions and constants relating to the version of the PulseAudio (PA) //! client system library. //! //! # Dynamic compatibility //! //! As discussed in the project `COMPATIBILITY.md` file, compatibility is offered for multiple //! versions of the PA client system library, with feature flags adapting the crate to changes made //! in the API of newer PA versions. //! //! Note that the minimum supported version of PA is v5.0. //! //! # Runtime checking //! //! The following functions are provided to retrieve and compare the version of the actual PA client //! system library in use at runtime: //! //! - The [`get_library_version()`] function obtains the version string the system library //! provides. //! - The [`get_library_version_numbers()`] function uses the previous function and attempts to //! parse the version string it returns into numeric form for comparison purposes. //! - The [`compare_with_library_version()`] function uses the previous function and allows //! comparing a provided major and minor version number with what it returned. //! - The [`library_version_is_too_old()`] function uses the previous function to compare against //! the [`TARGET_VERSION`] constant version numbers. This constant varies depending upon PA //! version feature flags, and thus this can be used to check that a program is not being run on //! a system with too old of a version of PA, helping combat the “forward” compatibility problem //! discussed in the project `COMPATIBILITY.md` documentation. //! //! # Dynamic constants //! //! The version constants defined here mostly relate to those provided in the PA C headers, and are //! likely of little use to most projects. They are set dynamically, depending upon the feature //! flags used, or in other words the level of minimum compatibility support selected. Note that PA //! version feature flags are only introduced when new versions of PA introduce changes to its API //! that would require one. The version numbers associated with each PA version feature flag are //! those from the PA version that required introduction of that feature flag. //! //! As an example to clarify, if the “newest” PA version feature flag enabled is `pa_v8` (which //! obviously corresponds to a minimum compatibility level of PA version 8.0), then the //! [`TARGET_VERSION`] constant is set to `(8, 0)`. The “next-newest” feature flag is `pa_v11`, //! which if enabled would bump it up to `(11, 0)`. use capi; use std::borrow::Cow; use std::cmp::Ordering; use std::ffi::CStr; // Re-export from sys pub use capi::version::{Compatibility, get_compatibility}; pub use capi::version::{TARGET_VERSION_STRING, TARGET_VERSION}; pub use capi::version::{PA_API_VERSION as API_VERSION, PA_PROTOCOL_VERSION as PROTOCOL_VERSION}; /// Kinds of errors from trying to parse the runtime PulseAudio system library version string. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[non_exhaustive] enum ErrorKind { /// Error parsing part as integer. ParseIntError, /// Missing version part. MissingPart, /// Too many parts found in the string (unexpected; something is wrong). ExtraParts, } /// Error from trying to parse the runtime PulseAudio system library version string. #[derive(Debug, Clone, Eq, PartialEq)] pub struct Error { /// The problematic version sring which could not be parsed. ver_str: Cow<'static, str>, } impl Error { #[inline] fn new(ver_str: Cow<'static, str>) -> Self { Self { ver_str } } } impl std::error::Error for Error {} impl std::fmt::Display for Error { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { format!("failed to parse PulseAudio system library version string '{}'", &self.ver_str) .fmt(f) } } /// Checks whether the version of the running system library is older than the version corresponding /// to the compatibility level selected via the available feature flags. /// /// Returns `Ok(true)` if the library version is older, `Ok(false)` if equal or newer, or `Err` if a /// problem occurred processing the version string. #[inline] pub fn library_version_is_too_old() -> Result { match compare_with_library_version(TARGET_VERSION.0, TARGET_VERSION.1)? { Ordering::Less | Ordering::Equal => Ok(false), Ordering::Greater => Ok(true), } } /// Compares the supplied version with that of the runtime system library. /// /// Returns the comparison, or `Err` if a problem occurred parsing the library version string. The /// comparison will represent `supplied.cmp(&library)`. #[inline] pub fn compare_with_library_version(major: u8, minor: u8) -> Result { let (lib_major, lib_minor, _) = get_library_version_numbers()?; Ok((major).cmp(&lib_major).then_with(|| minor.cmp(&lib_minor))) } /// Tries to convert the runtime system library version to numeric major, minor and micro form, for /// comparison purposes. /// /// Note, currently micro is always zero. This is the case even in beta/rc versions (like 13.99.1) /// due to the fact that the version string returned by PA always has micro fixed to zero. /// /// Returns `Err` if parsing the version number string fails. #[inline] pub fn get_library_version_numbers() -> Result<(u8, u8, u8), Error> { let ver = get_library_version().to_string_lossy(); pa_version_str_to_num(&ver).or_else(|_e| Err(Error::new(ver))) } /// Convert PulseAudio version string to major, minor and micro numbers. /// /// The version number string should come from `pa_get_library_version()` and thus currently will /// always consist of exactly `$MAJOR.$MINOR.0` per the compiled version.h header. Note that the /// micro number is fixed to zero. #[inline] fn pa_version_str_to_num(ver: &str) -> Result<(u8, u8, u8), ErrorKind> { let mut parts = ver.split('.'); let major: u8 = parts.next().ok_or(ErrorKind::MissingPart)?.parse().or(Err(ErrorKind::ParseIntError))?; let minor: u8 = parts.next().ok_or(ErrorKind::MissingPart)?.parse().or(Err(ErrorKind::ParseIntError))?; // Note, we want to be very strict about accepting only properly formatted values, as anything // otherwise suggests a wierd problem, thus we do parse the micro number even though it will // always be zero. let micro: u8 = parts.next().ok_or(ErrorKind::MissingPart)?.parse().or(Err(ErrorKind::ParseIntError))?; match parts.next().is_some() { true => Err(ErrorKind::ExtraParts), // Something isn’t right false => Ok((major, minor, micro)), } } /// Gets the version string of the (PulseAudio client system) library actually in use at runtime. #[inline] pub fn get_library_version() -> &'static CStr { unsafe { CStr::from_ptr(capi::pa_get_library_version()) } } /// Just compares given version with that defined in `TARGET_VERSION` and returns `true` if the /// `TARGET_VERSION` version is greater. This does **not** involve talking to the PulseAudio client /// library at runtime. This is not very useful! #[deprecated(since = "2.22.0")] #[inline(always)] pub fn check_version(major: u8, minor: u8, micro: u8) -> bool { #[allow(deprecated)] capi::pa_check_version(major, minor, micro) } #[test] fn test_ver_str_to_num() { assert_eq!(pa_version_str_to_num(""), Err(ErrorKind::ParseIntError)); assert_eq!(pa_version_str_to_num(" "), Err(ErrorKind::ParseIntError)); assert_eq!(pa_version_str_to_num("."), Err(ErrorKind::ParseIntError)); assert_eq!(pa_version_str_to_num("a"), Err(ErrorKind::ParseIntError)); assert_eq!(pa_version_str_to_num("a.a"), Err(ErrorKind::ParseIntError)); assert_eq!(pa_version_str_to_num("a.1"), Err(ErrorKind::ParseIntError)); assert_eq!(pa_version_str_to_num("14"), Err(ErrorKind::MissingPart)); assert_eq!(pa_version_str_to_num("14.0"), Err(ErrorKind::MissingPart)); assert_eq!(pa_version_str_to_num("14.0.0"), Ok((14, 0, 0))); assert_eq!(pa_version_str_to_num("14.1.0"), Ok((14, 1, 0))); assert_eq!(pa_version_str_to_num("14.2.0."), Err(ErrorKind::ExtraParts)); assert_eq!(pa_version_str_to_num("14.2.0.0"), Err(ErrorKind::ExtraParts)); assert_eq!(pa_version_str_to_num("12.2a"), Err(ErrorKind::ParseIntError)); assert_eq!(pa_version_str_to_num("12.a"), Err(ErrorKind::ParseIntError)); assert_eq!(pa_version_str_to_num("12.a.1"), Err(ErrorKind::ParseIntError)); } #[test] fn test_getting_pa_version() { let actual_ver_str = unsafe { CStr::from_ptr(capi::pa_get_library_version()).to_string_lossy() }; let (major, minor, micro) = get_library_version_numbers().unwrap(); assert_eq!(format!("{}.{}.{}", major, minor, micro), actual_ver_str); } #[test] fn test_comparing_pa_version() { let (major, minor, _micro) = get_library_version_numbers().unwrap(); assert_eq!(compare_with_library_version(major, minor).unwrap(), Ordering::Equal); assert_eq!(compare_with_library_version(major + 1, minor).unwrap(), Ordering::Greater); assert_eq!(compare_with_library_version(major - 1, minor).unwrap(), Ordering::Less); assert_eq!(compare_with_library_version(major, minor + 1).unwrap(), Ordering::Greater); assert_eq!(compare_with_library_version(major - 1, minor + 1).unwrap(), Ordering::Less); if minor > 0 { assert_eq!(compare_with_library_version(major, minor - 1).unwrap(), Ordering::Less); assert_eq!(compare_with_library_version(major + 1, minor - 1).unwrap(), Ordering::Greater); } } #[test] fn test_lib_ver_not_too_old() { assert_eq!(library_version_is_too_old(), Ok(false)); } libpulse-binding-2.28.1/src/volume.rs000064400000000000000000000764131046102023000156240ustar 00000000000000// Copyright 2017 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Constants and routines for volume handling. //! //! # Overview //! //! Sinks, sources, sink inputs, source outputs and samples can all have their own volumes. To deal //! with these, The PulseAudio library contains a number of functions that ease handling. //! //! The basic volume type in PulseAudio is the [`Volume`] type. Most of the time, applications will //! use the aggregated [`ChannelVolumes`] structure that can store the volume of all channels at //! once. //! //! Volumes commonly span between muted (0%), and normal (100%). It is possible to set volumes to //! higher than 100%, but clipping might occur. //! //! There is no single well-defined meaning attached to the 100% volume for a sink input. In fact, //! it depends on the server configuration. With flat volumes enabled, it means the maximum volume //! that the sound hardware is capable of, which is usually so high that you absolutely must not set //! sink input volume to 100% unless the user explicitly requests that (note that usually you //! shouldn’t set the volume anyway if the user doesn’t explicitly request it, instead, let //! PulseAudio decide the volume for the sink input). With flat volumes disabled the sink input //! volume is relative to the sink volume, so 100% sink input volume means that the sink input is //! played at the current sink volume level. In this case 100% is often a good default volume for a //! sink input, although you still should let PulseAudio decide the default volume. It is possible //! to figure out whether flat volume mode is in effect for a given sink by calling //! [`Introspector::get_sink_info_by_name()`]. //! //! # Calculations //! //! The [`Volume`]s in PulseAudio are cubic in nature and applications should not perform //! calculations with them directly. Instead, they should be converted to and from either dB or a //! linear scale. //! //! The [`VolumeDB`] type represents decibel (dB) converted values, and [`VolumeLinear`], linear. //! The `From` trait has been implemented for your convenience, allowing such conversions. //! //! For simple multiplication, [`Volume::multiply()`] and [`ChannelVolumes::sw_multiply()`] can be //! used. //! //! It’s often unknown what scale hardware volumes relate to. Don’t use the above functions on sink //! and source volumes, unless the sink or source in question has the //! [`SinkFlagSet::DECIBEL_VOLUME`] or [`SourceFlagSet::DECIBEL_VOLUME`] flag set. The conversion //! functions are rarely needed anyway, most of the time it’s sufficient to treat all volumes as //! opaque with a range from [`Volume::MUTED`] \(0%) to [`Volume::NORMAL`] \(100%). //! //! [`Introspector::get_sink_info_by_name()`]: crate::context::introspect::Introspector::get_sink_info_by_name //! [`SinkFlagSet::DECIBEL_VOLUME`]: crate::def::SinkFlagSet::DECIBEL_VOLUME //! [`SourceFlagSet::DECIBEL_VOLUME`]: crate::def::SourceFlagSet::DECIBEL_VOLUME use std::borrow::{Borrow, BorrowMut}; use std::ffi::CStr; use std::ptr::null; use crate::sample; use crate::channelmap::{Map, Position, PositionMask, POSITION_MASK_ALL}; /// A “normal” volume level. #[deprecated(since = "2.20.0", note = "use the associated constant on `Volume` instead")] pub const VOLUME_NORM: Volume = Volume(capi::PA_VOLUME_NORM); /// A muted volume level. #[deprecated(since = "2.20.0", note = "use the associated constant on `Volume` instead")] pub const VOLUME_MUTED: Volume = Volume(capi::PA_VOLUME_MUTED); /// A maximum volume level. #[deprecated(since = "2.20.0", note = "use the associated constant on `Volume` instead")] pub const VOLUME_MAX: Volume = Volume(capi::PA_VOLUME_MAX); /// An invalid volume level. #[deprecated(since = "2.20.0", note = "use the associated constant on `Volume` instead")] pub const VOLUME_INVALID: Volume = Volume(capi::PA_VOLUME_INVALID); /// Minus Infinity. /// /// This floor value is used / can be used, when using converting between integer software volume /// and decibel (dB, floating point) software volume. #[deprecated(since = "2.20.0", note = "use the associated constant on `VolumeDB` instead")] pub const DECIBEL_MINUS_INFINITY: VolumeDB = VolumeDB(capi::PA_DECIBEL_MININFTY); /// Software volume expressed as an integer. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Volume(pub capi::pa_volume_t); impl Default for Volume { fn default() -> Self { Self::NORMAL } } /// Software volume expressed in decibels (dBs). #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub struct VolumeDB(pub f64); impl Default for VolumeDB { fn default() -> Self { VolumeDB(0.0) } } /// Software volume expressed as linear factor. #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub struct VolumeLinear(pub f64); impl Default for VolumeLinear { fn default() -> Self { VolumeLinear(0.0) } } /// A structure encapsulating a per-channel volume #[repr(C)] #[derive(Debug, Copy, Clone, Default)] pub struct ChannelVolumes { /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */ /// Number of channels. channels: u8, /// Per-channel volume. values: [Volume; Self::CHANNELS_MAX as usize], } /// Test size is equal to `sys` equivalent #[test] fn set_compare_capi() { assert_eq!(std::mem::size_of::(), std::mem::size_of::()); assert_eq!(std::mem::align_of::(), std::mem::align_of::()); } impl Borrow<[Volume]> for ChannelVolumes { fn borrow(&self) -> &[Volume] { &self.values[..self.channels as usize] } } impl BorrowMut<[Volume]> for ChannelVolumes { fn borrow_mut(&mut self) -> &mut [Volume] { &mut self.values[..self.channels as usize] } } impl AsRef for ChannelVolumes { #[inline] fn as_ref(&self) -> &capi::pa_cvolume { unsafe { &*(self as *const Self as *const capi::pa_cvolume) } } } impl AsMut for ChannelVolumes { #[inline] fn as_mut(&mut self) -> &mut capi::pa_cvolume { unsafe { &mut *(self as *mut Self as *mut capi::pa_cvolume) } } } impl From for ChannelVolumes { #[inline] fn from(cv: capi::pa_cvolume) -> Self { unsafe { std::mem::transmute(cv) } } } impl PartialEq for ChannelVolumes { #[inline] fn eq(&self, other: &Self) -> bool { unsafe { capi::pa_cvolume_equal(self.as_ref(), other.as_ref()) != 0 } } } impl PartialEq for ChannelVolumes { /// Returns `true` if the volume of all channels are equal to the specified value. #[inline] fn eq(&self, v: &Volume) -> bool { unsafe { capi::pa_cvolume_channels_equal_to(self.as_ref(), v.0) != 0 } } } /// Converts a decibel value to a volume (amplitude, not power). /// /// This is only valid for software volumes! impl From for Volume { #[inline] fn from(v: VolumeDB) -> Self { Volume(unsafe { capi::pa_sw_volume_from_dB(v.0) }) } } /// Converts a volume to a decibel value (amplitude, not power). /// /// This is only valid for software volumes! impl From for VolumeDB { #[inline] fn from(v: Volume) -> Self { VolumeDB(unsafe { capi::pa_sw_volume_to_dB(v.0) }) } } /// Converts a linear factor to a volume. /// /// `0.0` and less is muted while `1.0` is [`Volume::NORMAL`]. /// /// This is only valid for software volumes! /// /// [`Volume::NORMAL`]: struct.Volume.html#associatedconstant.NORMAL impl From for Volume { #[inline] fn from(v: VolumeLinear) -> Self { Volume(unsafe { capi::pa_sw_volume_from_linear(v.0) }) } } /// Converts a volume to a linear factor. /// /// This is only valid for software volumes! impl From for VolumeLinear { #[inline] fn from(v: Volume) -> Self { VolumeLinear(unsafe { capi::pa_sw_volume_to_linear(v.0) }) } } /// Converts a linear factor to a decibel value (amplitude, not power). /// /// `0.0` and less is muted while `1.0` is [`Volume::NORMAL`]. /// /// This is only valid for software volumes! /// /// [`Volume::NORMAL`]: struct.Volume.html#associatedconstant.NORMAL impl From for VolumeDB { #[inline] fn from(v: VolumeLinear) -> Self { VolumeDB::from(Volume::from(v)) } } /// Converts a decibel value (amplitude, not power) to a linear factor. /// /// This is only valid for software volumes! impl From for VolumeLinear { #[inline] fn from(v: VolumeDB) -> Self { VolumeLinear::from(Volume::from(v)) } } impl VolumeLinear { /// Is a muted volume level. #[inline] pub fn is_muted(&self) -> bool { self.0 <= 0.0 } /// Is a “normal” volume level. #[inline] pub fn is_normal(&self) -> bool { self.0 == 1.0 } } impl Volume { /// A “normal” volume level. pub const NORMAL: Self = Self(capi::PA_VOLUME_NORM); /// A muted volume level. pub const MUTED: Self = Self(capi::PA_VOLUME_MUTED); /// A maximum volume level. pub const MAX: Self = Self(capi::PA_VOLUME_MAX); /// An invalid volume level. pub const INVALID: Self = Self(capi::PA_VOLUME_INVALID); /// Is a muted volume level. #[inline] pub fn is_muted(&self) -> bool { *self == Self::MUTED } /// Is a “normal” volume level. #[inline] pub fn is_normal(&self) -> bool { *self == Self::NORMAL } /// Is a maximum volume level. #[inline] pub fn is_max(&self) -> bool { *self == Self::MAX } /// Get the recommended maximum volume to show in user facing UIs. /// /// Note: UIs should deal gracefully with volumes greater than this value and not cause feedback /// loops etc. - i.e. if the volume is more than this, the UI should not limit it and push the /// limited value back to the server. #[inline] pub fn ui_max() -> Self { Volume(capi::pa_volume_ui_max()) } /// Checks if volume is valid. #[inline] pub const fn is_valid(&self) -> bool { capi::pa_volume_is_valid(self.0) } /// Clamps volume to the permitted range. #[inline] pub fn clamp(&mut self) { self.0 = capi::pa_clamp_volume(self.0) } /// Multiplies two software volumes, returning the result. /// /// This uses [`Volume::NORMAL`] as neutral element of multiplication. /// /// This is only valid for software volumes! /// /// [`Volume::NORMAL`]: struct.Volume.html#associatedconstant.NORMAL #[inline] pub fn multiply(a: Self, b: Self) -> Self { Volume(unsafe { capi::pa_sw_volume_multiply(a.0, b.0) }) } /// Divides two software volumes, returning the result. /// /// This uses [`Volume::NORMAL`] as neutral element of division. If a division by zero is tried /// the result will be `0`. /// /// This is only valid for software volumes! /// /// [`Volume::NORMAL`]: struct.Volume.html#associatedconstant.NORMAL #[inline] pub fn divide(a: Self, b: Self) -> Self { Volume(unsafe { capi::pa_sw_volume_divide(a.0, b.0) }) } /// Pretty prints a volume. pub fn print(&self) -> String { const PRINT_MAX: usize = capi::PA_VOLUME_SNPRINT_MAX; let mut tmp = Vec::with_capacity(PRINT_MAX); unsafe { capi::pa_volume_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.0); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } /// Pretty prints a volume but showing dB values. pub fn print_db(&self) -> String { const PRINT_DB_MAX: usize = capi::PA_SW_VOLUME_SNPRINT_DB_MAX; let mut tmp = Vec::with_capacity(PRINT_DB_MAX); unsafe { capi::pa_sw_volume_snprint_dB(tmp.as_mut_ptr(), PRINT_DB_MAX, self.0); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } /// Pretty prints a volume in a verbose way. /// /// The volume is printed in several formats: the raw volume value, percentage, and if /// `print_db` is true, also the dB value. pub fn print_verbose(&self, print_db: bool) -> String { const PRINT_VERBOSE_MAX: usize = capi::PA_VOLUME_SNPRINT_VERBOSE_MAX; let mut tmp = Vec::with_capacity(PRINT_VERBOSE_MAX); unsafe { capi::pa_volume_snprint_verbose(tmp.as_mut_ptr(), PRINT_VERBOSE_MAX, self.0, print_db as i32); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } } impl std::fmt::Display for Volume { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", &self.print()) } } impl VolumeDB { /// Minus Infinity. /// /// This floor value is used / can be used, when using converting between integer software /// volume and decibel (dB, floating point) software volume. pub const MINUS_INFINITY: Self = Self(capi::PA_DECIBEL_MININFTY); } impl ChannelVolumes { /// Maximum number of allowed channels. pub const CHANNELS_MAX: u8 = capi::PA_CHANNELS_MAX; /// Initializes the specified volume and return a pointer to it. /// /// The sample spec will have a defined state but [`is_valid()`](Self::is_valid) will fail for /// it. #[inline] pub fn init(&mut self) -> &Self { unsafe { capi::pa_cvolume_init(self.as_mut()) }; self } /// Checks if the `ChannelVolumes` structure is valid. #[inline] pub fn is_valid(&self) -> bool { unsafe { capi::pa_cvolume_valid(self.as_ref()) != 0 } } /// Gets the number of active channels. #[inline] pub fn len(&self) -> u8 { self.channels } /// Sets the number of active channels. /// /// Volumes for up to [`Self::CHANNELS_MAX`] channels can be held. This sets the portion of /// the internal array considered “active” and thus available for reading/writing (i.e. when /// borrowing `self` as a slice). /// /// **Panics** if the number of channels specified is greater than [`Self::CHANNELS_MAX`]. #[inline] pub fn set_len(&mut self, channels: u8) { assert!(channels <= Self::CHANNELS_MAX); self.channels = channels; } /// Gets an immutable slice of the set of “active” channels. #[inline] pub fn get(&self) -> &[Volume] { self.borrow() } /// Gets a mutable slice of the set of “active” channels. #[inline] pub fn get_mut(&mut self) -> &mut [Volume] { self.borrow_mut() } /// Sets the volume of the specified number of channels to the supplied volume. #[inline] pub fn set(&mut self, channels: u8, v: Volume) -> &Self { unsafe { capi::pa_cvolume_set(self.as_mut(), channels as u32, v.0) }; self } /// Sets the volume of the first n channels to [`Volume::NORMAL`]. #[inline] pub fn reset(&mut self, channels: u8) -> &Self { self.set(channels, Volume::NORMAL) } /// Sets the volume of the first n channels to [`Volume::MUTED`]. #[inline] pub fn mute(&mut self, channels: u8) -> &Self { self.set(channels, Volume::MUTED) } /// Checks if all channels are muted. #[inline] pub fn is_muted(&self) -> bool { self.eq(&Volume::MUTED) } /// Checks if all channels are at normal volume level. #[inline] pub fn is_norm(&self) -> bool { self.eq(&Volume::NORMAL) } /// Gets the average volume of all channels. #[inline] pub fn avg(&self) -> Volume { Volume(unsafe { capi::pa_cvolume_avg(self.as_ref()) }) } /// Returns the average volume of all channels that are included in the specified channel map /// with the specified channel position mask. /// /// If no channel is selected the returned value will be [`Volume::MUTED`]. If `mask` is `None`, /// has the same effect as passing [`POSITION_MASK_ALL`]. #[inline] pub fn avg_mask(&self, cm: &Map, mask: Option) -> Volume { let mask_actual = mask.unwrap_or(POSITION_MASK_ALL); Volume(unsafe { capi::pa_cvolume_avg_mask(self.as_ref(), cm.as_ref(), mask_actual) }) } /// Gets the maximum volume of all channels. #[inline] pub fn max(&self) -> Volume { Volume(unsafe { capi::pa_cvolume_max(self.as_ref()) }) } /// Gets the maximum volume of all channels that are included in the specified channel map /// with the specified channel position mask. /// /// If no channel is selected the returned value will be [`Volume::MUTED`]. If `mask` is `None`, /// has the same effect as passing [`POSITION_MASK_ALL`]. #[inline] pub fn max_mask(&self, cm: &Map, mask: Option) -> Volume { let mask_actual = mask.unwrap_or(POSITION_MASK_ALL); Volume(unsafe { capi::pa_cvolume_max_mask(self.as_ref(), cm.as_ref(), mask_actual) }) } /// Gets the minimum volume of all channels. #[inline] pub fn min(&self) -> Volume { Volume(unsafe { capi::pa_cvolume_min(self.as_ref()) }) } /// Gets the minimum volume of all channels that are included in the specified channel map /// with the specified channel position mask. /// /// If no channel is selected the returned value will be [`Volume::MUTED`]. If `mask` is `None`, /// has the same effect as passing [`POSITION_MASK_ALL`]. #[inline] pub fn min_mask(&self, cm: &Map, mask: Option) -> Volume { let mask_actual = mask.unwrap_or(POSITION_MASK_ALL); Volume(unsafe { capi::pa_cvolume_min_mask(self.as_ref(), cm.as_ref(), mask_actual) }) } /// Multiplies two per-channel volumes. /// /// If `with` is `None`, multiplies with itself. This is only valid for software volumes! /// Returns pointer to self. #[inline] pub fn sw_multiply(&mut self, with: Option<&Self>) -> &mut Self { unsafe { capi::pa_sw_cvolume_multiply(self.as_mut(), self.as_mut(), with.unwrap_or(self).as_ref()) }; self } /// Multiplies a per-channel volume with a scalar volume. /// /// This is only valid for software volumes! Returns pointer to self. #[inline] pub fn sw_multiply_scalar(&mut self, with: Volume) -> &mut Self { unsafe { capi::pa_sw_cvolume_multiply_scalar(self.as_mut(), self.as_ref(), with.0) }; self } /// Divides two per-channel volumes. /// /// If `with` is `None`, divides with itself. This is only valid for software volumes! Returns /// pointer to self. #[inline] pub fn sw_divide(&mut self, with: Option<&Self>) -> &mut Self { unsafe { capi::pa_sw_cvolume_divide(self.as_mut(), self.as_mut(), with.unwrap_or(self).as_ref()) }; self } /// Divides a per-channel volume by a scalar volume. /// /// This is only valid for software volumes! Returns pointer to self. #[inline] pub fn sw_divide_scalar(&mut self, with: Volume) -> &mut Self { unsafe { capi::pa_sw_cvolume_divide_scalar(self.as_mut(), self.as_ref(), with.0) }; self } /// Remaps a volume from one channel mapping to a different channel mapping. /// /// Returns pointer to self. #[inline] pub fn remap(&mut self, from: &Map, to: &Map) -> &mut Self { unsafe { capi::pa_cvolume_remap(self.as_mut(), from.as_ref(), to.as_ref()) }; self } /// Checks if the specified volume is compatible with the specified sample spec. #[inline] pub fn is_compatible_with_ss(&self, ss: &sample::Spec) -> bool { unsafe { capi::pa_cvolume_compatible(self.as_ref(), ss.as_ref()) != 0 } } /// Checks if the specified volume is compatible with the specified channel map. #[inline] pub fn is_compatible_with_cm(&self, cm: &Map) -> bool { unsafe { capi::pa_cvolume_compatible_with_channel_map(self.as_ref(), cm.as_ref()) != 0 } } /// Calculates a ‘balance’ value for the specified volume with the specified channel map. /// /// The return value will range from `-1.0` (left) to `+1.0` (right). If no balance value is /// applicable to this channel map the return value will always be `0.0`. See /// [`Map::can_balance()`]. #[inline] pub fn get_balance(&self, map: &Map) -> f32 { unsafe { capi::pa_cvolume_get_balance(self.as_ref(), map.as_ref()) } } /// Adjusts the ‘balance’ value for the specified volume with the specified channel map. /// /// The balance is a value between `-1.0` and `+1.0`. This operation might not be reversible! /// Also, after this call [`get_balance()`](Self::get_balance) is not guaranteed to actually /// return the requested balance value (e.g. when the input volume was zero anyway for all /// channels). If no balance value is applicable to this channel map the volume will not be /// modified. See [`Map::can_balance()`]. /// /// Returns pointer to self, or `None` on error. #[inline] pub fn set_balance(&mut self, map: &Map, new_balance: f32) -> Option<&mut Self> { let ptr = unsafe { capi::pa_cvolume_set_balance(self.as_mut(), map.as_ref(), new_balance) }; match ptr.is_null() { false => Some(self), true => None, } } /// Calculates a ‘fade’ value (i.e. ‘balance’ between front and rear) for the specified volume /// with the specified channel map. /// /// The return value will range from -1.0f (rear) to +1.0f (left). If no fade value is /// applicable to this channel map the return value will always be `0.0`. See /// [`Map::can_fade()`]. #[inline] pub fn get_fade(&self, map: &Map) -> f32 { unsafe { capi::pa_cvolume_get_fade(self.as_ref(), map.as_ref()) } } /// Adjusts the ‘fade’ value (i.e. ‘balance’ between front and rear) for the specified volume /// with the specified channel map. /// /// The balance is a value between `-1.0` and `+1.0`. This operation might not be reversible! /// Also, after this call [`get_fade()`](Self::get_fade) is not guaranteed to actually return /// the requested fade value (e.g. when the input volume was zero anyway for all channels). If /// no fade value is applicable to this channel map the volume will not be modified. See /// [`Map::can_fade()`]. /// /// Returns pointer to self, or `None` on error. #[inline] pub fn set_fade(&mut self, map: &Map, new_fade: f32) -> Option<&mut Self> { let ptr = unsafe { capi::pa_cvolume_set_fade(self.as_mut(), map.as_ref(), new_fade) }; match ptr.is_null() { false => Some(self), true => None, } } /// Calculates a ‘lfe balance’ value for the specified volume with the specified channel map. /// /// The return value will range from `-1.0` (no lfe) to `+1.0` (only lfe), where `0.0` is /// balanced. If no value is applicable to this channel map the return value will always be /// `0.0`. See [`Map::can_lfe_balance()`]. #[inline] #[cfg(any(doc, feature = "pa_v8"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v8")))] pub fn get_lfe_balance(&self, map: &Map) -> f32 { unsafe { capi::pa_cvolume_get_lfe_balance(self.as_ref(), map.as_ref()) } } /// Adjusts the ‘LFE balance’ value for the specified volume with the specified channel map. /// /// The balance is a value between `-1.0` (no lfe) and `+1.0` (only lfe). This operation might /// not be reversible! Also, after this call [`get_lfe_balance()`] is not guaranteed to actually /// return the requested value (e.g. when the input volume was zero anyway for all channels). If /// no lfe balance value is applicable to this channel map the volume will not be modified. See /// [`Map::can_lfe_balance()`]. /// /// Returns pointer to self, or `None` on error. /// /// [`get_lfe_balance()`]: Self::get_lfe_balance #[inline] #[cfg(any(doc, feature = "pa_v8"))] #[cfg_attr(docsrs, doc(cfg(feature = "pa_v8")))] pub fn set_lfe_balance(&mut self, map: &Map, new_balance: f32) -> Option<&mut Self> { let ptr = unsafe { capi::pa_cvolume_set_lfe_balance(self.as_mut(), map.as_ref(), new_balance) }; match ptr.is_null() { false => Some(self), true => None, } } /// Scales so that the maximum volume of all channels equals `max`. /// /// The proportions between the channel volumes are kept. /// /// Returns pointer to self, or `None` on error. #[inline] pub fn scale(&mut self, max: Volume) -> Option<&mut Self> { let ptr = unsafe { capi::pa_cvolume_scale(self.as_mut(), max.0) }; match ptr.is_null() { false => Some(self), true => None, } } /// Scales so that the maximum volume of all channels selected via `cm`/`mask` equals `max`. /// /// This also modifies the volume of those channels that are unmasked. The proportions between /// the channel volumes are kept. /// /// If `mask` is `None`, has the same effect as passing [`POSITION_MASK_ALL`]. /// /// Returns pointer to self, or `None` on error. #[inline] pub fn scale_mask(&mut self, max: Volume, cm: &mut Map, mask: Option) -> Option<&mut Self> { let mask_actual = mask.unwrap_or(POSITION_MASK_ALL); let ptr = unsafe { capi::pa_cvolume_scale_mask(self.as_mut(), max.0, cm.as_ref(), mask_actual) }; match ptr.is_null() { false => Some(self), true => None, } } /// Sets the passed volume to all channels at the specified channel position. /// /// Returns `None` if either invalid data was provided, or if there is no channel at the /// position specified. You can check if a channel map includes a specific position by calling /// [`Map::has_position()`]. On success, returns pointer to self. #[inline] pub fn set_position(&mut self, map: &Map, p: Position, v: Volume) -> Option<&mut Self> { // Note: C function returns NULL on invalid data or no channel at position specified (no // change needed). We could ignore failure and always return self ptr, but it does not seem // ideal to leave callers unaware should they be passing in invalid data. let ptr = unsafe { capi::pa_cvolume_set_position(self.as_mut(), map.as_ref(), p.into(), v.0) }; match ptr.is_null() { false => Some(self), true => None, } } /// Gets the maximum volume of all channels at the specified channel position. /// /// Will return `0` if there is no channel at the position specified. You can check if a channel /// map includes a specific position by calling [`Map::has_position()`]. #[inline] pub fn get_position(&self, map: &Map, p: Position) -> Volume { Volume(unsafe { capi::pa_cvolume_get_position(self.as_ref(), map.as_ref(), p.into()) }) } /// Merges one set of channel volumes with another. /// /// The channel count is set to the minimum between that of self and that of `with`. Only this /// number of channels are processed. For each channel processed, volume is set to the greatest /// of the values from self and from `with`. I.e if one set has three channels and the other has /// two, the number of channels will be set to two, and only the first two channels will be /// compared, with the greatest values of these two channels being stored. The third channel in /// the one set will be ignored. /// /// Returns pointer to self, or `None` on error. #[inline] pub fn merge(&mut self, with: &Self) -> Option<&mut Self> { let ptr = unsafe { capi::pa_cvolume_merge(self.as_mut(), self.as_ref(), with.as_ref()) }; match ptr.is_null() { false => Some(self), true => None, } } /// Increases the volume passed in by `inc`, but not exceeding `limit`. /// /// The proportions between the channels are kept. /// /// Returns pointer to self, or `None` on error. #[inline] pub fn inc_clamp(&mut self, inc: Volume, limit: Volume) -> Option<&mut Self> { let ptr = unsafe { capi::pa_cvolume_inc_clamp(self.as_mut(), inc.0, limit.0) }; match ptr.is_null() { false => Some(self), true => None, } } /// Increases the volume passed in by `inc`. /// /// The proportions between the channels are kept. /// /// Returns pointer to self, or `None` on error. #[inline] pub fn increase(&mut self, inc: Volume) -> Option<&mut Self> { let ptr = unsafe { capi::pa_cvolume_inc(self.as_mut(), inc.0) }; match ptr.is_null() { false => Some(self), true => None, } } /// Decreases the volume passed in by `dec`. /// /// The proportions between the channels are kept. /// /// Returns pointer to self, or `None` on error. #[inline] pub fn decrease(&mut self, dec: Volume) -> Option<&mut Self> { let ptr = unsafe { capi::pa_cvolume_dec(self.as_mut(), dec.0) }; match ptr.is_null() { false => Some(self), true => None, } } /// Pretty prints a volume structure. pub fn print(&self) -> String { const PRINT_MAX: usize = capi::PA_CVOLUME_SNPRINT_MAX; let mut tmp = Vec::with_capacity(PRINT_MAX); unsafe { capi::pa_cvolume_snprint(tmp.as_mut_ptr(), PRINT_MAX, self.as_ref()); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } /// Pretty prints a volume structure but show dB values. pub fn print_db(&self) -> String { const PRINT_DB_MAX: usize = capi::PA_SW_CVOLUME_SNPRINT_DB_MAX; let mut tmp = Vec::with_capacity(PRINT_DB_MAX); unsafe { capi::pa_sw_cvolume_snprint_dB(tmp.as_mut_ptr(), PRINT_DB_MAX, self.as_ref()); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } /// Pretty prints a volume structure in a verbose way. /// /// The volume for each channel is printed in several formats: the raw volume value, /// percentage, and if `print_db` is non-zero, also the dB value. If `map` is provided, the /// channel names will be printed. pub fn print_verbose(&self, map: Option<&Map>, print_db: bool) -> String { const PRINT_VERBOSE_MAX: usize = capi::PA_CVOLUME_SNPRINT_VERBOSE_MAX; let p_map = map.map_or(null::(), |m| m.as_ref()); let mut tmp = Vec::with_capacity(PRINT_VERBOSE_MAX); unsafe { capi::pa_cvolume_snprint_verbose(tmp.as_mut_ptr(), PRINT_VERBOSE_MAX, self.as_ref(), p_map, print_db as i32); CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned() } } } impl std::fmt::Display for ChannelVolumes { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", &self.print()) } } libpulse-binding-2.28.1/tests/time_micros.rs000064400000000000000000000076131046102023000171760ustar 00000000000000// Copyright 2020 Lyndon Brown // // This file is part of the PulseAudio Rust language binding. // // Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not // copy, modify, or distribute this file except in compliance with said license. You can find copies // of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at // and // respectively. // // Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a // fair-use basis, as discussed in the overall project readme (available in the git repository). //! Testing `MicroSeconds` operations //! //! (Covering stuff not already done in doc tests). extern crate libpulse_binding as pulse; use std::time::Duration; use pulse::time::MicroSeconds; // Check basic addition / subtraction implementations #[test] fn math() { let mut a = MicroSeconds(30); let b = MicroSeconds(10); assert_eq!(a + b, MicroSeconds(40)); assert_eq!(a - b, MicroSeconds(20)); a += b; assert_eq!(a, MicroSeconds(40)); a -= b; assert_eq!(a, MicroSeconds(30)); } // Test that basic addition overflow panics #[test] #[should_panic] fn add_overflow() { let _ = MicroSeconds::MAX + MicroSeconds(10); } // Test that basic subtraction overflow panics #[test] #[should_panic] fn sub_overflow() { let _ = MicroSeconds(10) - MicroSeconds(20); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Operations with `Duration` //////////////////////////////////////////////////////////////////////////////////////////////////// #[test] fn duration_math() { assert_eq!(MicroSeconds(300_000) + Duration::new(2, 0), MicroSeconds(2_300_000)); assert_eq!(MicroSeconds(3_500_000) - Duration::new(2, 0), MicroSeconds(1_500_000)); let mut x = MicroSeconds(300_000); x += Duration::new(2, 0); assert_eq!(x, MicroSeconds(2_300_000)); x -= Duration::new(2, 0); assert_eq!(x, MicroSeconds(300_000)); assert_eq!(Duration::new(2, 0) + MicroSeconds(300_000), Duration::new(2, 300_000_000)); assert_eq!(Duration::new(2, 0) - MicroSeconds(1_500_000), Duration::new(0, 500_000_000)); let mut x = Duration::new(2, 0); x += MicroSeconds(300_000); assert_eq!(x, Duration::new(2, 300_000_000)); x -= MicroSeconds(1_500_000); assert_eq!(x, Duration::new(0, 800_000_000)); } // Test that basic addition overflow panics #[test] #[should_panic] fn duration_add_overflow_to_micros() { let _ = MicroSeconds::MAX + Duration::new(2, 0); } // Test that basic addition overflow panics #[test] #[should_panic] fn duration_add_overflow_to_duration() { let _ = Duration::new(std::u64::MAX, 0) + MicroSeconds::MAX; } // Test that basic subtraction overflow panics #[test] #[should_panic] fn duration_sub_overflow_to_micros() { let _ = MicroSeconds(10) - Duration::new(1, 0); } // Test that basic subtraction overflow panics #[test] #[should_panic] fn duration_sub_overflow_to_duration() { let _ = Duration::new(1, 0) - MicroSeconds(2_000_000); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Operations with primitives //////////////////////////////////////////////////////////////////////////////////////////////////// #[test] fn primitives() { assert_eq!(MicroSeconds::SECOND * 2, MicroSeconds(2_000_000)); assert_eq!(2 * MicroSeconds::SECOND, MicroSeconds(2_000_000)); let mut x = MicroSeconds::SECOND; x *= 2; assert_eq!(x, MicroSeconds(2_000_000)); assert_eq!(MicroSeconds::SECOND / 2, MicroSeconds(500_000)); let mut x = MicroSeconds::SECOND; x /= 2; assert_eq!(x, MicroSeconds(500_000)); assert_eq!(MicroSeconds(200_000) % 7, MicroSeconds(3)); let mut x = MicroSeconds(200_000); x %= 7; assert_eq!(x, MicroSeconds(3)); }