Skip to main content

SCContentSharingPicker

Struct SCContentSharingPicker 

Source
pub struct SCContentSharingPicker;
Expand description

System UI for selecting content to share

Available on macOS 14.0+

The picker requires user interaction and cannot block the calling thread. Use one of these approaches:

  • Callback-based: show() / show_filter() - pass a callback closure
  • Async/await: AsyncSCContentSharingPicker from the async_api module

§Example (callback)

use screencapturekit::content_sharing_picker::*;

let config = SCContentSharingPickerConfiguration::new().expect("create picker configuration");
SCContentSharingPicker::show(&config, |outcome| {
    if let SCPickerOutcome::Picked(result) = outcome {
        let (width, height) = result.pixel_size();
        let filter = result.filter();
        // ... create stream
    }
});

§Example (async)

use screencapturekit::async_api::AsyncSCContentSharingPicker;
use screencapturekit::content_sharing_picker::*;

async fn example() {
    let config = SCContentSharingPickerConfiguration::new().expect("create picker configuration");
    if let SCPickerOutcome::Picked(result) = AsyncSCContentSharingPicker::show(&config).await {
        let (width, height) = result.pixel_size();
        let filter = result.filter();
        // ... create stream
    }
}

Implementations§

Source§

impl SCContentSharingPicker

Source

pub fn is_available() -> bool

Whether content-sharing picker APIs are available on this system.

Source

pub fn show<F>(config: &SCContentSharingPickerConfiguration, callback: F)
where F: FnOnce(SCPickerOutcome) + Send + 'static,

Show the picker UI with a callback for the result

This is non-blocking - the callback is invoked when the user makes a selection or cancels the picker.

§Example
use screencapturekit::content_sharing_picker::*;

let config = SCContentSharingPickerConfiguration::new().expect("create picker configuration");
SCContentSharingPicker::show(&config, |outcome| {
    match outcome {
        SCPickerOutcome::Picked(result) => {
            let (width, height) = result.pixel_size();
            let filter = result.filter();
            println!("Selected {}x{}", width, height);
        }
        SCPickerOutcome::Cancelled => println!("Cancelled"),
        SCPickerOutcome::Error(e) => eprintln!("Error: {}", e),
    }
});
Source

pub fn show_for_stream<F>( config: &SCContentSharingPickerConfiguration, stream: &SCStream, callback: F, )
where F: FnOnce(SCPickerOutcome) + Send + 'static,

Show the picker UI for an existing stream (to change source while capturing)

Use this when you have an active SCStream and want to let the user select a new content source. The callback receives the new filter which can be used with stream.update_content_filter().

§Example
use screencapturekit::content_sharing_picker::*;
use screencapturekit::stream::SCStream;
use screencapturekit::stream::configuration::SCStreamConfiguration;
use screencapturekit::stream::content_filter::SCContentFilter;
use screencapturekit::shareable_content::SCShareableContent;

fn example() -> Option<()> {
    let content = SCShareableContent::get().ok()?;
    let displays = content.displays();
    let display = displays.first()?;
    let filter = SCContentFilter::create().with_display(display).with_excluding_windows(&[]).build().ok()?;
    let stream_config = SCStreamConfiguration::new();
    let stream = SCStream::new(&filter, &stream_config).ok()?;

    // When stream is active and user wants to change source
    let config = SCContentSharingPickerConfiguration::new().ok()?;
    SCContentSharingPicker::show_for_stream(&config, &stream, |outcome| {
        if let SCPickerOutcome::Picked(result) = outcome {
            // Use result.filter() with stream.update_content_filter()
            let _ = result.filter();
        }
    });
    Some(())
}
Source

pub fn show_filter<F>(config: &SCContentSharingPickerConfiguration, callback: F)
where F: FnOnce(SCPickerFilterOutcome) + Send + 'static,

Show the picker UI with a callback that receives just the filter

This is the simple API - use when you just need the filter without metadata.

§Example
use screencapturekit::content_sharing_picker::*;

let config = SCContentSharingPickerConfiguration::new().expect("create picker configuration");
SCContentSharingPicker::show_filter(&config, |outcome| {
    if let SCPickerFilterOutcome::Filter(filter) = outcome {
        // Use filter with SCStream
    }
});
Source

pub fn show_using_style<F>( config: &SCContentSharingPickerConfiguration, style: SCShareableContentStyle, callback: F, )
where F: FnOnce(SCPickerOutcome) + Send + 'static,

Show the picker UI with a specific content style

Presents the picker pre-filtered to a specific content type.

§Arguments
  • config - The picker configuration
  • style - The content style to show (Window, Display, Application)
  • callback - Called with the picker result
Source

pub fn show_for_stream_using_style<F>( config: &SCContentSharingPickerConfiguration, stream: &SCStream, style: SCShareableContentStyle, callback: F, )
where F: FnOnce(SCPickerOutcome) + Send + 'static,

Show the picker for an existing stream with a specific content style

§Arguments
  • config - The picker configuration
  • stream - The stream to update
  • style - The content style to show (Window, Display, Application)
  • callback - Called with the picker result
Source

pub fn set_maximum_stream_count( count: usize, ) -> Result<(), SCPickerConfigurationError>

Set the maximum number of streams that can be created from the picker

Pass 0 to allow unlimited streams.

Source

pub fn maximum_stream_count() -> usize

Get the maximum number of streams allowed

Returns 0 if unlimited streams are allowed.

Source

pub fn is_active() -> bool

Returns whether the shared content-sharing picker is currently marked active.

Apple requires picker.isActive = true before its UI can appear. The various show*() trampolines on this type set it implicitly before presenting, but this getter is useful for callers that want to:

  • avoid double-presenting (skip a second show() while the first picker session is still up),
  • render UI affordances based on whether the picker is currently visible to the user.
Source

pub fn set_active(active: bool) -> Result<(), SCPickerConfigurationError>

Mark the shared content-sharing picker active or inactive.

Setting this to false hides the picker UI between sessions (the recommended hygiene step after a long-running app finishes using the picker — leaving it active leaves the system-level Control Center entry in a “ready to share” state).

Setting to true is required before present*() can surface the picker; the show*() trampolines do this for you. Set it manually only if you want to opt into the picker UI without immediately presenting it.

Source

pub fn deactivate()

Deactivate the picker and undo any activation-policy promotion the bridge performed on behalf of a non-UI (.prohibited) host process.

Presenting SCContentSharingPicker requires the process to be a regular, Dock-visible app. Pure-Rust hosts usually are not, so the bridge temporarily promotes them; the promotion is reference counted and unwound automatically when each one-shot show*() resolves. Call this after you are done with a long-lived observer session to drop the promotion immediately and clear the Control Center “ready to share” indicator.

Registered observers are not removed — drop their SCPickerSubscription for that.

Source

pub fn default_configuration() -> Result<SCContentSharingPickerConfiguration, SCError>

Read the picker’s process-wide default configuration.

Equivalent to Apple’s SCContentSharingPicker.shared.defaultConfiguration. This is the same value returned by SCContentSharingPickerConfiguration::default_from_system.

§Errors

Returns SCError::FeatureNotAvailable when run on macOS older than 14.0.

Source

pub fn set_default_configuration( config: &SCContentSharingPickerConfiguration, ) -> Result<(), SCPickerConfigurationError>

Assign the picker’s process-wide default configuration.

Apple’s SCContentSharingPicker.defaultConfiguration is read-write; previously this crate could only set it as a side effect of calling a show*() helper. Setting it explicitly is what you want when driving the picker with a persistent observer plus Self::present.

§Examples
use screencapturekit::content_sharing_picker::*;

let mut config = SCContentSharingPickerConfiguration::new().expect("create picker configuration");
config.set_allows_changing_selected_content(true);
SCContentSharingPicker::set_default_configuration(&config)
    .expect("call from the process main thread");

The assignment is complete when this method returns, so an immediate call to Self::default_configuration observes the new value.

§Errors

Returns SCPickerConfigurationError::Unavailable when the API is unavailable, or SCPickerConfigurationError::MainThreadRequired when called from any other thread. Failed calls do not enqueue a later mutation.

Source

pub fn set_configuration_for_stream( config: Option<&SCContentSharingPickerConfiguration>, stream: &SCStream, ) -> Result<(), SCPickerConfigurationError>

Assign a picker configuration scoped to a single stream, mirroring Apple’s setConfiguration(_:for:).

Pass None to clear the stream-specific configuration and fall back to the process-wide default.

The assignment is complete when this method returns.

§Errors

Returns SCPickerConfigurationError::Unavailable when the API is unavailable, or SCPickerConfigurationError::MainThreadRequired when called from any other thread. Failed calls do not enqueue a later mutation.

Source

pub fn add_observer<F>( handler: F, ) -> Result<SCPickerSubscription, SCPickerConfigurationError>
where F: Fn(SCPickerEvent) + Send + Sync + 'static,

Register a repeating observer that receives every picker event for as long as the returned subscription is alive.

This is the API to use with SCContentSharingPickerConfiguration::set_allows_changing_selected_content: Apple re-invokes contentSharingPicker(_:didUpdateWith:for:) each time the user re-picks during an active share, and the one-shot Self::show family deliberately latches after the first event.

The subscription unregisters on drop, so bind it to a variable that lives as long as you want events (use SCPickerSubscription::detach to keep it for the remainder of the process).

Pair this with Self::present / Self::present_for_stream to surface the UI.

Apple marks SCContentSharingPicker as @MainActor. Call this on the process main thread, or while an AppKit main run loop is active so the bridge can synchronously hop to it.

§Errors

Returns SCPickerConfigurationError::Unavailable below macOS 14.0, and SCPickerConfigurationError::MainThreadRequired when called off the main thread without an active main run loop.

§Examples
use screencapturekit::content_sharing_picker::*;

let mut config = SCContentSharingPickerConfiguration::new().expect("create picker configuration");
config.set_allows_changing_selected_content(true);
SCContentSharingPicker::set_default_configuration(&config)
    .expect("call from the process main thread");

let subscription = SCContentSharingPicker::add_observer(|event| match event {
    SCPickerEvent::Updated { result, stream } => {
        // Fires again every time the user changes their selection.
        let _filter = result.filter();
        let _existing_stream = stream;
    }
    SCPickerEvent::Cancelled { .. } => println!("cancelled"),
    SCPickerEvent::Failed(err) => eprintln!("picker failed: {err}"),
}).expect("register picker observer");

SCContentSharingPicker::present().expect("present picker");
// ... keep `subscription` alive for as long as you want updates ...
drop(subscription);
Source

pub fn remove_all_observers() -> usize

Remove every repeating observer registered through Self::add_observer, regardless of which subscriptions are still alive. Returns how many were removed.

Dropping the corresponding SCPickerSubscription afterwards is harmless — removal is idempotent.

Source

pub fn present() -> Result<(), SCPickerConfigurationError>

Present the picker without a content-style hint.

Use with Self::add_observer; the one-shot Self::show family presents for you.

Source

pub fn present_using_style( style: SCShareableContentStyle, ) -> Result<(), SCPickerConfigurationError>

Present the picker preselecting a content style.

Source

pub fn present_for_stream( stream: &SCStream, ) -> Result<(), SCPickerConfigurationError>

Present the picker targeting an existing stream, so the user can swap the shared source mid-capture.

Source

pub fn present_for_stream_using_style( stream: &SCStream, style: SCShareableContentStyle, ) -> Result<(), SCPickerConfigurationError>

Present the picker targeting an existing stream, preselecting a style.

Trait Implementations§

Source§

impl Debug for SCContentSharingPicker

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.