22using System . Collections . Generic ;
33using System . Diagnostics ;
44using System . IO ;
5- using System . IO . Pipes ;
65using System . Net . Http ;
76using System . Reflection ;
87using System . Text ;
98using System . Text . Json ;
109using System . Threading ;
1110using System . Threading . Tasks ;
12- using System . Linq ;
1311
1412using Avalonia ;
1513using Avalonia . Controls ;
@@ -48,8 +46,6 @@ public static void Main(string[] args)
4846 Environment . Exit ( exitTodo ) ;
4947 else if ( TryLaunchAsRebaseMessageEditor ( args , out int exitMessage ) )
5048 Environment . Exit ( exitMessage ) ;
51- else if ( TrySendArgsToExistingInstance ( args ) )
52- Environment . Exit ( 0 ) ;
5349 else
5450 BuildAvaloniaApp ( ) . StartWithClassicDesktopLifetime ( args ) ;
5551 }
@@ -81,44 +77,6 @@ public static AppBuilder BuildAvaloniaApp()
8177 return builder ;
8278 }
8379
84- private static bool TrySendArgsToExistingInstance ( string [ ] args )
85- {
86- if ( args == null || args . Length != 1 || ! Directory . Exists ( args [ 0 ] ) )
87- return false ;
88-
89- var pref = ViewModels . Preferences . Instance ;
90-
91- if ( ! pref . OpenReposInNewTab )
92- return false ;
93-
94- try
95- {
96- var processes = Process . GetProcessesByName ( "SourceGit" ) ;
97-
98- if ( processes . Length <= 1 )
99- return false ;
100-
101- using var client = new NamedPipeClientStream ( "." , "SourceGitIPC" , PipeDirection . Out ) ;
102-
103- client . Connect ( 1000 ) ;
104-
105- if ( client . IsConnected )
106- {
107- using var writer = new StreamWriter ( client ) ;
108-
109- writer . WriteLine ( args [ 0 ] ) ;
110- writer . Flush ( ) ;
111-
112- return true ;
113- }
114- }
115- catch ( Exception )
116- {
117- }
118-
119- return false ;
120- }
121-
12280 private static void LogException ( Exception ex )
12381 {
12482 if ( ex == null )
@@ -370,13 +328,7 @@ public override void Initialize()
370328 AvaloniaXamlLoader . Load ( this ) ;
371329
372330 var pref = ViewModels . Preferences . Instance ;
373-
374- pref . PropertyChanged += ( s , e ) => {
375- pref . Save ( ) ;
376-
377- if ( e . PropertyName . Equals ( nameof ( ViewModels . Preferences . OpenReposInNewTab ) ) )
378- HandleOpenReposInNewTabChanged ( ) ;
379- } ;
331+ pref . PropertyChanged += ( _ , _ ) => pref . Save ( ) ;
380332
381333 SetLocale ( pref . Locale ) ;
382334 SetTheme ( pref . Theme , pref . ThemeOverrides ) ;
@@ -395,7 +347,17 @@ public override void OnFrameworkInitializationCompleted()
395347 if ( TryLaunchAsAskpass ( desktop ) )
396348 return ;
397349
398- TryLaunchAsNormal ( desktop ) ;
350+ _ipcChannel = new Models . IpcChannel ( ) ;
351+ if ( ! _ipcChannel . IsFirstInstance )
352+ {
353+ _ipcChannel . SendToFirstInstance ( desktop . Args is { Length : 1 } ? desktop . Args [ 0 ] : string . Empty ) ;
354+ Quit ( 0 ) ;
355+ }
356+ else
357+ {
358+ _ipcChannel . MessageReceived += TryOpenRepository ;
359+ TryLaunchAsNormal ( desktop ) ;
360+ }
399361 }
400362 }
401363 #endregion
@@ -533,104 +495,33 @@ private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
533495 if ( desktop . Args != null && desktop . Args . Length == 1 && Directory . Exists ( desktop . Args [ 0 ] ) )
534496 startupRepo = desktop . Args [ 0 ] ;
535497
536- _launcher = new ViewModels . Launcher ( startupRepo ) ;
537- desktop . MainWindow = new Views . Launcher ( ) { DataContext = _launcher } ;
538-
539498 var pref = ViewModels . Preferences . Instance ;
499+ pref . SetCanModify ( ) ;
540500
541- HandleOpenReposInNewTabChanged ( ) ;
501+ _launcher = new ViewModels . Launcher ( startupRepo ) ;
502+ desktop . MainWindow = new Views . Launcher ( ) { DataContext = _launcher } ;
503+ desktop . Exit += ( _ , _ ) => _ipcChannel . Dispose ( ) ;
542504
543505#if ! DISABLE_UPDATE_DETECTION
544506 if ( pref . ShouldCheck4UpdateOnStartup ( ) )
545507 Check4Update ( ) ;
546508#endif
547509 }
548510
549- private void HandleOpenReposInNewTabChanged ( )
511+ private void TryOpenRepository ( string repo )
550512 {
551- var pref = ViewModels . Preferences . Instance ;
552-
553- if ( pref . OpenReposInNewTab )
554- {
555- if ( _ipcServerTask == null || _ipcServerTask . IsCompleted )
556- {
557- // Start IPC server
558- _ipcServerCts = new CancellationTokenSource ( ) ;
559- _ipcServerTask = Task . Run ( ( ) => StartIPCServer ( _ipcServerCts . Token ) ) ;
560- }
561- }
562- else
563- {
564- // Stop IPC server if running
565- if ( _ipcServerCts != null && ! _ipcServerCts . IsCancellationRequested )
566- {
567- _ipcServerCts . Cancel ( ) ;
568- _ipcServerCts . Dispose ( ) ;
569- _ipcServerCts = null ;
570- }
571- _ipcServerTask = null ;
572- }
573- }
513+ if ( string . IsNullOrEmpty ( repo ) || ! Directory . Exists ( repo ) )
514+ return ;
574515
575- private void StartIPCServer ( CancellationToken cancellationToken )
576- {
577- try
516+ var test = new Commands . QueryRepositoryRootPath ( repo ) . ReadToEnd ( ) ;
517+ if ( test . IsSuccess && ! string . IsNullOrEmpty ( test . StdOut ) )
578518 {
579- while ( ! cancellationToken . IsCancellationRequested )
519+ Dispatcher . UIThread . Invoke ( ( ) =>
580520 {
581- using var server = new NamedPipeServerStream ( "SourceGitIPC" , PipeDirection . In ) ;
582-
583- // Use WaitForConnectionAsync with cancellation token
584- try
585- {
586- Task connectionTask = server . WaitForConnectionAsync ( cancellationToken ) ;
587- connectionTask . Wait ( cancellationToken ) ;
588- }
589- catch ( OperationCanceledException )
590- {
591- return ;
592- }
593- catch ( AggregateException ae ) when ( ae . InnerExceptions . Any ( e => e is OperationCanceledException ) )
594- {
595- return ;
596- }
597-
598- // Process the connection
599- using var reader = new StreamReader ( server ) ;
600- var repoPath = reader . ReadLine ( ) ;
601-
602- if ( ! string . IsNullOrEmpty ( repoPath ) && Directory . Exists ( repoPath ) )
603- {
604- Dispatcher . UIThread . Post ( ( ) =>
605- {
606- try
607- {
608- var test = new Commands . QueryRepositoryRootPath ( repoPath ) . ReadToEnd ( ) ;
609-
610- if ( test . IsSuccess && ! string . IsNullOrEmpty ( test . StdOut ) )
611- {
612- var repoRootPath = test . StdOut . Trim ( ) ;
613- var pref = ViewModels . Preferences . Instance ;
614- var node = pref . FindOrAddNodeByRepositoryPath ( repoRootPath , null , false ) ;
615-
616- ViewModels . Welcome . Instance . Refresh ( ) ;
617-
618- _launcher ? . OpenRepositoryInTab ( node , null ) ;
619-
620- if ( ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && desktop . MainWindow != null )
621- desktop . MainWindow . Activate ( ) ;
622- }
623- }
624- catch ( Exception )
625- {
626- }
627- } ) ;
628- }
629- }
630- }
631- catch ( Exception )
632- {
633- // Pipe server failed, we can just exit the thread
521+ var node = ViewModels . Preferences . Instance . FindOrAddNodeByRepositoryPath ( test . StdOut . Trim ( ) , null , false ) ;
522+ ViewModels . Welcome . Instance . Refresh ( ) ;
523+ _launcher ? . OpenRepositoryInTab ( node , null ) ;
524+ } ) ;
634525 }
635526 }
636527
@@ -719,11 +610,10 @@ private string FixFontFamilyName(string input)
719610 return trimmed . Count > 0 ? string . Join ( ',' , trimmed ) : string . Empty ;
720611 }
721612
613+ private Models . IpcChannel _ipcChannel = null ;
722614 private ViewModels . Launcher _launcher = null ;
723615 private ResourceDictionary _activeLocale = null ;
724616 private ResourceDictionary _themeOverrides = null ;
725617 private ResourceDictionary _fontsOverrides = null ;
726- private Task _ipcServerTask = null ;
727- private CancellationTokenSource _ipcServerCts = null ;
728618 }
729619}
0 commit comments