@@ -344,6 +344,29 @@ async fn is_remote_session(session_id: &str) -> bool {
344344 false
345345}
346346
347+ fn emit_terminal_event ( app_handle : & AppHandle , event : & TerminalEvent ) -> bool {
348+ let event_name = "terminal_event" ;
349+ let local_emit_succeeded = match app_handle. emit ( event_name, event) {
350+ Ok ( ( ) ) => true ,
351+ Err ( error) => {
352+ warn ! ( "Failed to emit terminal event: {}" , error) ;
353+ false
354+ }
355+ } ;
356+ if let Ok ( payload) = serde_json:: to_value ( event) {
357+ super :: remote_connect_api:: maybe_fanout_peer_ui_event ( event_name, payload) ;
358+ }
359+ local_emit_succeeded
360+ }
361+
362+ fn remote_terminal_signal_bytes ( signal : & str ) -> Option < & ' static [ u8 ] > {
363+ match signal. trim ( ) . to_ascii_uppercase ( ) . as_str ( ) {
364+ "SIGINT" | "INT" => Some ( & [ 0x03 ] ) ,
365+ "SIGTSTP" | "TSTP" => Some ( & [ 0x1a ] ) ,
366+ _ => None ,
367+ }
368+ }
369+
347370async fn spawn_remote_pty_session (
348371 app : & AppHandle ,
349372 terminal_manager : & bitfun_core:: service:: remote_ssh:: RemoteTerminalManager ,
@@ -384,8 +407,8 @@ async fn spawn_remote_pty_session(
384407 let app_handle = app. clone ( ) ;
385408 let sid = session_id. clone ( ) ;
386409 tokio:: spawn ( async move {
387- let _ = app_handle . emit (
388- "terminal_event" ,
410+ emit_terminal_event (
411+ & app_handle ,
389412 & TerminalEvent :: Ready {
390413 session_id : sid. clone ( ) ,
391414 pid : 0 ,
@@ -397,14 +420,13 @@ async fn spawn_remote_pty_session(
397420 match rx. recv ( ) . await {
398421 Ok ( data) => {
399422 let text = String :: from_utf8_lossy ( & data) . to_string ( ) ;
400- if let Err ( e ) = app_handle . emit (
401- "terminal_event" ,
423+ if ! emit_terminal_event (
424+ & app_handle ,
402425 & TerminalEvent :: Data {
403426 session_id : sid. clone ( ) ,
404427 data : text,
405428 } ,
406429 ) {
407- warn ! ( "Failed to emit remote terminal event: {}" , e) ;
408430 break ;
409431 }
410432 }
@@ -421,8 +443,8 @@ async fn spawn_remote_pty_session(
421443 }
422444 }
423445
424- let _ = app_handle . emit (
425- "terminal_event" ,
446+ emit_terminal_event (
447+ & app_handle ,
426448 & TerminalEvent :: Exit {
427449 session_id : sid,
428450 exit_code : Some ( 0 ) ,
@@ -699,7 +721,22 @@ pub async fn terminal_signal(
699721 state : State < ' _ , TerminalState > ,
700722) -> Result < ( ) , String > {
701723 if is_remote_session ( & request. session_id ) . await {
702- // Remote terminals don't support signal yet
724+ let signal_data = remote_terminal_signal_bytes ( & request. signal ) . ok_or_else ( || {
725+ format ! (
726+ "Unsupported remote terminal signal: {}" ,
727+ request. signal. trim( )
728+ )
729+ } ) ?;
730+ let remote_manager =
731+ get_remote_workspace_manager ( ) . ok_or ( "Remote workspace manager not available" ) ?;
732+ let terminal_manager = remote_manager
733+ . get_terminal_manager ( )
734+ . await
735+ . ok_or ( "Remote terminal manager not available" ) ?;
736+ terminal_manager
737+ . write ( & request. session_id , signal_data)
738+ . await
739+ . map_err ( |e| format ! ( "Failed to send remote terminal signal: {}" , e) ) ?;
703740 return Ok ( ( ) ) ;
704741 }
705742
@@ -913,13 +950,21 @@ pub fn start_terminal_event_loop(terminal_state: TerminalState, app_handle: AppH
913950 let mut rx = api. subscribe_events ( ) ;
914951
915952 while let Some ( event) = rx. recv ( ) . await {
916- let event_name = "terminal_event" ;
917- if let Err ( e) = app_handle. emit ( event_name, & event) {
918- warn ! ( "Failed to emit terminal event: {}" , e) ;
919- }
920- if let Ok ( payload) = serde_json:: to_value ( & event) {
921- crate :: api:: remote_connect_api:: maybe_fanout_peer_ui_event ( event_name, payload) ;
922- }
953+ emit_terminal_event ( & app_handle, & event) ;
923954 }
924955 } ) ;
925956}
957+
958+ #[ cfg( test) ]
959+ mod tests {
960+ use super :: remote_terminal_signal_bytes;
961+
962+ #[ test]
963+ fn maps_supported_remote_terminal_signals_to_control_bytes ( ) {
964+ assert_eq ! ( remote_terminal_signal_bytes( "SIGINT" ) , Some ( & [ 0x03 ] [ ..] ) ) ;
965+ assert_eq ! ( remote_terminal_signal_bytes( "int" ) , Some ( & [ 0x03 ] [ ..] ) ) ;
966+ assert_eq ! ( remote_terminal_signal_bytes( "SIGTSTP" ) , Some ( & [ 0x1a ] [ ..] ) ) ;
967+ assert_eq ! ( remote_terminal_signal_bytes( "tstp" ) , Some ( & [ 0x1a ] [ ..] ) ) ;
968+ assert_eq ! ( remote_terminal_signal_bytes( "SIGTERM" ) , None ) ;
969+ }
970+ }
0 commit comments