@@ -28,6 +28,19 @@ static DPI_SCALES: &[u32] = &[50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 30
2828static DPI_SCALE_LABELS : LazyLock < Vec < String > > =
2929 LazyLock :: new ( || DPI_SCALES . iter ( ) . map ( |scale| format ! ( "{scale}%" ) ) . collect ( ) ) ;
3030
31+ /// Guard that ensures display identifiers are dismissed when dropped
32+ /// This handles the case where the app is killed and async cleanup doesn't complete
33+ struct DisplayIdentifierGuard ;
34+
35+ impl Drop for DisplayIdentifierGuard {
36+ fn drop ( & mut self ) {
37+ // Run synchronously to ensure it completes even if the process is being killed
38+ let _ = std:: process:: Command :: new ( "cosmic-osd" )
39+ . arg ( "dismiss-display-identifiers" )
40+ . output ( ) ;
41+ }
42+ }
43+
3144/// Display color depth options
3245#[ allow( dead_code) ]
3346#[ derive( Clone , Copy , Debug ) ]
@@ -138,6 +151,8 @@ pub struct Page {
138151 active_display : OutputKey ,
139152 randr_handle : Option < ( oneshot:: Sender < ( ) > , cosmic:: iced:: task:: Handle ) > ,
140153 hotplug_handle : Option < ( oneshot:: Sender < ( ) > , cosmic:: iced:: task:: Handle ) > ,
154+ display_identifier_handle : Option < ( oneshot:: Sender < ( ) > , ( ) ) > ,
155+ display_identifier_guard : Option < DisplayIdentifierGuard > ,
141156 config : Config ,
142157 cache : ViewCache ,
143158 // context: Option<ContextDrawer>,
@@ -163,6 +178,8 @@ impl Default for Page {
163178 active_display : OutputKey :: default ( ) ,
164179 randr_handle : None ,
165180 hotplug_handle : None ,
181+ display_identifier_handle : None ,
182+ display_identifier_guard : None ,
166183 config : Config :: default ( ) ,
167184 cache : ViewCache :: default ( ) ,
168185 // context: None,
@@ -365,6 +382,56 @@ impl page::Page<crate::pages::Message> for Page {
365382 tasks. push ( hotplug_task) ;
366383 self . hotplug_handle = Some ( ( hotplug_cancel_tx, hotplug_handle) ) ;
367384
385+ // Start a periodic task to send identify displays message every 0.5 seconds
386+ // This resets the 1-second auto-close timer in cosmic-osd
387+ let ( identifier_cancel_tx, identifier_cancel_rx) = oneshot:: channel :: < ( ) > ( ) ;
388+ let identifier_task =
389+ Task :: stream ( async_fn_stream:: fn_stream ( |_emitter| async move {
390+ let identifier_loop = async {
391+ loop {
392+ match tokio:: process:: Command :: new ( "cosmic-osd" )
393+ . arg ( "identify-displays" )
394+ . output ( )
395+ . await
396+ {
397+ Ok ( output) => {
398+ if !output. status . success ( ) {
399+ tracing:: error!(
400+ "cosmic-osd identify-displays failed: {}" ,
401+ String :: from_utf8_lossy( & output. stderr)
402+ ) ;
403+ }
404+ }
405+ Err ( why) => {
406+ tracing:: error!(
407+ why = why. to_string( ) ,
408+ "failed to execute cosmic-osd identify-displays"
409+ ) ;
410+ }
411+ }
412+
413+ tokio:: time:: sleep ( Duration :: from_millis ( 500 ) ) . await ;
414+ }
415+ } ;
416+
417+ tokio:: select! {
418+ _ = identifier_cancel_rx => {
419+ let _ = tokio:: process:: Command :: new( "cosmic-osd" )
420+ . arg( "dismiss-display-identifiers" )
421+ . output( )
422+ . await ;
423+ }
424+ _ = identifier_loop => {
425+ // Loop will never complete, but this branch is needed for tokio::select
426+ }
427+ }
428+ } ) ) ;
429+
430+ tasks. push ( identifier_task) ;
431+ self . display_identifier_handle = Some ( ( identifier_cancel_tx, ( ) ) ) ;
432+ // Create guard that will dismiss identifiers immediately if the app is killed
433+ self . display_identifier_guard = Some ( DisplayIdentifierGuard ) ;
434+
368435 cosmic:: task:: batch ( tasks)
369436 }
370437
@@ -379,29 +446,12 @@ impl page::Page<crate::pages::Message> for Page {
379446 handle. abort ( ) ;
380447 }
381448
382- // Dismiss display identifiers when leaving the page
383- tokio:: spawn ( async {
384- match tokio:: process:: Command :: new ( "cosmic-osd" )
385- . arg ( "dismiss-display-identifiers" )
386- . output ( )
387- . await
388- {
389- Ok ( output) => {
390- if !output. status . success ( ) {
391- tracing:: error!(
392- "cosmic-osd dismiss-display-identifiers failed: {}" ,
393- String :: from_utf8_lossy( & output. stderr)
394- ) ;
395- }
396- }
397- Err ( why) => {
398- tracing:: error!(
399- why = why. to_string( ) ,
400- "failed to execute cosmic-osd dismiss-display-identifiers"
401- ) ;
402- }
403- }
404- } ) ;
449+ if let Some ( ( canceller, ( ) ) ) = self . display_identifier_handle . take ( ) {
450+ _ = canceller. send ( ( ) ) ;
451+ }
452+
453+ // Remove the guard so it doesn't dismiss again after the async task completes
454+ self . display_identifier_guard = None ;
405455
406456 Task :: none ( )
407457 }
@@ -641,38 +691,6 @@ impl Page {
641691 match Arc :: into_inner ( randr) {
642692 Some ( Ok ( outputs) ) => {
643693 self . update_displays ( outputs) ;
644-
645- // Show display identifiers if there are 2+ enabled displays
646- let enabled_count =
647- self . list . outputs . values ( ) . filter ( |o| o. enabled ) . count ( ) ;
648-
649- if enabled_count >= 2 {
650- return Task :: perform (
651- async {
652- match tokio:: process:: Command :: new ( "cosmic-osd" )
653- . arg ( "identify-displays" )
654- . output ( )
655- . await
656- {
657- Ok ( output) => {
658- if !output. status . success ( ) {
659- tracing:: error!(
660- "cosmic-osd identify-displays failed: {}" ,
661- String :: from_utf8_lossy( & output. stderr)
662- ) ;
663- }
664- }
665- Err ( why) => {
666- tracing:: error!(
667- why = why. to_string( ) ,
668- "failed to execute cosmic-osd identify-displays"
669- ) ;
670- }
671- }
672- } ,
673- |_| app:: Message :: None ,
674- ) ;
675- }
676694 }
677695
678696 Some ( Err ( why) ) => {
0 commit comments