Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
44
third-party/vendor/objc2/examples/basic_usage.rs
vendored
Normal file
44
third-party/vendor/objc2/examples/basic_usage.rs
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use objc2::foundation::{NSArray, NSDictionary, NSObject};
|
||||
use objc2::ns_string;
|
||||
use objc2::rc::autoreleasepool;
|
||||
|
||||
fn main() {
|
||||
// Create and compare NSObjects
|
||||
let obj = NSObject::new();
|
||||
#[allow(clippy::eq_op)]
|
||||
{
|
||||
println!("{:?} == {:?}? {:?}", obj, obj, obj == obj);
|
||||
}
|
||||
|
||||
let obj2 = NSObject::new();
|
||||
println!("{:?} == {:?}? {:?}", obj, obj2, obj == obj2);
|
||||
|
||||
// Create an NSArray from a Vec
|
||||
let objs = vec![obj, obj2];
|
||||
let array = NSArray::from_vec(objs);
|
||||
for obj in array.iter() {
|
||||
println!("{:?}", obj);
|
||||
}
|
||||
println!("{}", array.len());
|
||||
|
||||
// Turn the NSArray back into a Vec
|
||||
let mut objs = NSArray::into_vec(array);
|
||||
let obj = objs.pop().unwrap();
|
||||
|
||||
// Create a static NSString
|
||||
let string = ns_string!("Hello, world!");
|
||||
// Use an autoreleasepool to get the `str` contents of the NSString
|
||||
autoreleasepool(|pool| {
|
||||
println!("{}", string.as_str(pool));
|
||||
});
|
||||
// Or simply use the `Display` implementation
|
||||
let _s = string.to_string(); // Using ToString
|
||||
println!("{}", string); // Or Display directly
|
||||
|
||||
// Create a dictionary mapping strings to objects
|
||||
let keys = &[string];
|
||||
let vals = vec![obj];
|
||||
let dict = NSDictionary::from_keys_and_objects(keys, vals);
|
||||
println!("{:?}", dict.get(string));
|
||||
println!("{}", dict.len());
|
||||
}
|
||||
142
third-party/vendor/objc2/examples/class_with_lifetime.rs
vendored
Normal file
142
third-party/vendor/objc2/examples/class_with_lifetime.rs
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
//! A custom Objective-C class with a lifetime parameter.
|
||||
//!
|
||||
//! Note that we can't use the `declare_class!` macro for this, it doesn't
|
||||
//! support such use-cases. Instead, we'll declare the class manually!
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Once;
|
||||
|
||||
use objc2::declare::{ClassBuilder, Ivar, IvarType};
|
||||
use objc2::foundation::NSObject;
|
||||
use objc2::rc::{Id, Owned};
|
||||
use objc2::runtime::{Class, Object, Sel};
|
||||
use objc2::{msg_send, msg_send_id, sel};
|
||||
use objc2::{ClassType, Encoding, Message, RefEncode};
|
||||
|
||||
/// Helper type for the instance variable
|
||||
struct NumberIvar<'a> {
|
||||
// Doesn't actually matter what we put here, but we have to use the
|
||||
// lifetime parameter somehow
|
||||
p: PhantomData<&'a mut u8>,
|
||||
}
|
||||
|
||||
unsafe impl<'a> IvarType for NumberIvar<'a> {
|
||||
type Type = &'a mut u8;
|
||||
const NAME: &'static str = "_number_ptr";
|
||||
}
|
||||
|
||||
/// Struct that represents our custom object.
|
||||
#[repr(C)]
|
||||
pub struct MyObject<'a> {
|
||||
// Required to give MyObject the proper layout
|
||||
superclass: NSObject,
|
||||
// SAFETY: The ivar is declared below, and is properly initialized in the
|
||||
// designated initializer.
|
||||
//
|
||||
// Note! Attempting to acess the ivar before it has been initialized is
|
||||
// undefined behaviour!
|
||||
number: Ivar<NumberIvar<'a>>,
|
||||
}
|
||||
|
||||
unsafe impl RefEncode for MyObject<'_> {
|
||||
const ENCODING_REF: Encoding = Object::ENCODING_REF;
|
||||
}
|
||||
|
||||
unsafe impl Message for MyObject<'_> {}
|
||||
|
||||
impl<'a> MyObject<'a> {
|
||||
unsafe extern "C" fn init_with_ptr(
|
||||
&mut self,
|
||||
_cmd: Sel,
|
||||
ptr: Option<&'a mut u8>,
|
||||
) -> Option<&'a mut Self> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
|
||||
this.map(|this| {
|
||||
// Properly initialize the number reference
|
||||
Ivar::write(&mut this.number, ptr.expect("got NULL number ptr"));
|
||||
this
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new(number: &'a mut u8) -> Id<Self, Owned> {
|
||||
// SAFETY: The lifetime of the reference is properly bound to the
|
||||
// returned type
|
||||
unsafe {
|
||||
let obj = msg_send_id![Self::class(), alloc];
|
||||
msg_send_id![obj, initWithPtr: number]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> &u8 {
|
||||
&self.number
|
||||
}
|
||||
|
||||
pub fn set(&mut self, number: u8) {
|
||||
**self.number = number;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> ClassType for MyObject<'a> {
|
||||
type Super = NSObject;
|
||||
const NAME: &'static str = "MyObject";
|
||||
|
||||
fn class() -> &'static Class {
|
||||
// TODO: Use std::lazy::LazyCell
|
||||
static REGISTER_CLASS: Once = Once::new();
|
||||
|
||||
REGISTER_CLASS.call_once(|| {
|
||||
let superclass = NSObject::class();
|
||||
let mut builder = ClassBuilder::new(Self::NAME, superclass).unwrap();
|
||||
|
||||
builder.add_static_ivar::<NumberIvar<'a>>();
|
||||
|
||||
unsafe {
|
||||
builder.add_method(
|
||||
sel!(initWithPtr:),
|
||||
Self::init_with_ptr as unsafe extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
}
|
||||
|
||||
let _cls = builder.register();
|
||||
});
|
||||
|
||||
Class::get("MyObject").unwrap()
|
||||
}
|
||||
|
||||
fn as_super(&self) -> &Self::Super {
|
||||
&self.superclass
|
||||
}
|
||||
|
||||
fn as_super_mut(&mut self) -> &mut Self::Super {
|
||||
&mut self.superclass
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut number = 54;
|
||||
let mut obj = MyObject::new(&mut number);
|
||||
|
||||
// It is not possible to convert to `Id<NSObject, Owned>` since that would
|
||||
// loose the lifetime information that `MyObject` stores
|
||||
// let obj = Id::into_super(obj);
|
||||
|
||||
println!("Number: {}", obj.get());
|
||||
|
||||
obj.set(7);
|
||||
// Won't compile, since `obj` holds a mutable reference to number
|
||||
// println!("Number: {}", number);
|
||||
println!("Number: {}", obj.get());
|
||||
|
||||
let obj = Id::into_shared(obj);
|
||||
let obj2 = obj.clone();
|
||||
|
||||
// We gave up ownership above, so can't edit the number any more!
|
||||
// obj.set(7);
|
||||
|
||||
println!("Number: {}", obj.get());
|
||||
println!("Number: {}", obj2.get());
|
||||
|
||||
drop(obj);
|
||||
drop(obj2);
|
||||
println!("Number: {}", number);
|
||||
}
|
||||
118
third-party/vendor/objc2/examples/delegate.rs
vendored
Normal file
118
third-party/vendor/objc2/examples/delegate.rs
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#![cfg_attr(not(all(feature = "apple", target_os = "macos")), allow(unused))]
|
||||
use objc2::declare::{Ivar, IvarDrop};
|
||||
use objc2::foundation::{NSCopying, NSObject, NSString};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::runtime::Object;
|
||||
use objc2::{declare_class, extern_class, msg_send, msg_send_id, ns_string, ClassType};
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
extern "C" {}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
extern_class!(
|
||||
#[derive(Debug)]
|
||||
struct NSResponder;
|
||||
|
||||
unsafe impl ClassType for NSResponder {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
struct CustomAppDelegate {
|
||||
pub ivar: u8,
|
||||
another_ivar: bool,
|
||||
box_ivar: IvarDrop<Box<i32>>,
|
||||
maybe_box_ivar: IvarDrop<Option<Box<i32>>>,
|
||||
id_ivar: IvarDrop<Id<NSString, Shared>>,
|
||||
maybe_id_ivar: IvarDrop<Option<Id<NSString, Shared>>>,
|
||||
}
|
||||
|
||||
unsafe impl ClassType for CustomAppDelegate {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
const NAME: &'static str = "MyCustomAppDelegate";
|
||||
}
|
||||
|
||||
unsafe impl CustomAppDelegate {
|
||||
#[sel(initWith:another:)]
|
||||
fn init_with(self: &mut Self, ivar: u8, another_ivar: bool) -> Option<&mut Self> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
|
||||
|
||||
// TODO: `ns_string` can't be used inside closures; investigate!
|
||||
let s = ns_string!("def");
|
||||
|
||||
this.map(|this| {
|
||||
Ivar::write(&mut this.ivar, ivar);
|
||||
*this.another_ivar = another_ivar;
|
||||
*this.maybe_box_ivar = None;
|
||||
*this.maybe_id_ivar = Some(s.copy());
|
||||
Ivar::write(&mut this.box_ivar, Box::new(2));
|
||||
Ivar::write(&mut this.id_ivar, NSString::from_str("abc"));
|
||||
this
|
||||
})
|
||||
}
|
||||
|
||||
#[sel(myClassMethod)]
|
||||
fn my_class_method() {
|
||||
println!("A class method!");
|
||||
}
|
||||
}
|
||||
|
||||
// For some reason, `NSApplicationDelegate` is not a "real" protocol we
|
||||
// can retrieve using `objc_getProtocol` - it seems it is created by
|
||||
// `clang` only when used in Objective-C...
|
||||
//
|
||||
// TODO: Investigate this!
|
||||
unsafe impl CustomAppDelegate {
|
||||
/// This is `unsafe` because it expects `sender` to be valid
|
||||
#[sel(applicationDidFinishLaunching:)]
|
||||
unsafe fn did_finish_launching(&self, sender: *mut Object) {
|
||||
println!("Did finish launching!");
|
||||
// Do something with `sender`
|
||||
dbg!(sender);
|
||||
}
|
||||
|
||||
/// Some comment before `sel`.
|
||||
#[sel(applicationWillTerminate:)]
|
||||
/// Some comment after `sel`.
|
||||
fn will_terminate(&self, _: *mut Object) {
|
||||
println!("Will terminate!");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
impl CustomAppDelegate {
|
||||
pub fn new(ivar: u8, another_ivar: bool) -> Id<Self, Shared> {
|
||||
let cls = Self::class();
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
msg_send_id![cls, alloc],
|
||||
initWith: ivar,
|
||||
another: another_ivar,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
fn main() {
|
||||
let delegate = CustomAppDelegate::new(42, true);
|
||||
|
||||
println!("{:?}", delegate);
|
||||
println!("{:?}", delegate.ivar);
|
||||
println!("{:?}", delegate.another_ivar);
|
||||
println!("{:?}", delegate.box_ivar);
|
||||
println!("{:?}", delegate.maybe_box_ivar);
|
||||
println!("{:?}", delegate.id_ivar);
|
||||
println!("{:?}", delegate.maybe_id_ivar);
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "apple", target_os = "macos")))]
|
||||
fn main() {
|
||||
panic!("This example uses AppKit, which is only present on macOS");
|
||||
}
|
||||
58
third-party/vendor/objc2/examples/introspection.rs
vendored
Normal file
58
third-party/vendor/objc2/examples/introspection.rs
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use objc2::foundation::NSObject;
|
||||
use objc2::runtime::Class;
|
||||
use objc2::{sel, ClassType, Encode};
|
||||
|
||||
fn main() {
|
||||
// Get the class representing `NSObject`
|
||||
let cls = NSObject::class();
|
||||
|
||||
// Inspect various properties of the class
|
||||
println!("NSObject superclass: {:?}", cls.superclass());
|
||||
println!("NSObject size: {}", cls.instance_size());
|
||||
println!(
|
||||
"-[NSObject alloc] would work: {}",
|
||||
cls.responds_to(sel!(alloc))
|
||||
);
|
||||
println!(
|
||||
"+[NSObject alloc] would work: {}",
|
||||
cls.metaclass().responds_to(sel!(alloc))
|
||||
);
|
||||
|
||||
// Inspect an instance variable on the class
|
||||
//
|
||||
// Note: You should not rely on the `isa` ivar being available,
|
||||
// this is only for demonstration.
|
||||
let ivar = cls
|
||||
.instance_variable("isa")
|
||||
.expect("No ivar with name 'isa' found on NSObject");
|
||||
println!(
|
||||
"Instance variable {} has type encoding {:?}",
|
||||
ivar.name(),
|
||||
ivar.type_encoding()
|
||||
);
|
||||
assert!(<*const Class>::ENCODING.equivalent_to_str(ivar.type_encoding()));
|
||||
|
||||
// Inspect a method of the class
|
||||
let method = cls.instance_method(sel!(hash)).unwrap();
|
||||
println!(
|
||||
"-[NSObject hash] takes {} parameters",
|
||||
method.arguments_count()
|
||||
);
|
||||
#[cfg(feature = "malloc")]
|
||||
{
|
||||
let hash_return = method.return_type();
|
||||
println!("-[NSObject hash] return type: {:?}", hash_return);
|
||||
assert!(usize::ENCODING.equivalent_to_str(&hash_return));
|
||||
}
|
||||
|
||||
// Create an instance
|
||||
let obj = NSObject::new();
|
||||
|
||||
println!("NSObject address: {:p}", obj);
|
||||
|
||||
// Access an ivar of the object
|
||||
//
|
||||
// As before, you should not rely on the `isa` ivar being available!
|
||||
let isa = unsafe { *obj.ivar::<*const Class>("isa") };
|
||||
println!("NSObject isa: {:?}", isa);
|
||||
}
|
||||
144
third-party/vendor/objc2/examples/nspasteboard.rs
vendored
Normal file
144
third-party/vendor/objc2/examples/nspasteboard.rs
vendored
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
//! Read from the global pasteboard, and write a new string into it.
|
||||
//!
|
||||
//! Works on macOS 10.7+
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![cfg_attr(not(all(feature = "apple", target_os = "macos")), allow(unused))]
|
||||
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
use objc2::foundation::{NSArray, NSDictionary, NSInteger, NSObject, NSString};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::runtime::{Class, Object};
|
||||
use objc2::{extern_class, msg_send, msg_send_id, ClassType};
|
||||
|
||||
type NSPasteboardType = NSString;
|
||||
type NSPasteboardReadingOptionKey = NSString;
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
extern "C" {
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboardtypestring?language=objc>
|
||||
static NSPasteboardTypeString: Option<&'static NSPasteboardType>;
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard?language=objc>
|
||||
pub struct NSPasteboard;
|
||||
|
||||
// SAFETY: NSPasteboard actually inherits from NSObject.
|
||||
unsafe impl ClassType for NSPasteboard {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
impl NSPasteboard {
|
||||
/// We return a `Shared` `Id` because `general` can easily be called
|
||||
/// again, and it would return the same object, resulting in two aliasing
|
||||
/// mutable references if we returned an `Owned` Id.
|
||||
///
|
||||
/// Besides, even if we could prevent this, there might be Objective-C
|
||||
/// code somewhere else that accesses this instance.
|
||||
///
|
||||
/// TODO: Is this safe to call outside the main thread?
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1530091-generalpasteboard?language=objc>
|
||||
pub fn general() -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), generalPasteboard] }
|
||||
}
|
||||
|
||||
/// Simple, straightforward implementation
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1533566-stringfortype?language=objc>
|
||||
pub fn text_impl_1(&self) -> Id<NSString, Shared> {
|
||||
let s = unsafe { NSPasteboardTypeString }.unwrap();
|
||||
unsafe { msg_send_id![self, stringForType: s] }
|
||||
}
|
||||
|
||||
/// More complex implementation using `readObjectsForClasses:options:`,
|
||||
/// intended to show some how some patterns might require more knowledge
|
||||
/// of nitty-gritty details.
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1524454-readobjectsforclasses?language=objc>
|
||||
pub fn text_impl_2(&self) -> Id<NSString, Shared> {
|
||||
// The NSPasteboard API is a bit weird, it requires you to pass
|
||||
// classes as objects, which `objc2::foundation::NSArray` was not
|
||||
// really made for - so we convert the class to an `Object` type
|
||||
// instead. Also, we wrap it in `ManuallyDrop` because I'm not sure
|
||||
// how classes handle `release` calls?
|
||||
//
|
||||
// TODO: Investigate and find a better way to express this in objc2.
|
||||
let string_classes: ManuallyDrop<[Id<Object, Shared>; 1]> = {
|
||||
let cls: *const Class = NSString::class();
|
||||
let cls = cls as *mut Object;
|
||||
unsafe { ManuallyDrop::new([Id::new(cls).unwrap()]) }
|
||||
};
|
||||
// Temporary, see https://github.com/rust-lang/rust-clippy/issues/9101
|
||||
#[allow(unknown_lints, clippy::explicit_auto_deref)]
|
||||
let class_array = NSArray::from_slice(&*string_classes);
|
||||
let options = NSDictionary::new();
|
||||
let objects = unsafe { self.read_objects_for_classes(&class_array, &options) };
|
||||
|
||||
// TODO: Should perhaps return Id<Object, Shared>?
|
||||
let ptr: *const Object = objects.first().unwrap();
|
||||
|
||||
// And this part is weird as well, since we now have to convert the
|
||||
// object into an NSString, which we know it to be since that's what
|
||||
// we told `readObjectsForClasses:options:`.
|
||||
let ptr = ptr as *mut NSString;
|
||||
unsafe { Id::retain(ptr) }.unwrap()
|
||||
}
|
||||
|
||||
/// Defined here to make it easier to declare which types are expected.
|
||||
/// This is a common pattern that I can wholeheartedly recommend!
|
||||
///
|
||||
/// SAFETY: `class_array` must contain classes!
|
||||
unsafe fn read_objects_for_classes(
|
||||
&self,
|
||||
class_array: &NSArray<Object, Shared>,
|
||||
options: &NSDictionary<NSPasteboardReadingOptionKey, Object>,
|
||||
) -> Id<NSArray<Object, Shared>, Shared> {
|
||||
unsafe { msg_send_id![self, readObjectsForClasses: class_array, options: options] }
|
||||
}
|
||||
|
||||
/// This takes `&self` even though `writeObjects:` would seem to mutate
|
||||
/// the pasteboard. "What is going on?", you might rightfully ask!
|
||||
///
|
||||
/// We do this because we can't soundly get a mutable reference to the
|
||||
/// global `NSPasteboard` instance, see [`NSPasteboard::general`].
|
||||
///
|
||||
/// This is sound because `NSPasteboard` contains `NSObject`, which in
|
||||
/// turn contains `UnsafeCell`, allowing interior mutability.
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1533599-clearcontents?language=objc>
|
||||
/// <https://developer.apple.com/documentation/appkit/nspasteboard/1525945-writeobjects?language=objc>
|
||||
pub fn set_text(&self, text: Id<NSString, Shared>) {
|
||||
let _: NSInteger = unsafe { msg_send![self, clearContents] };
|
||||
let string_array = NSArray::from_slice(&[text]);
|
||||
let res: bool = unsafe { msg_send![self, writeObjects: &*string_array] };
|
||||
if !res {
|
||||
panic!("Failed writing to pasteboard");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
fn main() {
|
||||
let pasteboard = NSPasteboard::general();
|
||||
let impl_1 = pasteboard.text_impl_1();
|
||||
let impl_2 = pasteboard.text_impl_2();
|
||||
println!("Pasteboard text from implementation 1 was: {:?}", impl_1);
|
||||
println!("Pasteboard text from implementation 2 was: {:?}", impl_2);
|
||||
assert_eq!(impl_1, impl_2);
|
||||
|
||||
let s = NSString::from_str("Hello, world!");
|
||||
pasteboard.set_text(s.clone());
|
||||
println!("Now the pasteboard text should be: {:?}", s);
|
||||
assert_eq!(s, pasteboard.text_impl_1());
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "apple", target_os = "macos")))]
|
||||
fn main() {
|
||||
panic!("this example only works on macOS");
|
||||
}
|
||||
178
third-party/vendor/objc2/examples/speech_synthethis.rs
vendored
Normal file
178
third-party/vendor/objc2/examples/speech_synthethis.rs
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
//! Speak synthethized text.
|
||||
//!
|
||||
//! This uses `NSSpeechSynthesizer` on macOS, and `AVSpeechSynthesizer` on
|
||||
//! other Apple platforms. Note that `AVSpeechSynthesizer` _is_ available on
|
||||
//! macOS, but only since 10.15!
|
||||
//!
|
||||
//! TODO: Unsure about when to use `&mut` here?
|
||||
//!
|
||||
//! Works on macOS >= 10.7 and iOS > 7.0.
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![cfg_attr(feature = "gnustep-1-7", allow(unused))]
|
||||
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use objc2::foundation::{NSObject, NSString};
|
||||
use objc2::rc::{Id, Owned};
|
||||
use objc2::{extern_class, msg_send, msg_send_id, ns_string, ClassType};
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
mod appkit {
|
||||
use objc2::{foundation::NSCopying, rc::Shared};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
extern "C" {}
|
||||
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/appkit/nsspeechsynthesizer?language=objc>
|
||||
pub struct NSSpeechSynthesizer;
|
||||
|
||||
unsafe impl ClassType for NSSpeechSynthesizer {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
impl NSSpeechSynthesizer {
|
||||
// Uses default voice
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
fn set_rate(&mut self, rate: f32) {
|
||||
unsafe { msg_send![self, setRate: rate] }
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, volume: f32) {
|
||||
unsafe { msg_send![self, setVolume: volume] }
|
||||
}
|
||||
|
||||
fn start_speaking(&mut self, s: &NSString) {
|
||||
unsafe { msg_send![self, startSpeakingString: s] }
|
||||
}
|
||||
|
||||
pub fn speak(&mut self, utterance: &Utterance) {
|
||||
// Convert to the range 90-720 that `NSSpeechSynthesizer` seems to
|
||||
// support
|
||||
//
|
||||
// Note that you'd probably want a nonlinear conversion here to
|
||||
// make it match `AVSpeechSynthesizer`.
|
||||
self.set_rate(90.0 + (utterance.rate * (360.0 - 90.0)));
|
||||
self.set_volume(utterance.volume);
|
||||
self.start_speaking(&utterance.string);
|
||||
}
|
||||
|
||||
pub fn is_speaking(&self) -> bool {
|
||||
unsafe { msg_send![self, isSpeaking] }
|
||||
}
|
||||
}
|
||||
|
||||
// Shim to make NSSpeechSynthesizer work similar to AVSpeechSynthesizer
|
||||
pub struct Utterance {
|
||||
rate: f32,
|
||||
volume: f32,
|
||||
string: Id<NSString, Shared>,
|
||||
}
|
||||
|
||||
impl Utterance {
|
||||
pub fn new(string: &NSString) -> Self {
|
||||
Self {
|
||||
rate: 0.5,
|
||||
volume: 1.0,
|
||||
string: string.copy(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_rate(&mut self, rate: f32) {
|
||||
self.rate = rate;
|
||||
}
|
||||
|
||||
pub fn set_volume(&mut self, volume: f32) {
|
||||
self.volume = volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", not(target_os = "macos")))]
|
||||
mod avfaudio {
|
||||
use super::*;
|
||||
|
||||
#[link(name = "AVFoundation", kind = "framework")]
|
||||
extern "C" {}
|
||||
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/avfaudio/avspeechsynthesizer?language=objc>
|
||||
#[derive(Debug)]
|
||||
pub struct AVSpeechSynthesizer;
|
||||
|
||||
unsafe impl ClassType for AVSpeechSynthesizer {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
impl AVSpeechSynthesizer {
|
||||
pub fn new() -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![Self::class(), new] }
|
||||
}
|
||||
|
||||
pub fn speak(&mut self, utterance: &AVSpeechUtterance) {
|
||||
unsafe { msg_send![self, speakUtterance: utterance] }
|
||||
}
|
||||
|
||||
pub fn is_speaking(&self) -> bool {
|
||||
unsafe { msg_send![self, isSpeaking] }
|
||||
}
|
||||
}
|
||||
|
||||
extern_class!(
|
||||
/// <https://developer.apple.com/documentation/avfaudio/avspeechutterance?language=objc>
|
||||
#[derive(Debug)]
|
||||
pub struct AVSpeechUtterance;
|
||||
|
||||
unsafe impl ClassType for AVSpeechUtterance {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
impl AVSpeechUtterance {
|
||||
pub fn new(string: &NSString) -> Id<Self, Owned> {
|
||||
unsafe { msg_send_id![msg_send_id![Self::class(), alloc], initWithString: string] }
|
||||
}
|
||||
|
||||
pub fn set_rate(&mut self, rate: f32) {
|
||||
unsafe { msg_send![self, setRate: rate] }
|
||||
}
|
||||
|
||||
pub fn set_volume(&mut self, volume: f32) {
|
||||
unsafe { msg_send![self, setVolume: volume] }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "apple", target_os = "macos"))]
|
||||
use appkit::{NSSpeechSynthesizer as Synthesizer, Utterance};
|
||||
#[cfg(all(feature = "apple", not(target_os = "macos")))]
|
||||
use avfaudio::{AVSpeechSynthesizer as Synthesizer, AVSpeechUtterance as Utterance};
|
||||
|
||||
#[cfg(feature = "apple")]
|
||||
fn main() {
|
||||
let mut synthesizer = Synthesizer::new();
|
||||
let mut utterance = Utterance::new(ns_string!("Hello from Rust!"));
|
||||
utterance.set_rate(0.5);
|
||||
utterance.set_volume(0.5);
|
||||
synthesizer.speak(&utterance);
|
||||
|
||||
// Wait until speech has properly started up
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
// Wait until finished speaking
|
||||
while synthesizer.is_speaking() {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gnustep-1-7")]
|
||||
fn main() {
|
||||
panic!("this example is only available on Apple targets");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue