@@ -79,6 +79,8 @@ use tauri_plugin_notification::{NotificationExt, PermissionState};
7979use tauri_plugin_opener:: OpenerExt ;
8080use tauri_plugin_shell:: ShellExt ;
8181use tauri_specta:: Event ;
82+ #[ cfg( target_os = "macos" ) ]
83+ use tokio:: sync:: Mutex ;
8284use tokio:: sync:: { RwLock , oneshot} ;
8385use tracing:: { error, trace} ;
8486use upload:: { S3UploadMeta , create_or_get_video, upload_image, upload_video} ;
@@ -320,13 +322,94 @@ pub struct RequestOpenSettings {
320322 page : String ,
321323}
322324
325+ #[ derive( Deserialize , specta:: Type , Serialize , tauri_specta:: Event , Debug , Clone ) ]
326+ pub struct RequestScreenCapturePrewarm {
327+ #[ serde( default ) ]
328+ pub force : bool ,
329+ }
330+
323331#[ derive( Deserialize , specta:: Type , Serialize , tauri_specta:: Event , Debug , Clone ) ]
324332pub struct NewNotification {
325333 title : String ,
326334 body : String ,
327335 is_error : bool ,
328336}
329337
338+ #[ cfg( target_os = "macos" ) ]
339+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
340+ enum PrewarmState {
341+ Idle ,
342+ Warming ,
343+ Warmed ,
344+ }
345+
346+ #[ cfg( target_os = "macos" ) ]
347+ pub ( crate ) struct ScreenCapturePrewarmer {
348+ state : Mutex < PrewarmState > ,
349+ }
350+
351+ #[ cfg( target_os = "macos" ) ]
352+ impl Default for ScreenCapturePrewarmer {
353+ fn default ( ) -> Self {
354+ Self {
355+ state : Mutex :: new ( PrewarmState :: Idle ) ,
356+ }
357+ }
358+ }
359+
360+ #[ cfg( target_os = "macos" ) ]
361+ impl ScreenCapturePrewarmer {
362+ async fn request ( & self , force : bool ) {
363+ let should_start = {
364+ let mut state = self . state . lock ( ) . await ;
365+
366+ if force {
367+ * state = PrewarmState :: Idle ;
368+ }
369+
370+ match * state {
371+ PrewarmState :: Idle => {
372+ * state = PrewarmState :: Warming ;
373+ true
374+ }
375+ PrewarmState :: Warming => {
376+ trace ! ( "ScreenCaptureKit prewarm already in progress" ) ;
377+ false
378+ }
379+ PrewarmState :: Warmed => {
380+ if force {
381+ * state = PrewarmState :: Warming ;
382+ true
383+ } else {
384+ trace ! ( "ScreenCaptureKit cache already warmed" ) ;
385+ false
386+ }
387+ }
388+ }
389+ } ;
390+
391+ if !should_start {
392+ return ;
393+ }
394+
395+ let warm_start = std:: time:: Instant :: now ( ) ;
396+ let result = scap_targets:: prewarm_shareable_content ( ) . await ;
397+
398+ let mut state = self . state . lock ( ) . await ;
399+ match result {
400+ Ok ( ( ) ) => {
401+ let elapsed_ms = warm_start. elapsed ( ) . as_micros ( ) as f64 / 1000.0 ;
402+ * state = PrewarmState :: Warmed ;
403+ trace ! ( elapsed_ms, "ScreenCaptureKit cache warmed" ) ;
404+ }
405+ Err ( error) => {
406+ * state = PrewarmState :: Idle ;
407+ tracing:: warn!( error = %error, "ScreenCaptureKit prewarm failed" ) ;
408+ }
409+ }
410+ }
411+ }
412+
330413type ArcLock < T > = Arc < RwLock < T > > ;
331414pub type MutableState < ' a , T > = State < ' a , Arc < RwLock < T > > > ;
332415
@@ -1937,6 +2020,7 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
19372020 RequestOpenRecordingPicker ,
19382021 RequestNewScreenshot ,
19392022 RequestOpenSettings ,
2023+ RequestScreenCapturePrewarm ,
19402024 NewNotification ,
19412025 AuthenticationInvalid ,
19422026 audio_meter:: AudioInputLevelChange ,
@@ -2075,6 +2159,8 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
20752159 fake_window:: init ( & app) ;
20762160 app. manage ( target_select_overlay:: WindowFocusManager :: default ( ) ) ;
20772161 app. manage ( EditorWindowIds :: default ( ) ) ;
2162+ #[ cfg( target_os = "macos" ) ]
2163+ app. manage ( ScreenCapturePrewarmer :: default ( ) ) ;
20782164
20792165 tokio:: spawn ( {
20802166 let camera_feed = camera_feed. clone ( ) ;
@@ -2204,6 +2290,12 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
22042290 . await ;
22052291 } ) ;
22062292
2293+ #[ cfg( target_os = "macos" ) ]
2294+ RequestScreenCapturePrewarm :: listen_any_spawn ( & app, async |event, app| {
2295+ let prewarmer = app. state :: < ScreenCapturePrewarmer > ( ) ;
2296+ prewarmer. request ( event. force ) . await ;
2297+ } ) ;
2298+
22072299 let app_handle = app. clone ( ) ;
22082300 app. deep_link ( ) . on_open_url ( move |event| {
22092301 deeplink_actions:: handle ( & app_handle, event. urls ( ) ) ;
0 commit comments