|
2 | 2 | using System.Collections.Generic;
|
3 | 3 | using System.Diagnostics;
|
4 | 4 | using System.IO;
|
| 5 | +using System.IO.Pipes; |
5 | 6 | using System.Net.Http;
|
6 | 7 | using System.Reflection;
|
7 | 8 | using System.Text;
|
8 | 9 | using System.Text.Json;
|
9 | 10 | using System.Threading;
|
10 | 11 | using System.Threading.Tasks;
|
| 12 | +using System.Linq; |
11 | 13 |
|
12 | 14 | using Avalonia;
|
13 | 15 | using Avalonia.Controls;
|
@@ -46,6 +48,8 @@ public static void Main(string[] args)
|
46 | 48 | Environment.Exit(exitTodo);
|
47 | 49 | else if (TryLaunchAsRebaseMessageEditor(args, out int exitMessage))
|
48 | 50 | Environment.Exit(exitMessage);
|
| 51 | + else if (TrySendArgsToExistingInstance(args)) |
| 52 | + Environment.Exit(0); |
49 | 53 | else
|
50 | 54 | BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
51 | 55 | }
|
@@ -77,6 +81,44 @@ public static AppBuilder BuildAvaloniaApp()
|
77 | 81 | return builder;
|
78 | 82 | }
|
79 | 83 |
|
| 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 | + |
80 | 122 | private static void LogException(Exception ex)
|
81 | 123 | {
|
82 | 124 | if (ex == null)
|
@@ -328,7 +370,13 @@ public override void Initialize()
|
328 | 370 | AvaloniaXamlLoader.Load(this);
|
329 | 371 |
|
330 | 372 | var pref = ViewModels.Preferences.Instance;
|
331 |
| - pref.PropertyChanged += (_, _) => pref.Save(); |
| 373 | + |
| 374 | + pref.PropertyChanged += (s, e) => { |
| 375 | + pref.Save(); |
| 376 | + |
| 377 | + if (e.PropertyName.Equals(nameof(ViewModels.Preferences.OpenReposInNewTab))) |
| 378 | + HandleOpenReposInNewTabChanged(); |
| 379 | + }; |
332 | 380 |
|
333 | 381 | SetLocale(pref.Locale);
|
334 | 382 | SetTheme(pref.Theme, pref.ThemeOverrides);
|
@@ -488,13 +536,104 @@ private void TryLaunchAsNormal(IClassicDesktopStyleApplicationLifetime desktop)
|
488 | 536 | _launcher = new ViewModels.Launcher(startupRepo);
|
489 | 537 | desktop.MainWindow = new Views.Launcher() { DataContext = _launcher };
|
490 | 538 |
|
491 |
| -#if !DISABLE_UPDATE_DETECTION |
492 | 539 | var pref = ViewModels.Preferences.Instance;
|
| 540 | + |
| 541 | + HandleOpenReposInNewTabChanged(); |
| 542 | + |
| 543 | +#if !DISABLE_UPDATE_DETECTION |
493 | 544 | if (pref.ShouldCheck4UpdateOnStartup())
|
494 | 545 | Check4Update();
|
495 | 546 | #endif
|
496 | 547 | }
|
497 | 548 |
|
| 549 | + private void HandleOpenReposInNewTabChanged() |
| 550 | + { |
| 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 | + } |
| 574 | + |
| 575 | + private void StartIPCServer(CancellationToken cancellationToken) |
| 576 | + { |
| 577 | + try |
| 578 | + { |
| 579 | + while (!cancellationToken.IsCancellationRequested) |
| 580 | + { |
| 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 |
| 634 | + } |
| 635 | + } |
| 636 | + |
498 | 637 | private void Check4Update(bool manually = false)
|
499 | 638 | {
|
500 | 639 | Task.Run(async () =>
|
@@ -584,5 +723,7 @@ private string FixFontFamilyName(string input)
|
584 | 723 | private ResourceDictionary _activeLocale = null;
|
585 | 724 | private ResourceDictionary _themeOverrides = null;
|
586 | 725 | private ResourceDictionary _fontsOverrides = null;
|
| 726 | + private Task _ipcServerTask = null; |
| 727 | + private CancellationTokenSource _ipcServerCts = null; |
587 | 728 | }
|
588 | 729 | }
|
0 commit comments