diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
index 40e9c953..d5f9b1c5 100644
--- a/.github/workflows/build-linux.yml
+++ b/.github/workflows/build-linux.yml
@@ -22,7 +22,7 @@ jobs:
matrix:
configuration: [Release]
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: Checkout
diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props
index 24484112..9dcfbe61 100644
--- a/v2rayN/Directory.Packages.props
+++ b/v2rayN/Directory.Packages.props
@@ -5,6 +5,7 @@
false
+
@@ -19,6 +20,7 @@
+
@@ -27,4 +29,4 @@
-
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
index 8fc62dfe..8b3467bd 100644
--- a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
@@ -1,5 +1,6 @@
using System.Collections.Concurrent;
using System.Reactive.Linq;
+using System.Text;
using System.Text.RegularExpressions;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
@@ -9,9 +10,9 @@ namespace ServiceLib.ViewModels;
public class MsgViewModel : MyReactiveObject
{
private readonly ConcurrentQueue _queueMsg = new();
- private readonly int _numMaxMsg = 500;
- private bool _lastMsgFilterNotAvailable;
- private bool _blLockShow = false;
+ private volatile bool _lastMsgFilterNotAvailable;
+ private int _showLock = 0; // 0 = unlocked, 1 = locked
+ public int NumMaxMsg { get; } = 50;
[Reactive]
public string MsgFilter { get; set; }
@@ -33,46 +34,52 @@ public class MsgViewModel : MyReactiveObject
this.WhenAnyValue(
x => x.AutoRefresh,
y => y == true)
- .Subscribe(c => { _config.MsgUIItem.AutoRefresh = AutoRefresh; });
+ .Subscribe(c => _config.MsgUIItem.AutoRefresh = AutoRefresh);
AppEvents.SendMsgViewRequested
.AsObservable()
//.ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe(async content => await AppendQueueMsg(content));
+ .Subscribe(content => _ = AppendQueueMsg(content));
}
private async Task AppendQueueMsg(string msg)
{
- //if (msg == Global.CommandClearMsg)
- //{
- // ClearMsg();
- // return;
- //}
if (AutoRefresh == false)
{
return;
}
- _ = EnqueueQueueMsg(msg);
- if (_blLockShow)
- {
- return;
- }
+ EnqueueQueueMsg(msg);
+
if (!_config.UiItem.ShowInTaskbar)
{
return;
}
- _blLockShow = true;
+ if (Interlocked.CompareExchange(ref _showLock, 1, 0) != 0)
+ {
+ return;
+ }
- await Task.Delay(500);
- var txt = string.Join("", _queueMsg.ToArray());
- await _updateView?.Invoke(EViewAction.DispatcherShowMsg, txt);
+ try
+ {
+ await Task.Delay(500).ConfigureAwait(false);
- _blLockShow = false;
+ var sb = new StringBuilder();
+ while (_queueMsg.TryDequeue(out var line))
+ {
+ sb.Append(line);
+ }
+
+ await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString());
+ }
+ finally
+ {
+ Interlocked.Exchange(ref _showLock, 0);
+ }
}
- private async Task EnqueueQueueMsg(string msg)
+ private void EnqueueQueueMsg(string msg)
{
//filter msg
if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
@@ -91,26 +98,17 @@ public class MsgViewModel : MyReactiveObject
}
}
- //Enqueue
- if (_queueMsg.Count > _numMaxMsg)
- {
- for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++)
- {
- _queueMsg.TryDequeue(out _);
- }
- }
_queueMsg.Enqueue(msg);
if (!msg.EndsWith(Environment.NewLine))
{
_queueMsg.Enqueue(Environment.NewLine);
}
- await Task.CompletedTask;
}
- public void ClearMsg()
- {
- _queueMsg.Clear();
- }
+ //public void ClearMsg()
+ //{
+ // _queueMsg.Clear();
+ //}
private void DoMsgFilter()
{
diff --git a/v2rayN/v2rayN.Desktop/App.axaml b/v2rayN/v2rayN.Desktop/App.axaml
index 8b61125f..222ab488 100644
--- a/v2rayN/v2rayN.Desktop/App.axaml
+++ b/v2rayN/v2rayN.Desktop/App.axaml
@@ -11,6 +11,7 @@
RequestedThemeVariant="Default">
+
diff --git a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs
index 3473cf23..ae12fa07 100644
--- a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs
+++ b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs
@@ -5,6 +5,7 @@ using Avalonia.Controls.Notifications;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.Styling;
+using AvaloniaEdit;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Semi.Avalonia;
@@ -112,7 +113,8 @@ public class ThemeSettingViewModel : MyReactiveObject
x.OfType(),
x.OfType(),
x.OfType(),
- x.OfType()
+ x.OfType(),
+ x.OfType()
));
style.Add(new Setter()
{
@@ -153,7 +155,8 @@ public class ThemeSettingViewModel : MyReactiveObject
x.OfType(),
x.OfType(),
x.OfType(),
- x.OfType()
+ x.OfType(),
+ x.OfType()
));
style.Add(new Setter()
{
diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml
index b6ca81a1..2f7ad807 100644
--- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml
@@ -2,6 +2,7 @@
x:Class="v2rayN.Desktop.Views.MsgView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:avaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
@@ -70,35 +71,36 @@
Theme="{DynamicResource SimpleToggleSwitch}" />
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
index e2d21101..f7b2f9b9 100644
--- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs
@@ -1,5 +1,4 @@
using System.Reactive.Disposables;
-using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
@@ -10,13 +9,12 @@ namespace v2rayN.Desktop.Views;
public partial class MsgView : ReactiveUserControl
{
- private readonly ScrollViewer _scrollViewer;
+ private const int MaxLines = 350;
+ private const int KeepLines = 320;
public MsgView()
{
InitializeComponent();
- _scrollViewer = this.FindControl("msgScrollViewer");
-
ViewModel = new MsgViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
@@ -34,9 +32,8 @@ public partial class MsgView : ReactiveUserControl
if (obj is null)
return false;
- Dispatcher.UIThread.Post(() =>
- ShowMsg(obj),
- DispatcherPriority.ApplicationIdle);
+ Dispatcher.UIThread.Post(() => ShowMsg(obj),
+ DispatcherPriority.ApplicationIdle);
break;
}
return await Task.FromResult(true);
@@ -44,23 +41,35 @@ public partial class MsgView : ReactiveUserControl
private void ShowMsg(object msg)
{
- txtMsg.Text = msg.ToString();
+ txtMsg.AppendText(msg.ToString());
+
+ if (txtMsg.Document.LineCount > MaxLines)
+ {
+ var lc = txtMsg.Document.LineCount;
+ var cutLineNumber = lc - KeepLines;
+ var cutLine = txtMsg.Document.GetLineByNumber(cutLineNumber);
+ txtMsg.Document.Remove(0, cutLine.Offset);
+ }
+
if (togScrollToEnd.IsChecked ?? true)
{
- _scrollViewer?.ScrollToEnd();
+ txtMsg.ScrollToEnd();
}
}
public void ClearMsg()
{
- ViewModel?.ClearMsg();
- txtMsg.Text = "";
+ txtMsg.Text = string.Empty;
+ txtMsg.AppendText("----- Message cleared -----\n");
}
private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e)
{
- txtMsg.Focus();
- txtMsg.SelectAll();
+ Dispatcher.UIThread.Post(() =>
+ {
+ txtMsg.TextArea.Focus();
+ txtMsg.SelectAll();
+ }, DispatcherPriority.Render);
}
private async void menuMsgViewCopy_Click(object? sender, RoutedEventArgs e)
diff --git a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj
index 8c274a69..f6ff5735 100644
--- a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj
+++ b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj
@@ -9,6 +9,7 @@
+
true
@@ -17,6 +18,7 @@
+
true
diff --git a/v2rayN/v2rayN/Views/MsgView.xaml.cs b/v2rayN/v2rayN/Views/MsgView.xaml.cs
index 3cc1fdfa..af8eb5f4 100644
--- a/v2rayN/v2rayN/Views/MsgView.xaml.cs
+++ b/v2rayN/v2rayN/Views/MsgView.xaml.cs
@@ -48,18 +48,25 @@ public partial class MsgView
private void ShowMsg(object msg)
{
txtMsg.BeginChange();
- txtMsg.Text = msg.ToString();
+
+ if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
+ {
+ ClearMsg();
+ }
+
+ txtMsg.AppendText(msg.ToString());
if (togScrollToEnd.IsChecked ?? true)
{
txtMsg.ScrollToEnd();
}
+
txtMsg.EndChange();
}
public void ClearMsg()
{
- ViewModel?.ClearMsg();
txtMsg.Clear();
+ txtMsg.AppendText("----- Message cleared -----\n");
}
private void menuMsgViewSelectAll_Click(object sender, System.Windows.RoutedEventArgs e)