Compare commits

..

48 Commits
7.0.0 ... 7.0.7

Author SHA1 Message Date
2dust
45febe3fff up 7.0.7
https://github.com/2dust/v2rayN/issues/5970
2024-11-06 15:00:45 +08:00
2dust
689a81a985 Built-in font for Desktop
https://github.com/2dust/v2rayN/issues/5970
2024-11-06 14:52:54 +08:00
2dust
28620b385a Update MsgView.xaml
https://github.com/2dust/v2rayN/issues/5997
2024-11-05 19:49:27 +08:00
2dust
be13446e69 Speed ​​test address maintenance 2024-11-04 14:21:11 +08:00
2dust
ee57d5b8e6 Improve checkup date 2024-11-04 14:15:59 +08:00
2dust
4eda3dd8fa Add Memo attribute to subscription group
https://github.com/2dust/v2rayN/issues/5981
2024-11-04 10:42:42 +08:00
2dust
911dc7f90e Revert
9fd20ff001
2024-11-04 10:37:44 +08:00
fonaix
18005b96e8 修复:双击表头弹出编辑窗口的问题 (#5984)
* 修复:日志文本框垂直显示上下空白问题

* 修复:双击表头弹出编辑窗口的问题
2024-11-04 09:44:03 +08:00
2dust
a3e45d206e Bug fix
https://github.com/2dust/v2rayN/issues/5979
2024-11-03 21:15:50 +08:00
2dust
0accf262dc up 7.0.6 2024-11-03 16:49:06 +08:00
2dust
e0eb73bb0a Code clean 2024-11-03 16:45:48 +08:00
2dust
258e822c13 Add exit function to the main interface for Desktop 2024-11-03 14:48:13 +08:00
2dust
4f05b93d63 Bug fix
MainModule FileName only exists in Win32.
2024-11-03 13:44:12 +08:00
2dust
bb661d4f50 Only one instance is allowed to run 2024-11-03 13:30:32 +08:00
2dust
201cfaa922 Fix
https://github.com/2dust/v2rayN/issues/5966
2024-11-02 15:02:30 +08:00
2dust
c339aa349c up PackageReference 2024-11-02 14:42:26 +08:00
2dust
046421f345 Code clean 2024-11-02 14:42:01 +08:00
fonaix
3a2c9e7aaa 修复:日志文本框垂直显示上下空白问题 (#5967) 2024-11-02 10:28:18 +08:00
2dust
0bb6e6bd86 up 7.0.5 2024-11-01 16:54:16 +08:00
2dust
693e8ab157 Add startup auto-start for desktop 2024-11-01 14:54:23 +08:00
2dust
033c16c992 Refactor to select active nodes when updating subscriptions 2024-11-01 10:19:31 +08:00
2dust
6ec61433fb Only update the stable version of the core
https://github.com/2dust/v2rayN/issues/5950
2024-11-01 09:26:15 +08:00
2dust
072f773245 up 7.0.4 2024-10-31 09:59:59 +08:00
2dust
0086f65a96 Bug fix
b7f4fd7469
2024-10-30 08:57:10 +08:00
2dust
281f14f47e Adjustment of preset rule sets 2024-10-28 13:45:39 +08:00
2dust
9fd20ff001 De-duplication after subscription update 2024-10-28 09:38:20 +08:00
2dust
7df90a6034 Bug fix
https://github.com/2dust/v2rayN/issues/5928
2024-10-28 09:16:24 +08:00
2dust
019869ec28 up 7.0.3 2024-10-27 15:01:14 +08:00
2dust
b7f4fd7469 Add font settings for Desktop 2024-10-27 14:59:06 +08:00
2dust
1273d2aee1 Improved font settings 2024-10-27 14:57:45 +08:00
2dust
88990b4828 Bug fix
0efb0b5e3e
2024-10-26 18:00:22 +08:00
2dust
2f02c2970c UseShellExecute = true 2024-10-26 17:55:36 +08:00
2dust
ade789c6d4 Update UpgradeApp.cs 2024-10-26 17:55:08 +08:00
2dust
9738f90970 up 7.0.2 2024-10-26 15:00:57 +08:00
2dust
6ed0741339 Bug fix
https://github.com/2dust/v2rayN/issues/5923
2024-10-26 14:57:28 +08:00
2dust
e6b1e22245 Update UpgradeApp.cs 2024-10-26 14:42:58 +08:00
2dust
6b922be0c6 Set AssemblyName to v2rayN 2024-10-26 10:42:29 +08:00
2dust
6e35a260e8 Bug fix
https://github.com/2dust/v2rayN/issues/5915
2024-10-26 09:55:22 +08:00
2dust
1106fd8cf1 Give upgrade app execute permission at runtime 2024-10-25 20:32:47 +08:00
2dust
fb92b90d5c Bug fix
https://github.com/2dust/v2rayN/issues/5909
2024-10-25 17:58:54 +08:00
2dust
5effbee50b Fix
https://github.com/2dust/v2rayN/issues/5905
2024-10-25 17:41:01 +08:00
2dust
9bc50a9f34 up 7.0.1 2024-10-25 13:50:17 +08:00
2dust
2a5a339c27 Update UpgradeApp.cs 2024-10-25 11:56:15 +08:00
2dust
78d182fff3 Add a warning about the use of insecure HTTP protocol subscription address 2024-10-25 11:06:04 +08:00
2dust
0efb0b5e3e Give core execute permission at runtime 2024-10-25 10:10:55 +08:00
2dust
a2e8755730 Xray Asset Location use XRAY_LOCATION_ASSET 2024-10-25 09:31:16 +08:00
2dust
06ddedbc4c The core folder is all lowercase letters 2024-10-25 09:27:11 +08:00
2dust
fa148cdf42 Bug fix 2024-10-25 09:01:59 +08:00
63 changed files with 996 additions and 740 deletions

View File

@@ -1,6 +1,5 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO.Compression; using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
namespace AmazTool namespace AmazTool
@@ -12,7 +11,7 @@ namespace AmazTool
Console.WriteLine(fileName); Console.WriteLine(fileName);
Console.WriteLine("In progress, please wait...(正在进行中,请等待)"); Console.WriteLine("In progress, please wait...(正在进行中,请等待)");
Thread.Sleep(5000); Thread.Sleep(9000);
if (!File.Exists(fileName)) if (!File.Exists(fileName))
{ {
@@ -20,17 +19,14 @@ namespace AmazTool
return; return;
} }
Console.WriteLine("Try to end the process(尝试结束进程).");
try try
{ {
Process[] existing = Process.GetProcessesByName(V2rayN()); var existing = Process.GetProcessesByName(V2rayN);
foreach (Process p in existing) foreach (var pp in existing)
{ {
var path = p.MainModule?.FileName ?? ""; pp?.Kill();
if (path.StartsWith(GetPath(V2rayN()))) pp?.WaitForExit(1000);
{
p.Kill();
p.WaitForExit(100);
}
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -40,6 +36,7 @@ namespace AmazTool
"Close it manually, or the upgrade may fail.(请手动关闭正在运行的v2rayN否则可能升级失败。\n\n" + ex.StackTrace); "Close it manually, or the upgrade may fail.(请手动关闭正在运行的v2rayN否则可能升级失败。\n\n" + ex.StackTrace);
} }
Console.WriteLine("Start extracting files(开始解压文件).");
StringBuilder sb = new(); StringBuilder sb = new();
try try
{ {
@@ -57,6 +54,8 @@ namespace AmazTool
continue; continue;
} }
Console.WriteLine(entry.FullName);
var lst = entry.FullName.Split(splitKey); var lst = entry.FullName.Split(splitKey);
if (lst.Length == 1) continue; if (lst.Length == 1) continue;
string fullName = string.Join(splitKey, lst[1..lst.Length]); string fullName = string.Join(splitKey, lst[1..lst.Length]);
@@ -81,22 +80,22 @@ namespace AmazTool
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("Upgrade Failed(升级失败)." + ex.StackTrace); Console.WriteLine("Upgrade Failed(升级失败)." + ex.StackTrace);
return; //return;
} }
if (sb.Length > 0) if (sb.Length > 0)
{ {
Console.WriteLine("Upgrade Failed.\n" + Console.WriteLine("Upgrade Failed(升级失败)." + sb.ToString());
"(升级失败)." + sb.ToString()); //return;
return;
} }
Console.WriteLine("Start v2rayN, please wait...(正在重启,请等待)"); Console.WriteLine("Start v2rayN, please wait...(正在重启,请等待)");
Thread.Sleep(3000); Thread.Sleep(9000);
Process process = new() Process process = new()
{ {
StartInfo = new() StartInfo = new()
{ {
FileName = V2rayN(), UseShellExecute = true,
FileName = V2rayN,
WorkingDirectory = StartupPath() WorkingDirectory = StartupPath()
} }
}; };
@@ -123,19 +122,6 @@ namespace AmazTool
return Path.Combine(startupPath, fileName); return Path.Combine(startupPath, fileName);
} }
private static string V2rayN() private static string V2rayN => "v2rayN";
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (File.Exists(GetPath("v2rayN.exe")))
return "v2rayN";
else
return "v2rayN.Desktop";
}
else
{
return "v2rayN.Desktop";
}
}
} }
} }

View File

@@ -37,40 +37,41 @@ namespace ServiceLib.Common
foreach (var filePath in files) foreach (var filePath in files)
{ {
var file = new FileInfo(filePath); var file = new FileInfo(filePath);
if (file.CreationTime < now) if (file.CreationTime >= now) continue;
try
{ {
try file.Delete();
{ }
file.Delete(); catch
} {
catch { } // ignored
} }
} }
} }
catch { } catch
{
// ignored
}
}); });
} }
public static void SaveLog(string strContent) public static void SaveLog(string strContent)
{ {
if (LogManager.IsLoggingEnabled()) if (!LogManager.IsLoggingEnabled()) return;
{
var logger = LogManager.GetLogger("Log1"); LogManager.GetLogger("Log1").Info(strContent);
logger.Info(strContent);
}
} }
public static void SaveLog(string strTitle, Exception ex) public static void SaveLog(string strTitle, Exception ex)
{ {
if (LogManager.IsLoggingEnabled()) if (!LogManager.IsLoggingEnabled()) return;
var logger = LogManager.GetLogger("Log2");
logger.Debug($"{strTitle},{ex.Message}");
logger.Debug(ex.StackTrace);
if (ex?.InnerException != null)
{ {
var logger = LogManager.GetLogger("Log2"); logger.Error(ex.InnerException);
logger.Debug($"{strTitle},{ex.Message}");
logger.Debug(ex.StackTrace);
if (ex?.InnerException != null)
{
logger.Error(ex.InnerException);
}
} }
} }
} }

View File

@@ -38,6 +38,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
} }
return result; return result;
} }
@@ -58,6 +59,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
} }
return null; return null;
} }
@@ -92,6 +94,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
} }
return string.Empty; return string.Empty;
} }
@@ -116,6 +119,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
} }
return null; return null;
} }
@@ -137,6 +141,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
} }
return null; return null;
} }
@@ -156,6 +161,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog("Base64Encode", ex); Logging.SaveLog("Base64Encode", ex);
} }
return string.Empty; return string.Empty;
} }
@@ -170,12 +176,12 @@ namespace ServiceLib.Common
{ {
if (plainText.IsNullOrEmpty()) return ""; if (plainText.IsNullOrEmpty()) return "";
plainText = plainText.Trim() plainText = plainText.Trim()
.Replace(Environment.NewLine, "") .Replace(Environment.NewLine, "")
.Replace("\n", "") .Replace("\n", "")
.Replace("\r", "") .Replace("\r", "")
.Replace('_', '/') .Replace('_', '/')
.Replace('-', '+') .Replace('-', '+')
.Replace(" ", ""); .Replace(" ", "");
if (plainText.Length % 4 > 0) if (plainText.Length % 4 > 0)
{ {
@@ -189,6 +195,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog("Base64Decode", ex); Logging.SaveLog("Base64Decode", ex);
} }
return string.Empty; return string.Empty;
} }
@@ -251,14 +258,17 @@ namespace ServiceLib.Common
unit = "TB"; unit = "TB";
return; return;
} }
result = GBs + ((MBs % factor) / (factor + 0.0)); result = GBs + ((MBs % factor) / (factor + 0.0));
unit = "GB"; unit = "GB";
return; return;
} }
result = MBs + ((KBs % factor) / (factor + 0.0)); result = MBs + ((KBs % factor) / (factor + 0.0));
unit = "MB"; unit = "MB";
return; return;
} }
result = KBs + ((amount % factor) / (factor + 0.0)); result = KBs + ((amount % factor) / (factor + 0.0));
unit = "KB"; unit = "KB";
return; return;
@@ -302,6 +312,7 @@ namespace ServiceLib.Common
{ {
continue; continue;
} }
var key = Uri.UnescapeDataString(keyValue[0]); var key = Uri.UnescapeDataString(keyValue[0]);
var val = Uri.UnescapeDataString(keyValue[1]); var val = Uri.UnescapeDataString(keyValue[1]);
@@ -323,6 +334,7 @@ namespace ServiceLib.Common
{ {
sb.Append(b.ToString("x2")); sb.Append(b.ToString("x2"));
} }
return sb.ToString(); return sb.ToString();
} }
@@ -337,6 +349,7 @@ namespace ServiceLib.Common
{ {
return url; return url;
} }
try try
{ {
Uri uri = new(url); Uri uri = new(url);
@@ -368,6 +381,7 @@ namespace ServiceLib.Common
{ {
return text; return text;
} }
return text.Replace("", ",").Replace(Environment.NewLine, ","); return text.Replace("", ",").Replace(Environment.NewLine, ",");
} }
@@ -391,6 +405,7 @@ namespace ServiceLib.Common
{ {
return true; return true;
} }
return text == "null"; return text == "null";
} }
@@ -424,6 +439,7 @@ namespace ServiceLib.Common
_ => false, _ => false,
}; };
} }
return false; return false;
} }
@@ -448,6 +464,7 @@ namespace ServiceLib.Common
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) return true; if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) return true;
if (ipBytes[0] == 192 && ipBytes[1] == 168) return true; if (ipBytes[0] == 192 && ipBytes[1] == 168) return true;
} }
return false; return false;
} }
@@ -468,6 +485,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
} }
return false; return false;
} }
@@ -489,6 +507,7 @@ namespace ServiceLib.Common
catch catch
{ {
} }
return 59090; return 59090;
} }
@@ -512,7 +531,8 @@ namespace ServiceLib.Common
{ {
if (blFull) if (blFull)
{ {
return $"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture} - {File.GetLastWriteTime(GetExePath()):yyyy/MM/dd}"; return
$"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture} - {File.GetLastWriteTime(GetExePath()):yyyy/MM/dd}";
} }
else else
{ {
@@ -523,6 +543,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
} }
return Global.AppName; return Global.AppName;
} }
@@ -560,6 +581,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
} }
return string.Empty; return string.Empty;
} }
@@ -572,7 +594,11 @@ namespace ServiceLib.Common
{ {
try try
{ {
if (fileName.IsNullOrEmpty()) { return; } if (fileName.IsNullOrEmpty())
{
return;
}
Process.Start(new ProcessStartInfo(fileName, arguments) { UseShellExecute = true }); Process.Start(new ProcessStartInfo(fileName, arguments) { UseShellExecute = true });
} }
catch (Exception ex) catch (Exception ex)
@@ -605,6 +631,7 @@ namespace ServiceLib.Common
{ {
Logging.SaveLog(ex.Message, ex); Logging.SaveLog(ex.Message, ex);
} }
return systemHosts; return systemHosts;
} }
@@ -629,17 +656,20 @@ namespace ServiceLib.Common
cmd = cmd.WithArguments(args); cmd = cmd.WithArguments(args);
} }
} }
var result = await cmd.ExecuteBufferedAsync(); var result = await cmd.ExecuteBufferedAsync();
if (result.IsSuccess) if (result.IsSuccess)
{ {
return result.StandardOutput.ToString(); return result.StandardOutput.ToString();
} }
Logging.SaveLog(result.ToString() ?? ""); Logging.SaveLog(result.ToString() ?? "");
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("GetCliWrapOutput", ex); Logging.SaveLog("GetCliWrapOutput", ex);
} }
return null; return null;
} }
@@ -654,6 +684,7 @@ namespace ServiceLib.Common
{ {
return startupPath; return startupPath;
} }
return Path.Combine(startupPath, fileName); return Path.Combine(startupPath, fileName);
} }
@@ -674,6 +705,7 @@ namespace ServiceLib.Common
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
if (IsNullOrEmpty(filename)) if (IsNullOrEmpty(filename))
{ {
return tempPath; return tempPath;
@@ -691,6 +723,7 @@ namespace ServiceLib.Common
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
return Path.Combine(tempPath, filename); return Path.Combine(tempPath, filename);
} }
@@ -701,6 +734,7 @@ namespace ServiceLib.Common
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
if (Utils.IsNullOrEmpty(filename)) if (Utils.IsNullOrEmpty(filename))
{ {
return tempPath; return tempPath;
@@ -718,14 +752,16 @@ namespace ServiceLib.Common
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
if (coreType != null) if (coreType != null)
{ {
tempPath = Path.Combine(tempPath, coreType.ToString()); tempPath = Path.Combine(tempPath, coreType.ToLower().ToString());
if (!Directory.Exists(tempPath)) if (!Directory.Exists(tempPath))
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
} }
if (IsNullOrEmpty(filename)) if (IsNullOrEmpty(filename))
{ {
return tempPath; return tempPath;
@@ -743,6 +779,7 @@ namespace ServiceLib.Common
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
if (Utils.IsNullOrEmpty(filename)) if (Utils.IsNullOrEmpty(filename))
{ {
return tempPath; return tempPath;
@@ -760,6 +797,7 @@ namespace ServiceLib.Common
{ {
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
} }
if (Utils.IsNullOrEmpty(filename)) if (Utils.IsNullOrEmpty(filename))
{ {
return tempPath; return tempPath;
@@ -818,6 +856,20 @@ namespace ServiceLib.Common
return await GetCliWrapOutput("/bin/bash", arg); return await GetCliWrapOutput("/bin/bash", arg);
} }
public static async Task<string?> GetLinuxFontFamily(string lang)
{
// var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" };
var arg = new List<string>() { "-c", $"fc-list : family" };
return await GetCliWrapOutput("/bin/bash", arg);
}
public static string? GetHomePath()
{
return IsWindows()
? Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%")
: Environment.GetEnvironmentVariable("HOME");
}
#endregion Platform #endregion Platform
} }
} }

View File

@@ -0,0 +1,52 @@
using Microsoft.Win32;
namespace ServiceLib.Common
{
internal static class WindowsUtils
{
public static string? RegReadValue(string path, string name, string def)
{
RegistryKey? regKey = null;
try
{
regKey = Registry.CurrentUser.OpenSubKey(path, false);
var value = regKey?.GetValue(name) as string;
return Utils.IsNullOrEmpty(value) ? def : value;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
finally
{
regKey?.Close();
}
return def;
}
public static void RegWriteValue(string path, string name, object value)
{
RegistryKey? regKey = null;
try
{
regKey = Registry.CurrentUser.CreateSubKey(path);
if (Utils.IsNullOrEmpty(value.ToString()))
{
regKey?.DeleteValue(name, false);
}
else
{
regKey?.SetValue(name, value);
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
finally
{
regKey?.Close();
}
}
}
}

View File

@@ -5,6 +5,7 @@
ClearMsg, ClearMsg,
SendMsgView, SendMsgView,
SendSnackMsg, SendSnackMsg,
RefreshProfiles RefreshProfiles,
StopSpeedtest
} }
} }

View File

@@ -19,6 +19,7 @@
Shutdown, Shutdown,
BrowseServer, BrowseServer,
ImportRulesFromFile, ImportRulesFromFile,
InitSettingFont,
SubEditWindow, SubEditWindow,
RoutingRuleSettingWindow, RoutingRuleSettingWindow,
RoutingRuleDetailsWindow, RoutingRuleDetailsWindow,

View File

@@ -28,21 +28,24 @@
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json"; public const string CoreSpeedtestConfigFileName = "configSpeedtest.json";
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json"; public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
public const string ClashMixinConfigFileName = "Mixin.yaml"; public const string ClashMixinConfigFileName = "Mixin.yaml";
public const string V2raySampleClient = "ServiceLib.Sample.SampleClientConfig";
public const string SingboxSampleClient = "ServiceLib.Sample.SingboxSampleClientConfig"; public const string NamespaceSample = "ServiceLib.Sample.";
public const string V2raySampleHttpRequestFileName = "ServiceLib.Sample.SampleHttpRequest"; public const string V2raySampleClient = NamespaceSample + "SampleClientConfig";
public const string V2raySampleHttpResponseFileName = "ServiceLib.Sample.SampleHttpResponse"; public const string SingboxSampleClient = NamespaceSample + "SingboxSampleClientConfig";
public const string V2raySampleInbound = "ServiceLib.Sample.SampleInbound"; public const string V2raySampleHttpRequestFileName = NamespaceSample + "SampleHttpRequest";
public const string V2raySampleOutbound = "ServiceLib.Sample.SampleOutbound"; public const string V2raySampleHttpResponseFileName = NamespaceSample + "SampleHttpResponse";
public const string SingboxSampleOutbound = "ServiceLib.Sample.SingboxSampleOutbound"; public const string V2raySampleInbound = NamespaceSample + "SampleInbound";
public const string CustomRoutingFileName = "ServiceLib.Sample.custom_routing_"; public const string V2raySampleOutbound = NamespaceSample + "SampleOutbound";
public const string TunSingboxDNSFileName = "ServiceLib.Sample.tun_singbox_dns"; public const string SingboxSampleOutbound = NamespaceSample + "SingboxSampleOutbound";
public const string TunSingboxInboundFileName = "ServiceLib.Sample.tun_singbox_inbound"; public const string CustomRoutingFileName = NamespaceSample + "custom_routing_";
public const string TunSingboxRulesFileName = "ServiceLib.Sample.tun_singbox_rules"; public const string TunSingboxDNSFileName = NamespaceSample + "tun_singbox_dns";
public const string DNSV2rayNormalFileName = "ServiceLib.Sample.dns_v2ray_normal"; public const string TunSingboxInboundFileName = NamespaceSample + "tun_singbox_inbound";
public const string DNSSingboxNormalFileName = "ServiceLib.Sample.dns_singbox_normal"; public const string TunSingboxRulesFileName = NamespaceSample + "tun_singbox_rules";
public const string ClashMixinYaml = "ServiceLib.Sample.clash_mixin_yaml"; public const string DNSV2rayNormalFileName = NamespaceSample + "dns_v2ray_normal";
public const string ClashTunYaml = "ServiceLib.Sample.clash_tun_yaml"; public const string DNSSingboxNormalFileName = NamespaceSample + "dns_singbox_normal";
public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml";
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
public const string DefaultSecurity = "auto"; public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp"; public const string DefaultNetwork = "tcp";
@@ -102,9 +105,9 @@
public static readonly List<string> SpeedTestUrls = new() { public static readonly List<string> SpeedTestUrls = new() {
@"https://speed.cloudflare.com/__down?bytes=100000000", @"https://speed.cloudflare.com/__down?bytes=100000000",
@"https://speed.cloudflare.com/__down?bytes=50000000",
@"https://speed.cloudflare.com/__down?bytes=10000000", @"https://speed.cloudflare.com/__down?bytes=10000000",
@"http://cachefly.cachefly.net/50mb.test", @"https://cachefly.cachefly.net/50mb.test",
@"http://cachefly.cachefly.net/10mb.test"
}; };
public static readonly List<string> SpeedPingTestUrls = new() { public static readonly List<string> SpeedPingTestUrls = new() {

View File

@@ -71,7 +71,7 @@
public bool InitComponents() public bool InitComponents()
{ {
Logging.Setup(); Logging.Setup();
Logging.LoggingEnabled(true); Logging.LoggingEnabled(_config.GuiItem.EnableLog);
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}"); Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}"); Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
Logging.ClearLogs(); Logging.ClearLogs();
@@ -159,7 +159,7 @@
await ConfigHandler.SetDefaultServer(_config, lstModel); await ConfigHandler.SetDefaultServer(_config, lstModel);
var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? []; var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
var lstProfileExs = ProfileExHandler.Instance.ProfileExs; var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
lstModel = (from t in lstModel lstModel = (from t in lstModel
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
from t22 in t2b.DefaultIfEmpty() from t22 in t2b.DefaultIfEmpty()

View File

@@ -0,0 +1,155 @@
using System.Security.Principal;
using System.Text.RegularExpressions;
namespace ServiceLib.Handler
{
public static class AutoStartupHandler
{
public static async Task<bool> UpdateTask(Config config)
{
if (Utils.IsWindows())
{
await ClearTaskWindows();
if (config.GuiItem.AutoRun)
{
await SetTaskWindows();
}
}
else if (Utils.IsLinux())
{
await ClearTaskLinux();
if (config.GuiItem.AutoRun)
{
await SetTaskLinux();
}
}
return true;
}
#region Windows
private static async Task ClearTaskWindows()
{
var autoRunName = GetAutoRunNameWindows();
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, "");
if (Utils.IsAdministrator())
{
AutoStartTaskService(autoRunName, "", "");
}
}
private static async Task SetTaskWindows()
{
try
{
var autoRunName = GetAutoRunNameWindows();
var exePath = Utils.GetExePath();
if (Utils.IsAdministrator())
{
AutoStartTaskService(autoRunName, exePath, "");
}
else
{
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath.AppendQuotes());
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
/// <summary>
/// Auto Start via TaskService
/// </summary>
/// <param name="taskName"></param>
/// <param name="fileName"></param>
/// <param name="description"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void AutoStartTaskService(string taskName, string fileName, string description)
{
if (Utils.IsNullOrEmpty(taskName))
{
return;
}
var logonUser = WindowsIdentity.GetCurrent().Name;
using var taskService = new Microsoft.Win32.TaskScheduler.TaskService();
var tasks = taskService.RootFolder.GetTasks(new Regex(taskName));
if (Utils.IsNullOrEmpty(fileName))
{
foreach (var t in tasks)
{
taskService.RootFolder.DeleteTask(t.Name);
}
return;
}
var task = taskService.NewTask();
task.RegistrationInfo.Description = description;
task.Settings.DisallowStartIfOnBatteries = false;
task.Settings.StopIfGoingOnBatteries = false;
task.Settings.RunOnlyIfIdle = false;
task.Settings.IdleSettings.StopOnIdleEnd = false;
task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) });
task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest;
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
taskService.RootFolder.RegisterTaskDefinition(taskName, task);
}
private static string GetAutoRunNameWindows()
{
return $"{Global.AutoRunName}_{Utils.GetMd5(Utils.StartupPath())}";
}
#endregion Windows
#region Linux
private static async Task ClearTaskLinux()
{
try
{
File.Delete(GetHomePathLinux());
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
private static async Task SetTaskLinux()
{
try
{
var linuxConfig = Utils.GetEmbedText(Global.LinuxAutostartConfig);
if (linuxConfig.IsNotEmpty())
{
linuxConfig = linuxConfig.Replace("$ExecPath$", Utils.GetExePath());
Logging.SaveLog(linuxConfig);
var homePath = GetHomePathLinux();
await File.WriteAllTextAsync(homePath, linuxConfig);
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
private static string GetHomePathLinux()
{
var homePath = Path.Combine(Utils.GetHomePath(), ".config", "autostart", $"{Global.AppName}.desktop");
Directory.CreateDirectory(Path.GetDirectoryName(homePath));
return homePath;
}
#endregion Linux
}
}

View File

@@ -28,7 +28,7 @@ namespace ServiceLib.Handler
return new Tuple<ClashProxies, ClashProviders>(clashProxies, clashProviders); return new Tuple<ClashProxies, ClashProviders>(clashProxies, clashProviders);
} }
await Task.Delay(5000); await Task.Delay(2000);
} }
return null; return null;

View File

@@ -162,6 +162,7 @@ namespace ServiceLib.Handler
config.ClashUIItem ??= new(); config.ClashUIItem ??= new();
config.SystemProxyItem ??= new(); config.SystemProxyItem ??= new();
config.WebDavItem ??= new(); config.WebDavItem ??= new();
config.CheckUpdateItem ??= new();
return config; return config;
} }
@@ -746,7 +747,7 @@ namespace ServiceLib.Handler
{ {
return -1; return -1;
} }
var lstProfileExs = ProfileExHandler.Instance.ProfileExs; var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
var lstProfile = (from t in lstModel var lstProfile = (from t in lstModel
join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b
from t33 in t3b.DefaultIfEmpty() from t33 in t3b.DefaultIfEmpty()
@@ -1034,14 +1035,14 @@ namespace ServiceLib.Handler
/// <param name="strData"></param> /// <param name="strData"></param>
/// <param name="subid"></param> /// <param name="subid"></param>
/// <returns>成功导入的数量</returns> /// <returns>成功导入的数量</returns>
private static async Task<int> AddBatchServers(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub) private static async Task<int> AddBatchServersCommon(Config config, string strData, string subid, bool isSub)
{ {
if (Utils.IsNullOrEmpty(strData)) if (Utils.IsNullOrEmpty(strData))
{ {
return -1; return -1;
} }
string subFilter = string.Empty; var subFilter = string.Empty;
//remove sub items //remove sub items
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && Utils.IsNotEmpty(subid))
{ {
@@ -1049,16 +1050,14 @@ namespace ServiceLib.Handler
subFilter = (await AppHandler.Instance.GetSubItem(subid))?.Filter ?? ""; subFilter = (await AppHandler.Instance.GetSubItem(subid))?.Filter ?? "";
} }
int countServers = 0; var countServers = 0;
//Check for duplicate indexId
List<string>? lstDbIndexId = null;
List<ProfileItem> lstAdd = new(); List<ProfileItem> lstAdd = new();
var arrData = strData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty()); var arrData = strData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty());
if (isSub) if (isSub)
{ {
arrData = arrData.Distinct(); arrData = arrData.Distinct();
} }
foreach (string str in arrData) foreach (var str in arrData)
{ {
//maybe sub //maybe sub
if (!isSub && (str.StartsWith(Global.HttpsProtocol) || str.StartsWith(Global.HttpProtocol))) if (!isSub && (str.StartsWith(Global.HttpsProtocol) || str.StartsWith(Global.HttpProtocol)))
@@ -1075,35 +1074,12 @@ namespace ServiceLib.Handler
continue; continue;
} }
//exist sub items //exist sub items //filter
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && Utils.IsNotEmpty(subid) && Utils.IsNotEmpty(subFilter))
{ {
var existItem = lstOriSub?.FirstOrDefault(t => t.IsSub == isSub if (!Regex.IsMatch(profileItem.Remarks, subFilter))
&& config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == profileItem.Remarks : CompareProfileItem(t, profileItem, true));
if (existItem != null)
{ {
//Check for duplicate indexId continue;
if (lstDbIndexId is null)
{
lstDbIndexId = await AppHandler.Instance.ProfileItemIndexes("");
}
if (lstAdd.Any(t => t.IndexId == existItem.IndexId)
|| lstDbIndexId.Any(t => t == existItem.IndexId))
{
profileItem.IndexId = string.Empty;
}
else
{
profileItem.IndexId = existItem.IndexId;
}
}
//filter
if (Utils.IsNotEmpty(subFilter))
{
if (!Regex.IsMatch(profileItem.Remarks, subFilter))
{
continue;
}
} }
} }
profileItem.Subid = subid; profileItem.Subid = subid;
@@ -1138,7 +1114,7 @@ namespace ServiceLib.Handler
return countServers; return countServers;
} }
private static async Task<int> AddBatchServers4Custom(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub) private static async Task<int> AddBatchServers4Custom(Config config, string strData, string subid, bool isSub)
{ {
if (Utils.IsNullOrEmpty(strData)) if (Utils.IsNullOrEmpty(strData))
{ {
@@ -1222,10 +1198,7 @@ namespace ServiceLib.Handler
{ {
await RemoveServerViaSubid(config, subid, isSub); await RemoveServerViaSubid(config, subid, isSub);
} }
if (isSub && lstOriSub?.Count == 1)
{
profileItem.IndexId = lstOriSub[0].IndexId;
}
profileItem.Subid = subid; profileItem.Subid = subid;
profileItem.IsSub = isSub; profileItem.IsSub = isSub;
profileItem.PreSocksPort = preSocksPort; profileItem.PreSocksPort = preSocksPort;
@@ -1239,7 +1212,7 @@ namespace ServiceLib.Handler
} }
} }
private static async Task<int> AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub, List<ProfileItem> lstOriSub) private static async Task<int> AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub)
{ {
if (Utils.IsNullOrEmpty(strData)) if (Utils.IsNullOrEmpty(strData))
{ {
@@ -1278,34 +1251,47 @@ namespace ServiceLib.Handler
return -1; return -1;
} }
List<ProfileItem>? lstOriSub = null; List<ProfileItem>? lstOriSub = null;
ProfileItem? activeProfile = null;
if (isSub && Utils.IsNotEmpty(subid)) if (isSub && Utils.IsNotEmpty(subid))
{ {
lstOriSub = await AppHandler.Instance.ProfileItems(subid); lstOriSub = await AppHandler.Instance.ProfileItems(subid);
activeProfile = lstOriSub?.FirstOrDefault(t => t.IndexId == config.IndexId);
} }
var counter = 0; var counter = 0;
if (Utils.IsBase64String(strData)) if (Utils.IsBase64String(strData))
{ {
counter = await AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub); counter = await AddBatchServersCommon(config, Utils.Base64Decode(strData), subid, isSub);
} }
if (counter < 1) if (counter < 1)
{ {
counter = await AddBatchServers(config, strData, subid, isSub, lstOriSub); counter = await AddBatchServersCommon(config, strData, subid, isSub);
} }
if (counter < 1) if (counter < 1)
{ {
counter = await AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub); counter = await AddBatchServersCommon(config, Utils.Base64Decode(strData), subid, isSub);
} }
if (counter < 1) if (counter < 1)
{ {
counter = await AddBatchServers4SsSIP008(config, strData, subid, isSub, lstOriSub); counter = await AddBatchServers4SsSIP008(config, strData, subid, isSub);
} }
//maybe other sub //maybe other sub
if (counter < 1) if (counter < 1)
{ {
counter = await AddBatchServers4Custom(config, strData, subid, isSub, lstOriSub); counter = await AddBatchServers4Custom(config, strData, subid, isSub);
}
//Select active node
if (activeProfile != null)
{
var lstSub = await AppHandler.Instance.ProfileItems(subid);
var existItem = lstSub?.FirstOrDefault(t => config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == activeProfile.Remarks : CompareProfileItem(t, activeProfile, true));
if (existItem != null)
{
await ConfigHandler.SetDefaultServerIndex(config, existItem.IndexId);
}
} }
return counter; return counter;
@@ -1340,7 +1326,9 @@ namespace ServiceLib.Handler
//Do not allow http protocol //Do not allow http protocol
if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost)) if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost))
{ {
return -1; //TODO Temporary reminder to be removed later
NoticeHandler.Instance.Enqueue(ResUI.InsecureUrlProtocol);
//return -1;
} }
var queryVars = Utils.ParseQueryString(uri.Query); var queryVars = Utils.ParseQueryString(uri.Query);
@@ -1371,6 +1359,7 @@ namespace ServiceLib.Handler
item.PrevProfile = subItem.PrevProfile; item.PrevProfile = subItem.PrevProfile;
item.NextProfile = subItem.NextProfile; item.NextProfile = subItem.NextProfile;
item.PreSocksPort = subItem.PreSocksPort; item.PreSocksPort = subItem.PreSocksPort;
item.Memo = subItem.Memo;
} }
if (Utils.IsNullOrEmpty(item.Id)) if (Utils.IsNullOrEmpty(item.Id))

View File

@@ -12,18 +12,12 @@
if (node.ConfigType == EConfigType.Custom) if (node.ConfigType == EConfigType.Custom)
{ {
if (node.CoreType is ECoreType.mihomo) result = node.CoreType switch
{ {
result = await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName); ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
} ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName),
if (node.CoreType is ECoreType.sing_box) _ => await GenerateClientCustomConfig(node, fileName)
{ };
result = await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName);
}
else
{
result = await GenerateClientCustomConfig(node, fileName);
}
} }
else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
{ {

View File

@@ -15,13 +15,38 @@ namespace ServiceLib.Handler
private Process? _processPre; private Process? _processPre;
private Action<bool, string>? _updateFunc; private Action<bool, string>? _updateFunc;
public void Init(Config config, Action<bool, string> updateFunc) public async Task Init(Config config, Action<bool, string> updateFunc)
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
Environment.SetEnvironmentVariable("v2ray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
if (Utils.IsLinux())
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo)
{
if (it.CoreType == ECoreType.v2rayN)
{
if (Utils.UpgradeAppExists(out var fileName))
{
await Utils.SetLinuxChmod(fileName);
}
continue;
}
foreach (var name in it.CoreExes)
{
var exe = Utils.GetBinPath(Utils.GetExeName(name), it.CoreType.ToString());
if (File.Exists(exe))
{
await Utils.SetLinuxChmod(exe);
}
}
}
}
} }
public async Task LoadCore(ProfileItem? node) public async Task LoadCore(ProfileItem? node)
@@ -83,13 +108,11 @@ namespace ServiceLib.Handler
{ {
try try
{ {
bool hasProc = false;
if (_process != null) if (_process != null)
{ {
await KillProcess(_process); await KillProcess(_process);
_process.Dispose(); _process.Dispose();
_process = null; _process = null;
hasProc = true;
} }
if (_processPre != null) if (_processPre != null)
@@ -97,31 +120,6 @@ namespace ServiceLib.Handler
await KillProcess(_processPre); await KillProcess(_processPre);
_processPre.Dispose(); _processPre.Dispose();
_processPre = null; _processPre = null;
hasProc = true;
}
if (!hasProc)
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo)
{
if (it.CoreType == ECoreType.v2rayN)
{
continue;
}
foreach (string vName in it.CoreExes)
{
var existing = Process.GetProcessesByName(vName);
foreach (Process p in existing)
{
string? path = p.MainModule?.FileName;
if (path == Utils.GetExeName(Utils.GetBinPath(vName, it.CoreType.ToString())))
{
await KillProcess(p);
}
}
}
}
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -148,10 +146,9 @@ namespace ServiceLib.Handler
private string CoreFindExe(CoreInfo coreInfo) private string CoreFindExe(CoreInfo coreInfo)
{ {
string fileName = string.Empty; string fileName = string.Empty;
foreach (string name in coreInfo.CoreExes) foreach (var name in coreInfo.CoreExes)
{ {
string vName = Utils.GetExeName(name); var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
vName = Utils.GetBinPath(vName, coreInfo.CoreType.ToString());
if (File.Exists(vName)) if (File.Exists(vName))
{ {
fileName = vName; fileName = vName;

View File

@@ -9,27 +9,31 @@ namespace ServiceLib.Handler
private static readonly Lazy<ProfileExHandler> _instance = new(() => new()); private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
private ConcurrentBag<ProfileExItem> _lstProfileEx = []; private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
private Queue<string> _queIndexIds = new(); private Queue<string> _queIndexIds = new();
public ConcurrentBag<ProfileExItem> ProfileExs => _lstProfileEx;
public static ProfileExHandler Instance => _instance.Value; public static ProfileExHandler Instance => _instance.Value;
public ProfileExHandler() public ProfileExHandler()
{ {
Init(); //Init();
} }
private async Task Init() public async Task Init()
{ {
await InitData(); await InitData();
await Task.Run(async () => Task.Run(async () =>
{ {
while (true) while (true)
{ {
await SaveQueueIndexIds();
await Task.Delay(1000 * 600); await Task.Delay(1000 * 600);
await SaveQueueIndexIds();
} }
}); });
} }
public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs()
{
return _lstProfileEx;
}
private async Task InitData() private async Task InitData()
{ {
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )"); await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");

View File

@@ -11,24 +11,24 @@ namespace ServiceLib.Handler.SysProxy
{ {
if (type == 1) if (type == 1)
{ {
RegWriteValue(_regPath, "ProxyEnable", 0); WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
RegWriteValue(_regPath, "ProxyServer", string.Empty); WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
RegWriteValue(_regPath, "ProxyOverride", string.Empty); WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
RegWriteValue(_regPath, "AutoConfigURL", string.Empty); WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
} }
if (type == 2) if (type == 2)
{ {
RegWriteValue(_regPath, "ProxyEnable", 1); WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 1);
RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty); WindowsUtils.RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty);
RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty); WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty);
RegWriteValue(_regPath, "AutoConfigURL", string.Empty); WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
} }
else if (type == 4) else if (type == 4)
{ {
RegWriteValue(_regPath, "ProxyEnable", 0); WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
RegWriteValue(_regPath, "ProxyServer", string.Empty); WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
RegWriteValue(_regPath, "ProxyOverride", string.Empty); WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty); WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty);
} }
return true; return true;
} }
@@ -356,30 +356,5 @@ namespace ServiceLib.Handler.SysProxy
ref int lpcEntries // Number of entries written to the buffer ref int lpcEntries // Number of entries written to the buffer
); );
} }
private static void RegWriteValue(string path, string name, object value)
{
Microsoft.Win32.RegistryKey? regKey = null;
try
{
regKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(path);
if (string.IsNullOrEmpty(value.ToString()))
{
regKey?.DeleteValue(name, false);
}
else
{
regKey?.SetValue(name, value);
}
}
catch (Exception ex)
{
//Logging.SaveLog(ex.Message, ex);
}
finally
{
regKey?.Close();
}
}
} }
} }

View File

@@ -1,6 +1,6 @@
namespace ServiceLib.Models namespace ServiceLib.Models
{ {
public class CheckUpdateItem public class CheckUpdateModel
{ {
public bool? IsSelected { get; set; } public bool? IsSelected { get; set; }
public string? CoreType { get; set; } public string? CoreType { get; set; }

View File

@@ -15,15 +15,14 @@
public bool IsRunningCore(ECoreType type) public bool IsRunningCore(ECoreType type)
{ {
if (type == ECoreType.Xray && RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5) switch (type)
{ {
return true; case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5:
case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo:
return true;
default:
return false;
} }
if (type == ECoreType.sing_box && RunningCoreType is ECoreType.sing_box or ECoreType.mihomo)
{
return true;
}
return false;
} }
#endregion property #endregion property
@@ -46,6 +45,7 @@
public ClashUIItem ClashUIItem { get; set; } public ClashUIItem ClashUIItem { get; set; }
public SystemProxyItem SystemProxyItem { get; set; } public SystemProxyItem SystemProxyItem { get; set; }
public WebDavItem WebDavItem { get; set; } public WebDavItem WebDavItem { get; set; }
public CheckUpdateItem CheckUpdateItem { get; set; }
public List<InItem> Inbound { get; set; } public List<InItem> Inbound { get; set; }
public List<KeyEventItem> GlobalHotkeys { get; set; } public List<KeyEventItem> GlobalHotkeys { get; set; }
public List<CoreTypeItem> CoreTypeItem { get; set; } public List<CoreTypeItem> CoreTypeItem { get; set; }

View File

@@ -80,14 +80,14 @@
public bool IgnoreGeoUpdateCore { get; set; } = true; public bool IgnoreGeoUpdateCore { get; set; } = true;
public int AutoUpdateInterval { get; set; } public int AutoUpdateInterval { get; set; }
public bool CheckPreReleaseUpdate { get; set; } = false;
public bool EnableSecurityProtocolTls13 { get; set; } public bool EnableSecurityProtocolTls13 { get; set; }
public int TrayMenuServersLimit { get; set; } = 20; public int TrayMenuServersLimit { get; set; } = 20;
public bool EnableHWA { get; set; } = false; public bool EnableHWA { get; set; } = false;
public bool EnableLog { get; set; } = true;
} }
[Serializable] [Serializable]
@@ -243,4 +243,11 @@
public string? Password { get; set; } public string? Password { get; set; }
public string? DirName { get; set; } public string? DirName { get; set; }
} }
[Serializable]
public class CheckUpdateItem
{
public bool CheckPreReleaseUpdate { get; set; }
public List<string>? SelectedCoreTypes { get; set; }
}
} }

View File

@@ -33,5 +33,7 @@ namespace ServiceLib.Models
public string? NextProfile { get; set; } public string? NextProfile { get; set; }
public int? PreSocksPort { get; set; } public int? PreSocksPort { get; set; }
public string? Memo { get; set; }
} }
} }

View File

@@ -294,6 +294,24 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Please do not use the insecure HTTP protocol subscription address 的本地化字符串。
/// </summary>
public static string InsecureUrlProtocol {
get {
return ResourceManager.GetString("InsecureUrlProtocol", resourceCulture);
}
}
/// <summary>
/// 查找类似 Invalid address (Url) 的本地化字符串。
/// </summary>
public static string InvalidUrlTip {
get {
return ResourceManager.GetString("InvalidUrlTip", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 {0} {1} already up to date. 的本地化字符串。 /// 查找类似 {0} {1} already up to date. 的本地化字符串。
/// </summary> /// </summary>
@@ -429,6 +447,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Remarks Memo 的本地化字符串。
/// </summary>
public static string LvMemo {
get {
return ResourceManager.GetString("LvMemo", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 More URLs, separated by commas; Subscription conversion will be invalid 的本地化字符串。 /// 查找类似 More URLs, separated by commas; Subscription conversion will be invalid 的本地化字符串。
/// </summary> /// </summary>
@@ -852,6 +879,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Are you sure to exit? 的本地化字符串。
/// </summary>
public static string menuExitTips {
get {
return ResourceManager.GetString("menuExitTips", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Export selected server for complete configuration 的本地化字符串。 /// 查找类似 Export selected server for complete configuration 的本地化字符串。
/// </summary> /// </summary>
@@ -2842,6 +2878,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Install the font to the system and restart the settings 的本地化字符串。
/// </summary>
public static string TbSettingsCurrentFontFamilyLinuxTip {
get {
return ResourceManager.GetString("TbSettingsCurrentFontFamilyLinuxTip", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Copy the font TTF/TTC file to the directory guiFonts, restart the settings 的本地化字符串。 /// 查找类似 Copy the font TTF/TTC file to the directory guiFonts, restart the settings 的本地化字符串。
/// </summary> /// </summary>

View File

@@ -1354,4 +1354,19 @@
<data name="menuAddServerViaImage" xml:space="preserve"> <data name="menuAddServerViaImage" xml:space="preserve">
<value>Scan QR code in the image</value> <value>Scan QR code in the image</value>
</data> </data>
<data name="InvalidUrlTip" xml:space="preserve">
<value>Invalid address (Url)</value>
</data>
<data name="InsecureUrlProtocol" xml:space="preserve">
<value>Please do not use the insecure HTTP protocol subscription address</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>Install the font to the system and restart the settings</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>Are you sure to exit?</value>
</data>
<data name="LvMemo" xml:space="preserve">
<value>Remarks Memo</value>
</data>
</root> </root>

View File

@@ -1351,4 +1351,19 @@
<data name="menuAddServerViaImage" xml:space="preserve"> <data name="menuAddServerViaImage" xml:space="preserve">
<value>扫描图片中的二维码</value> <value>扫描图片中的二维码</value>
</data> </data>
<data name="InvalidUrlTip" xml:space="preserve">
<value>地址(Url)无效</value>
</data>
<data name="InsecureUrlProtocol" xml:space="preserve">
<value>请不要使用不安全的HTTP协议订阅地址</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安装字体到系统中,重启设置</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>是否确定退出?</value>
</data>
<data name="LvMemo" xml:space="preserve">
<value>备注备忘</value>
</data>
</root> </root>

View File

@@ -1231,4 +1231,19 @@
<data name="menuAddServerViaImage" xml:space="preserve"> <data name="menuAddServerViaImage" xml:space="preserve">
<value>掃描圖片中的二維碼</value> <value>掃描圖片中的二維碼</value>
</data> </data>
<data name="InvalidUrlTip" xml:space="preserve">
<value>地址(Url)無效</value>
</data>
<data name="InsecureUrlProtocol" xml:space="preserve">
<value>請不要使用不安全的HTTP協定訂閱位址</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安裝字體到系統中,重新啟動設定</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>是否確定退出?</value>
</data>
<data name="LvMemo" xml:space="preserve">
<value>備註備忘</value>
</data>
</root> </root>

View File

@@ -42,15 +42,7 @@
] ]
}, },
{ {
"remarks": "代理GFW", "remarks": "代理IP",
"outboundTag": "proxy",
"domain": [
"geosite:gfw",
"geosite:greatfire"
]
},
{
"remarks": "代理Google等",
"outboundTag": "proxy", "outboundTag": "proxy",
"ip": [ "ip": [
"1.0.0.1", "1.0.0.1",
@@ -65,6 +57,14 @@
"geoip:twitter" "geoip:twitter"
] ]
}, },
{
"remarks": "代理GFW",
"outboundTag": "proxy",
"domain": [
"geosite:gfw",
"geosite:greatfire"
]
},
{ {
"remarks": "最终直连", "remarks": "最终直连",
"port": "0-65535", "port": "0-65535",

View File

@@ -34,19 +34,6 @@
"geosite:private" "geosite:private"
] ]
}, },
{
"remarks": "绕过中国域名",
"outboundTag": "direct",
"domain": [
"domain:dns.alidns.com",
"domain:doh.pub",
"domain:dot.pub",
"domain:doh.360.cn",
"domain:dot.360.cn",
"geosite:cn",
"geosite:geolocation-cn"
]
},
{ {
"remarks": "绕过中国IP", "remarks": "绕过中国IP",
"outboundTag": "direct", "outboundTag": "direct",
@@ -73,6 +60,19 @@
"geoip:cn" "geoip:cn"
] ]
}, },
{
"remarks": "绕过中国域名",
"outboundTag": "direct",
"domain": [
"domain:dns.alidns.com",
"domain:doh.pub",
"domain:dot.pub",
"domain:doh.360.cn",
"domain:dot.360.cn",
"geosite:cn",
"geosite:geolocation-cn"
]
},
{ {
"remarks": "最终代理", "remarks": "最终代理",
"port": "0-65535", "port": "0-65535",

View File

@@ -0,0 +1,10 @@
[Desktop Entry]
Type=Application
Exec=$ExecPath$
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name[en_US]=v2rayN
Name=v2rayN
Comment[en_US]=v2rayN
Comment=v2rayN

View File

@@ -4,9 +4,9 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Version>7.0.0</Version> <Version>7.0.7</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Downloader" Version="3.2.1" /> <PackageReference Include="Downloader" Version="3.2.1" />
<PackageReference Include="ReactiveUI" Version="20.1.63" /> <PackageReference Include="ReactiveUI" Version="20.1.63" />
@@ -16,9 +16,11 @@
<PackageReference Include="WebDav.Client" Version="2.8.0" /> <PackageReference Include="WebDav.Client" Version="2.8.0" />
<PackageReference Include="YamlDotNet" Version="16.1.3" /> <PackageReference Include="YamlDotNet" Version="16.1.3" />
<PackageReference Include="QRCoder" Version="1.6.0" /> <PackageReference Include="QRCoder" Version="1.6.0" />
<PackageReference Include="CliWrap" Version="3.6.6" /> <PackageReference Include="CliWrap" Version="3.6.7" />
<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" /> <PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
<PackageReference Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" /> <PackageReference Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
<PackageReference Include="TaskScheduler" Version="2.11.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -41,6 +43,7 @@
<EmbeddedResource Include="Sample\tun_singbox_dns" /> <EmbeddedResource Include="Sample\tun_singbox_dns" />
<EmbeddedResource Include="Sample\tun_singbox_inbound" /> <EmbeddedResource Include="Sample\tun_singbox_inbound" />
<EmbeddedResource Include="Sample\tun_singbox_rules" /> <EmbeddedResource Include="Sample\tun_singbox_rules" />
<EmbeddedResource Include="Sample\linux_autostart_config" />
</ItemGroup> </ItemGroup>

View File

@@ -1,4 +1,5 @@
using System.Diagnostics; using ReactiveUI;
using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
@@ -80,10 +81,12 @@ namespace ServiceLib.Services
Task.Run(RunMixedtestAsync); Task.Run(RunMixedtestAsync);
break; break;
} }
MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop);
} }
public void ExitLoop() private void ExitLoop(string x)
{ {
if (_exitLoop) return;
_exitLoop = true; _exitLoop = true;
UpdateFunc("", ResUI.SpeedtestingStop); UpdateFunc("", ResUI.SpeedtestingStop);
} }

View File

@@ -6,12 +6,10 @@ namespace ServiceLib.Services
public class UpdateService public class UpdateService
{ {
private Action<bool, string>? _updateFunc; private Action<bool, string>? _updateFunc;
private Config _config;
private int _timeout = 30; private int _timeout = 30;
public async Task CheckUpdateGuiN(Config config, Action<bool, string> updateFunc, bool preRelease) public async Task CheckUpdateGuiN(Config config, Action<bool, string> updateFunc, bool preRelease)
{ {
_config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
var url = string.Empty; var url = string.Empty;
var fileName = string.Empty; var fileName = string.Empty;
@@ -53,7 +51,6 @@ namespace ServiceLib.Services
public async Task CheckUpdateCore(ECoreType type, Config config, Action<bool, string> updateFunc, bool preRelease) public async Task CheckUpdateCore(ECoreType type, Config config, Action<bool, string> updateFunc, bool preRelease)
{ {
_config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
var url = string.Empty; var url = string.Empty;
var fileName = string.Empty; var fileName = string.Empty;
@@ -108,13 +105,12 @@ namespace ServiceLib.Services
public async Task UpdateSubscriptionProcess(Config config, string subId, bool blProxy, Action<bool, string> updateFunc) public async Task UpdateSubscriptionProcess(Config config, string subId, bool blProxy, Action<bool, string> updateFunc)
{ {
_config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
_updateFunc?.Invoke(false, ResUI.MsgUpdateSubscriptionStart); _updateFunc?.Invoke(false, ResUI.MsgUpdateSubscriptionStart);
var subItem = await AppHandler.Instance.SubItems(); var subItem = await AppHandler.Instance.SubItems();
if (subItem == null || subItem.Count <= 0) if (subItem is not { Count: > 0 })
{ {
_updateFunc?.Invoke(false, ResUI.MsgNoValidSubscription); _updateFunc?.Invoke(false, ResUI.MsgNoValidSubscription);
return; return;
@@ -122,10 +118,10 @@ namespace ServiceLib.Services
foreach (var item in subItem) foreach (var item in subItem)
{ {
string id = item.Id.TrimEx(); var id = item.Id.TrimEx();
string url = item.Url.TrimEx(); var url = item.Url.TrimEx();
string userAgent = item.UserAgent.TrimEx(); var userAgent = item.UserAgent.TrimEx();
string hashCode = $"{item.Remarks}->"; var hashCode = $"{item.Remarks}->";
if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || Utils.IsNotEmpty(subId) && item.Id != subId) if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || Utils.IsNotEmpty(subId) && item.Id != subId)
{ {
//_updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgNoValidSubscription}"); //_updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgNoValidSubscription}");
@@ -219,7 +215,7 @@ namespace ServiceLib.Services
_updateFunc?.Invoke(false, $"{hashCode}{result}"); _updateFunc?.Invoke(false, $"{hashCode}{result}");
} }
int ret = await ConfigHandler.AddBatchServers(config, result, id, true); var ret = await ConfigHandler.AddBatchServers(config, result, id, true);
if (ret <= 0) if (ret <= 0)
{ {
Logging.SaveLog("FailedImportSubscription"); Logging.SaveLog("FailedImportSubscription");
@@ -231,6 +227,8 @@ namespace ServiceLib.Services
: $"{hashCode}{ResUI.MsgFailedImportSubscription}"); : $"{hashCode}{ResUI.MsgFailedImportSubscription}");
} }
_updateFunc?.Invoke(false, "-------------------------------------------------------"); _updateFunc?.Invoke(false, "-------------------------------------------------------");
//await ConfigHandler.DedupServerList(config, id);
} }
_updateFunc?.Invoke(true, $"{ResUI.MsgUpdateSubscriptionEnd}"); _updateFunc?.Invoke(true, $"{ResUI.MsgUpdateSubscriptionEnd}");
@@ -309,10 +307,9 @@ namespace ServiceLib.Services
{ {
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type);
string filePath = string.Empty; string filePath = string.Empty;
foreach (string name in coreInfo.CoreExes) foreach (var name in coreInfo.CoreExes)
{ {
string vName = Utils.GetExeName(name); var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
vName = Utils.GetBinPath(vName, coreInfo.CoreType.ToString());
if (File.Exists(vName)) if (File.Exists(vName))
{ {
filePath = vName; filePath = vName;
@@ -453,7 +450,6 @@ namespace ServiceLib.Services
private async Task UpdateGeoFile(string geoName, Config config, Action<bool, string> updateFunc) private async Task UpdateGeoFile(string geoName, Config config, Action<bool, string> updateFunc)
{ {
_config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
var geoUrl = string.IsNullOrEmpty(config?.ConstItem.GeoSourceUrl) var geoUrl = string.IsNullOrEmpty(config?.ConstItem.GeoSourceUrl)
@@ -469,7 +465,6 @@ namespace ServiceLib.Services
private async Task UpdateSrsFileAll(Config config, Action<bool, string> updateFunc) private async Task UpdateSrsFileAll(Config config, Action<bool, string> updateFunc)
{ {
_config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
var geoipFiles = new List<string>(); var geoipFiles = new List<string>();
@@ -520,9 +515,9 @@ namespace ServiceLib.Services
private async Task UpdateSrsFile(string type, string srsName, Config config, Action<bool, string> updateFunc) private async Task UpdateSrsFile(string type, string srsName, Config config, Action<bool, string> updateFunc)
{ {
var srsUrl = string.IsNullOrEmpty(_config.ConstItem.SrsSourceUrl) var srsUrl = string.IsNullOrEmpty(config.ConstItem.SrsSourceUrl)
? Global.SingboxRulesetUrl ? Global.SingboxRulesetUrl
: _config.ConstItem.SrsSourceUrl; : config.ConstItem.SrsSourceUrl;
var fileName = $"{type}-{srsName}.srs"; var fileName = $"{type}-{srsName}.srs";
var targetPath = Path.Combine(Utils.GetBinPath("srss"), fileName); var targetPath = Path.Combine(Utils.GetBinPath("srss"), fileName);

View File

@@ -12,10 +12,10 @@ namespace ServiceLib.ViewModels
{ {
private const string _geo = "GeoFiles"; private const string _geo = "GeoFiles";
private string _v2rayN = ECoreType.v2rayN.ToString(); private string _v2rayN = ECoreType.v2rayN.ToString();
private List<CheckUpdateItem> _lstUpdated = []; private List<CheckUpdateModel> _lstUpdated = [];
private IObservableCollection<CheckUpdateItem> _checkUpdateItem = new ObservableCollectionExtended<CheckUpdateItem>(); private IObservableCollection<CheckUpdateModel> _checkUpdateModel = new ObservableCollectionExtended<CheckUpdateModel>();
public IObservableCollection<CheckUpdateItem> CheckUpdateItems => _checkUpdateItem; public IObservableCollection<CheckUpdateModel> CheckUpdateModels => _checkUpdateModel;
public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; } public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; }
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; } [Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
@@ -29,65 +29,56 @@ namespace ServiceLib.ViewModels
await CheckUpdate(); await CheckUpdate();
}); });
EnableCheckPreReleaseUpdate = _config.GuiItem.CheckPreReleaseUpdate; EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate;
this.WhenAnyValue( this.WhenAnyValue(
x => x.EnableCheckPreReleaseUpdate, x => x.EnableCheckPreReleaseUpdate,
y => y == true) y => y == true)
.Subscribe(c => { _config.GuiItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate; }); .Subscribe(c => { _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate; });
RefreshSubItems(); RefreshCheckUpdateItems();
} }
private void RefreshSubItems() private void RefreshCheckUpdateItems()
{ {
_checkUpdateItem.Clear(); _checkUpdateModel.Clear();
if (RuntimeInformation.ProcessArchitecture != Architecture.X86) if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
{ {
_checkUpdateItem.Add(new CheckUpdateItem() _checkUpdateModel.Add(GetCheckUpdateModel(_v2rayN));
{ _checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.Xray.ToString()));
IsSelected = false, _checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString()));
CoreType = _v2rayN, _checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString()));
Remarks = ResUI.menuCheckUpdate,
});
_checkUpdateItem.Add(new CheckUpdateItem()
{
IsSelected = true,
CoreType = ECoreType.Xray.ToString(),
Remarks = ResUI.menuCheckUpdate,
});
_checkUpdateItem.Add(new CheckUpdateItem()
{
IsSelected = true,
CoreType = ECoreType.mihomo.ToString(),
Remarks = ResUI.menuCheckUpdate,
});
_checkUpdateItem.Add(new CheckUpdateItem()
{
IsSelected = true,
CoreType = ECoreType.sing_box.ToString(),
Remarks = ResUI.menuCheckUpdate,
});
} }
_checkUpdateModel.Add(GetCheckUpdateModel(_geo));
}
_checkUpdateItem.Add(new CheckUpdateItem() private CheckUpdateModel GetCheckUpdateModel(string coreType)
{
return new()
{ {
IsSelected = true, IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true,
CoreType = _geo, CoreType = coreType,
Remarks = ResUI.menuCheckUpdate, Remarks = ResUI.menuCheckUpdate,
}); };
}
private async Task SaveSelectedCoreTypes()
{
_config.CheckUpdateItem.SelectedCoreTypes = _checkUpdateModel.Where(t => t.IsSelected == true).Select(t => t.CoreType ?? "").ToList();
await ConfigHandler.SaveConfig(_config);
} }
private async Task CheckUpdate() private async Task CheckUpdate()
{ {
_lstUpdated.Clear(); _lstUpdated.Clear();
_lstUpdated = _checkUpdateItem.Where(x => x.IsSelected == true) _lstUpdated = _checkUpdateModel.Where(x => x.IsSelected == true)
.Select(x => new CheckUpdateItem() { CoreType = x.CoreType }).ToList(); .Select(x => new CheckUpdateModel() { CoreType = x.CoreType }).ToList();
await SaveSelectedCoreTypes();
for (var k = _checkUpdateItem.Count - 1; k >= 0; k--) for (var k = _checkUpdateModel.Count - 1; k >= 0; k--)
{ {
var item = _checkUpdateItem[k]; var item = _checkUpdateModel[k];
if (item.IsSelected != true) continue; if (item.IsSelected != true) continue;
UpdateView(item.CoreType, "..."); UpdateView(item.CoreType, "...");
@@ -99,13 +90,13 @@ namespace ServiceLib.ViewModels
{ {
await CheckUpdateN(EnableCheckPreReleaseUpdate); await CheckUpdateN(EnableCheckPreReleaseUpdate);
} }
else if (item.CoreType == ECoreType.mihomo.ToString()) else if (item.CoreType == ECoreType.Xray.ToString())
{ {
await CheckUpdateCore(item, false); await CheckUpdateCore(item, EnableCheckPreReleaseUpdate);
} }
else else
{ {
await CheckUpdateCore(item, EnableCheckPreReleaseUpdate); await CheckUpdateCore(item, false);
} }
} }
@@ -161,23 +152,23 @@ namespace ServiceLib.ViewModels
}); });
} }
private async Task CheckUpdateCore(CheckUpdateItem item, bool preRelease) private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease)
{ {
void _updateUI(bool success, string msg) void _updateUI(bool success, string msg)
{ {
UpdateView(item.CoreType, msg); UpdateView(model.CoreType, msg);
if (success) if (success)
{ {
UpdateView(item.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfullyMore); UpdateView(model.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfullyMore);
UpdatedPlusPlus(item.CoreType, msg); UpdatedPlusPlus(model.CoreType, msg);
} }
} }
var type = (ECoreType)Enum.Parse(typeof(ECoreType), item.CoreType); var type = (ECoreType)Enum.Parse(typeof(ECoreType), model.CoreType);
await (new UpdateService()).CheckUpdateCore(type, _config, _updateUI, preRelease) await (new UpdateService()).CheckUpdateCore(type, _config, _updateUI, preRelease)
.ContinueWith(t => .ContinueWith(t =>
{ {
UpdatedPlusPlus(item.CoreType, ""); UpdatedPlusPlus(model.CoreType, "");
}); });
} }
@@ -291,7 +282,7 @@ namespace ServiceLib.ViewModels
private void UpdateView(string coreType, string msg) private void UpdateView(string coreType, string msg)
{ {
var item = new CheckUpdateItem() var item = new CheckUpdateModel()
{ {
CoreType = coreType, CoreType = coreType,
Remarks = msg, Remarks = msg,
@@ -299,13 +290,13 @@ namespace ServiceLib.ViewModels
_updateView?.Invoke(EViewAction.DispatcherCheckUpdate, item); _updateView?.Invoke(EViewAction.DispatcherCheckUpdate, item);
} }
public void UpdateViewResult(CheckUpdateItem item) public void UpdateViewResult(CheckUpdateModel model)
{ {
var found = _checkUpdateItem.FirstOrDefault(t => t.CoreType == item.CoreType); var found = _checkUpdateModel.FirstOrDefault(t => t.CoreType == model.CoreType);
if (found == null) return; if (found == null) return;
var itemCopy = JsonUtils.DeepCopy(found); var itemCopy = JsonUtils.DeepCopy(found);
itemCopy.Remarks = item.Remarks; itemCopy.Remarks = model.Remarks;
_checkUpdateItem.Replace(found, itemCopy); _checkUpdateModel.Replace(found, itemCopy);
} }
} }
} }

View File

@@ -96,7 +96,7 @@ namespace ServiceLib.ViewModels
private async Task Init() private async Task Init()
{ {
await ProxiesReload(); await ProxiesReload();
await DelayTestTask(); DelayTestTask();
} }
private async Task DoRulemodeSelected(bool c) private async Task DoRulemodeSelected(bool c)
@@ -434,25 +434,29 @@ namespace ServiceLib.ViewModels
public async Task DelayTestTask() public async Task DelayTestTask()
{ {
var lastTime = DateTime.Now; var lastTime = DateTime.Now;
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000 * 60);
Observable.Interval(TimeSpan.FromSeconds(60)) if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
.Subscribe(async x => {
{ continue;
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box))) }
{ if (_config.ClashUIItem.ProxiesAutoDelayTestInterval <= 0)
return; {
} continue;
var dtNow = DateTime.Now; }
if (_config.ClashUIItem.ProxiesAutoDelayTestInterval > 0) var dtNow = DateTime.Now;
{ if ((dtNow - lastTime).Minutes % _config.ClashUIItem.ProxiesAutoDelayTestInterval != 0)
if ((dtNow - lastTime).Minutes % _config.ClashUIItem.ProxiesAutoDelayTestInterval == 0) {
{ continue;
await ProxiesDelayTest(); }
lastTime = dtNow; await ProxiesDelayTest();
} lastTime = dtNow;
Task.Delay(1000).Wait(); }
} });
});
} }
#endregion task #endregion task

View File

@@ -52,6 +52,8 @@ namespace ServiceLib.ViewModels
public ReactiveCommand<Unit, Unit> ReloadCmd { get; } public ReactiveCommand<Unit, Unit> ReloadCmd { get; }
public ReactiveCommand<Unit, Unit> ExitCmd { get; }
[Reactive] [Reactive]
public bool BlReloadEnabled { get; set; } public bool BlReloadEnabled { get; set; }
@@ -187,6 +189,11 @@ namespace ServiceLib.ViewModels
await Reload(); await Reload();
}); });
ExitCmd = ReactiveCommand.CreateFromTask(async () =>
{
await Exit();
});
RegionalPresetDefaultCmd = ReactiveCommand.CreateFromTask(async () => RegionalPresetDefaultCmd = ReactiveCommand.CreateFromTask(async () =>
{ {
await ApplyRegionalPreset(EPresetType.Default); await ApplyRegionalPreset(EPresetType.Default);
@@ -208,7 +215,8 @@ namespace ServiceLib.ViewModels
await ConfigHandler.InitBuiltinRouting(_config); await ConfigHandler.InitBuiltinRouting(_config);
await ConfigHandler.InitBuiltinDNS(_config); await ConfigHandler.InitBuiltinDNS(_config);
CoreHandler.Instance.Init(_config, UpdateHandler); await ProfileExHandler.Instance.Init();
await CoreHandler.Instance.Init(_config, UpdateHandler);
TaskHandler.Instance.RegUpdateTask(_config, UpdateTaskHandler); TaskHandler.Instance.RegUpdateTask(_config, UpdateTaskHandler);
if (_config.GuiItem.EnableStatistics) if (_config.GuiItem.EnableStatistics)
@@ -265,7 +273,7 @@ namespace ServiceLib.ViewModels
try try
{ {
Locator.Current.GetService<StatusBarViewModel>()?.UpdateStatistics(update); Locator.Current.GetService<StatusBarViewModel>()?.UpdateStatistics(update);
if ((update.ProxyUp + update.ProxyDown) > 0 && DateTime.Now.Second % 3 == 0) if ((update.ProxyUp + update.ProxyDown) > 0 && DateTime.Now.Second % 9 == 0)
{ {
Locator.Current.GetService<ProfilesViewModel>()?.UpdateStatistics(update); Locator.Current.GetService<ProfilesViewModel>()?.UpdateStatistics(update);
} }
@@ -288,7 +296,7 @@ namespace ServiceLib.ViewModels
await ProfileExHandler.Instance.SaveTo(); await ProfileExHandler.Instance.SaveTo();
await StatisticsHandler.Instance.SaveTo(); await StatisticsHandler.Instance.SaveTo();
StatisticsHandler.Instance.Close(); StatisticsHandler.Instance.Close();
CoreHandler.Instance.CoreStop(); await CoreHandler.Instance.CoreStop();
Logging.SaveLog("MyAppExit End"); Logging.SaveLog("MyAppExit End");
} }
@@ -312,6 +320,7 @@ namespace ServiceLib.ViewModels
{ {
StartInfo = new ProcessStartInfo StartInfo = new ProcessStartInfo
{ {
UseShellExecute = true,
FileName = fileName, FileName = fileName,
Arguments = arg.AppendQuotes(), Arguments = arg.AppendQuotes(),
WorkingDirectory = Utils.StartupPath() WorkingDirectory = Utils.StartupPath()
@@ -389,6 +398,10 @@ namespace ServiceLib.ViewModels
RefreshServers(); RefreshServers();
NoticeHandler.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret)); NoticeHandler.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret));
} }
else
{
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
}
} }
public async Task AddServerViaScanAsync() public async Task AddServerViaScanAsync()
@@ -433,6 +446,10 @@ namespace ServiceLib.ViewModels
RefreshServers(); RefreshServers();
NoticeHandler.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan); NoticeHandler.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan);
} }
else
{
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
}
} }
} }
@@ -578,6 +595,16 @@ namespace ServiceLib.ViewModels
} }
} }
private async Task Exit()
{
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
{
return;
}
await MyAppExitAsync(false);
}
#endregion core job #endregion core job
#region Presets #region Presets

View File

@@ -53,7 +53,6 @@ namespace ServiceLib.ViewModels
[Reactive] public bool EnableUpdateSubOnlyRemarksExist { get; set; } [Reactive] public bool EnableUpdateSubOnlyRemarksExist { get; set; }
[Reactive] public bool EnableSecurityProtocolTls13 { get; set; } [Reactive] public bool EnableSecurityProtocolTls13 { get; set; }
[Reactive] public bool AutoHideStartup { get; set; } [Reactive] public bool AutoHideStartup { get; set; }
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
[Reactive] public bool EnableDragDropSort { get; set; } [Reactive] public bool EnableDragDropSort { get; set; }
[Reactive] public bool DoubleClick2Activate { get; set; } [Reactive] public bool DoubleClick2Activate { get; set; }
[Reactive] public int AutoUpdateInterval { get; set; } [Reactive] public int AutoUpdateInterval { get; set; }
@@ -117,6 +116,8 @@ namespace ServiceLib.ViewModels
private async Task Init() private async Task Init()
{ {
await _updateView?.Invoke(EViewAction.InitSettingFont, null);
#region Core #region Core
var inbound = _config.Inbound[0]; var inbound = _config.Inbound[0];
@@ -164,7 +165,6 @@ namespace ServiceLib.ViewModels
EnableUpdateSubOnlyRemarksExist = _config.UiItem.EnableUpdateSubOnlyRemarksExist; EnableUpdateSubOnlyRemarksExist = _config.UiItem.EnableUpdateSubOnlyRemarksExist;
EnableSecurityProtocolTls13 = _config.GuiItem.EnableSecurityProtocolTls13; EnableSecurityProtocolTls13 = _config.GuiItem.EnableSecurityProtocolTls13;
AutoHideStartup = _config.UiItem.AutoHideStartup; AutoHideStartup = _config.UiItem.AutoHideStartup;
EnableCheckPreReleaseUpdate = _config.GuiItem.CheckPreReleaseUpdate;
EnableDragDropSort = _config.UiItem.EnableDragDropSort; EnableDragDropSort = _config.UiItem.EnableDragDropSort;
DoubleClick2Activate = _config.UiItem.DoubleClick2Activate; DoubleClick2Activate = _config.UiItem.DoubleClick2Activate;
AutoUpdateInterval = _config.GuiItem.AutoUpdateInterval; AutoUpdateInterval = _config.GuiItem.AutoUpdateInterval;
@@ -315,7 +315,6 @@ namespace ServiceLib.ViewModels
_config.GuiItem.EnableSecurityProtocolTls13 = EnableSecurityProtocolTls13; _config.GuiItem.EnableSecurityProtocolTls13 = EnableSecurityProtocolTls13;
_config.UiItem.AutoHideStartup = AutoHideStartup; _config.UiItem.AutoHideStartup = AutoHideStartup;
_config.GuiItem.AutoUpdateInterval = AutoUpdateInterval; _config.GuiItem.AutoUpdateInterval = AutoUpdateInterval;
_config.GuiItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate;
_config.UiItem.EnableDragDropSort = EnableDragDropSort; _config.UiItem.EnableDragDropSort = EnableDragDropSort;
_config.UiItem.DoubleClick2Activate = DoubleClick2Activate; _config.UiItem.DoubleClick2Activate = DoubleClick2Activate;
_config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit; _config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit;
@@ -347,14 +346,9 @@ namespace ServiceLib.ViewModels
if (await ConfigHandler.SaveConfig(_config) == 0) if (await ConfigHandler.SaveConfig(_config) == 0)
{ {
if (needReboot) await AutoStartupHandler.UpdateTask(_config);
{
NoticeHandler.Instance.Enqueue(ResUI.NeedRebootTips); NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
}
else
{
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
}
_updateView?.Invoke(EViewAction.CloseWindow, null); _updateView?.Invoke(EViewAction.CloseWindow, null);
} }
else else

View File

@@ -16,7 +16,6 @@ namespace ServiceLib.ViewModels
private List<ProfileItem> _lstProfile; private List<ProfileItem> _lstProfile;
private string _serverFilter = string.Empty; private string _serverFilter = string.Empty;
private Dictionary<string, bool> _dicHeaderSort = new(); private Dictionary<string, bool> _dicHeaderSort = new();
private SpeedtestService? _speedtestHandler;
#endregion private prop #endregion private prop
@@ -686,12 +685,12 @@ namespace ServiceLib.ViewModels
} }
//ClearTestResult(); //ClearTestResult();
_speedtestHandler = new SpeedtestService(_config, lstSelecteds, actionType, UpdateSpeedtestHandler); _ = new SpeedtestService(_config, lstSelecteds, actionType, UpdateSpeedtestHandler);
} }
public void ServerSpeedtestStop() public void ServerSpeedtestStop()
{ {
_speedtestHandler?.ExitLoop(); MessageBus.Current.SendMessage("", EMsgCommand.StopSpeedtest.ToString());
} }
private async Task Export2ClientConfigAsync(bool blClipboard) private async Task Export2ClientConfigAsync(bool blClipboard)

View File

@@ -176,13 +176,14 @@ namespace ServiceLib.ViewModels
return; return;
} }
var lst = new List<RulesItem4Ray>(); var lst = new List<RulesItem>();
foreach (var it in SelectedSources ?? [SelectedSource]) foreach (var it in SelectedSources ?? [SelectedSource])
{ {
var item = _rules.FirstOrDefault(t => t.Id == it?.Id); var item = _rules.FirstOrDefault(t => t.Id == it?.Id);
if (item != null) if (item != null)
{ {
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item)); var item2 = JsonUtils.DeepCopy(item); //JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
item2.Id = null;
lst.Add(item2 ?? new()); lst.Add(item2 ?? new());
} }
} }

View File

@@ -104,6 +104,18 @@ namespace ServiceLib.ViewModels
public StatusBarViewModel(Func<EViewAction, object?, Task<bool>>? updateView) public StatusBarViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{ {
_config = AppHandler.Instance.Config; _config = AppHandler.Instance.Config;
SelectedRouting = new();
SelectedServer = new();
RunningServerToolTipText = "-";
if (_config.TunModeItem.EnableTun && AppHandler.Instance.IsAdministrator)
{
EnableTun = true;
}
else
{
_config.TunModeItem.EnableTun = EnableTun = false;
}
#region WhenAnyValue && ReactiveCommand #region WhenAnyValue && ReactiveCommand
@@ -179,19 +191,6 @@ namespace ServiceLib.ViewModels
private async Task Init() private async Task Init()
{ {
SelectedRouting = new();
SelectedServer = new();
RunningServerToolTipText = "-";
if (_config.TunModeItem.EnableTun && AppHandler.Instance.IsAdministrator)
{
EnableTun = true;
}
else
{
_config.TunModeItem.EnableTun = EnableTun = false;
}
await RefreshRoutingsMenu(); await RefreshRoutingsMenu();
await InboundDisplayStatus(); await InboundDisplayStatus();
await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true); await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true);

View File

@@ -39,14 +39,14 @@ namespace ServiceLib.ViewModels
var uri = Utils.TryUri(url); var uri = Utils.TryUri(url);
if (uri == null) if (uri == null)
{ {
NoticeHandler.Instance.Enqueue(ResUI.LvUrl); NoticeHandler.Instance.Enqueue(ResUI.InvalidUrlTip);
return; return;
} }
//Do not allow http protocol //Do not allow http protocol
if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost)) if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost))
{ {
NoticeHandler.Instance.Enqueue(ResUI.LvUrl); NoticeHandler.Instance.Enqueue(ResUI.InsecureUrlProtocol);
return; //return;
} }
} }

View File

@@ -9,8 +9,6 @@ namespace v2rayN.Desktop;
public partial class App : Application public partial class App : Application
{ {
//public static EventWaitHandle ProgramStarted;
public override void Initialize() public override void Initialize()
{ {
if (!AppHandler.Instance.InitApp()) if (!AppHandler.Instance.InitApp())
@@ -32,7 +30,7 @@ public partial class App : Application
{ {
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
OnStartup(desktop.Args); AppHandler.Instance.InitComponents();
desktop.Exit += OnExit; desktop.Exit += OnExit;
desktop.MainWindow = new MainWindow(); desktop.MainWindow = new MainWindow();
@@ -41,22 +39,6 @@ public partial class App : Application
base.OnFrameworkInitializationCompleted(); base.OnFrameworkInitializationCompleted();
} }
private void OnStartup(string[]? Args)
{
var exePathKey = Utils.GetMd5(Utils.GetExePath());
var rebootas = (Args ?? new string[] { }).Any(t => t == Global.RebootAs);
//ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out bool bCreatedNew);
//if (!rebootas && !bCreatedNew)
//{
// ProgramStarted.Set();
// Environment.Exit(0);
// return;
//}
AppHandler.Instance.InitComponents();
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{ {
if (e.ExceptionObject != null) if (e.ExceptionObject != null)

View File

@@ -0,0 +1,19 @@
using Avalonia;
using Avalonia.Media;
using System.Reflection;
namespace v2rayN.Desktop.Common
{
public static class AppBuilderExtension
{
public static AppBuilder WithFontByDefault(this AppBuilder appBuilder)
{
var uri = $"avares://{Assembly.GetExecutingAssembly().GetName().Name}/Assets/Fonts#Noto Sans SC";
return appBuilder.With(new FontManagerOptions()
{
DefaultFamilyName = uri,
FontFallbacks = new[] { new FontFallback { FontFamily = new FontFamily(uri) } }
});
}
}
}

View File

@@ -1,22 +1,56 @@
using Avalonia; using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop; namespace v2rayN.Desktop;
internal class Program internal class Program
{ {
public static EventWaitHandle ProgramStarted;
// Initialization code. Don't use any Avalonia, third-party APIs or any // Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break. // yet and stuff might break.
[STAThread] [STAThread]
public static void Main(string[] args) => BuildAvaloniaApp() public static void Main(string[] args)
.StartWithClassicDesktopLifetime(args); {
OnStartup(args);
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
private static void OnStartup(string[]? Args)
{
if (Utils.IsWindows())
{
var exePathKey = Utils.GetMd5(Utils.GetExePath());
var rebootas = (Args ?? new string[] { }).Any(t => t == Global.RebootAs);
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out bool bCreatedNew);
if (!rebootas && !bCreatedNew)
{
ProgramStarted.Set();
Environment.Exit(0);
return;
}
}
else
{
_ = new Mutex(true, "v2rayN", out var bOnlyOneInstance);
if (!bOnlyOneInstance)
{
Environment.Exit(0);
return;
}
}
}
// Avalonia configuration, don't remove; also used by visual designer. // Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>() => AppBuilder.Configure<App>()
.UsePlatformDetect() .UsePlatformDetect()
.WithInterFont() //.WithInterFont()
.WithFontByDefault()
.LogToTrace() .LogToTrace()
.UseReactiveUI(); .UseReactiveUI();
} }

View File

@@ -1,5 +1,8 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
@@ -9,14 +12,11 @@ namespace v2rayN.Desktop.ViewModels
{ {
public class ThemeSettingViewModel : MyReactiveObject public class ThemeSettingViewModel : MyReactiveObject
{ {
[Reactive] [Reactive] public bool ColorModeDark { get; set; }
public bool ColorModeDark { get; set; }
[Reactive] [Reactive] public int CurrentFontSize { get; set; }
public int CurrentFontSize { get; set; }
[Reactive] [Reactive] public string CurrentLanguage { get; set; }
public string CurrentLanguage { get; set; }
public ThemeSettingViewModel() public ThemeSettingViewModel()
{ {
@@ -29,6 +29,7 @@ namespace v2rayN.Desktop.ViewModels
private void RestoreUI() private void RestoreUI()
{ {
ModifyTheme(_config.UiItem.ColorModeDark); ModifyTheme(_config.UiItem.ColorModeDark);
ModifyFontFamily();
} }
private void BindingUI() private void BindingUI()
@@ -38,34 +39,34 @@ namespace v2rayN.Desktop.ViewModels
CurrentLanguage = _config.UiItem.CurrentLanguage; CurrentLanguage = _config.UiItem.CurrentLanguage;
this.WhenAnyValue(x => x.ColorModeDark) this.WhenAnyValue(x => x.ColorModeDark)
.Subscribe(c => .Subscribe(c =>
{ {
if (_config.UiItem.ColorModeDark != ColorModeDark) if (_config.UiItem.ColorModeDark != ColorModeDark)
{ {
_config.UiItem.ColorModeDark = ColorModeDark; _config.UiItem.ColorModeDark = ColorModeDark;
ModifyTheme(ColorModeDark); ModifyTheme(ColorModeDark);
ConfigHandler.SaveConfig(_config); ConfigHandler.SaveConfig(_config);
} }
}); });
this.WhenAnyValue( this.WhenAnyValue(
x => x.CurrentFontSize, x => x.CurrentFontSize,
y => y > 0) y => y > 0)
.Subscribe(c => .Subscribe(c =>
{ {
if (CurrentFontSize >= Global.MinFontSize) if (CurrentFontSize >= Global.MinFontSize)
{ {
_config.UiItem.CurrentFontSize = CurrentFontSize; _config.UiItem.CurrentFontSize = CurrentFontSize;
double size = CurrentFontSize; double size = CurrentFontSize;
ModifyFontSize(size); ModifyFontSize(size);
ConfigHandler.SaveConfig(_config); ConfigHandler.SaveConfig(_config);
} }
}); });
this.WhenAnyValue( this.WhenAnyValue(
x => x.CurrentLanguage, x => x.CurrentLanguage,
y => y != null && !y.IsNullOrEmpty()) y => y != null && !y.IsNullOrEmpty())
.Subscribe(c => .Subscribe(c =>
{ {
if (Utils.IsNotEmpty(CurrentLanguage) && _config.UiItem.CurrentLanguage != CurrentLanguage) if (Utils.IsNotEmpty(CurrentLanguage) && _config.UiItem.CurrentLanguage != CurrentLanguage)
@@ -89,53 +90,54 @@ namespace v2rayN.Desktop.ViewModels
private void ModifyFontSize(double size) private void ModifyFontSize(double size)
{ {
Style buttonStyle = new(x => x.OfType<Button>()); Style style = new(x => Selectors.Or(
buttonStyle.Add(new Setter() x.OfType<Button>(),
x.OfType<TextBox>(),
x.OfType<TextBlock>(),
x.OfType<Menu>(),
x.OfType<ContextMenu>(),
x.OfType<DataGridRow>(),
x.OfType<ListBoxItem>()
));
style.Add(new Setter()
{ {
Property = Button.FontSizeProperty, Property = TemplatedControl.FontSizeProperty,
Value = size, Value = size,
}); });
Application.Current?.Styles.Add(buttonStyle); Application.Current?.Styles.Add(style);
}
Style textStyle = new(x => x.OfType<TextBox>()); private void ModifyFontFamily()
textStyle.Add(new Setter() {
var currentFontFamily = _config.UiItem.CurrentFontFamily;
if (currentFontFamily.IsNullOrEmpty())
{ {
Property = TextBox.FontSizeProperty, return;
Value = size, }
});
Application.Current?.Styles.Add(textStyle);
Style textBlockStyle = new(x => x.OfType<TextBlock>()); try
textBlockStyle.Add(new Setter()
{ {
Property = TextBlock.FontSizeProperty, Style style = new(x => Selectors.Or(
Value = size, x.OfType<Button>(),
}); x.OfType<TextBox>(),
Application.Current?.Styles.Add(textBlockStyle); x.OfType<TextBlock>(),
x.OfType<Menu>(),
Style menuStyle = new(x => x.OfType<Menu>()); x.OfType<ContextMenu>(),
menuStyle.Add(new Setter() x.OfType<DataGridRow>(),
x.OfType<ListBoxItem>(),
x.OfType<WindowNotificationManager>()
));
style.Add(new Setter()
{
Property = TemplatedControl.FontFamilyProperty,
Value = new FontFamily(currentFontFamily),
});
Application.Current?.Styles.Add(style);
}
catch (Exception ex)
{ {
Property = Menu.FontSizeProperty, Logging.SaveLog("ModifyFontFamily", ex);
Value = size, }
});
Application.Current?.Styles.Add(menuStyle);
Style dataStyle = new(x => x.OfType<DataGridRow>());
dataStyle.Add(new Setter()
{
Property = DataGridRow.FontSizeProperty,
Value = size,
});
Application.Current?.Styles.Add(dataStyle);
Style listStyle = new(x => x.OfType<ListBoxItem>());
listStyle.Add(new Setter()
{
Property = ListBoxItem.FontSizeProperty,
Value = size,
});
Application.Current?.Styles.Add(listStyle);
} }
} }
} }

View File

@@ -39,7 +39,7 @@
<ListBox <ListBox
x:Name="lstCheckUpdates" x:Name="lstCheckUpdates"
BorderThickness="1" BorderThickness="1"
ItemsSource="{Binding CheckUpdateItems}"> ItemsSource="{Binding CheckUpdateModels}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel Orientation="Vertical" /> <StackPanel Orientation="Vertical" />

View File

@@ -15,7 +15,7 @@ namespace v2rayN.Desktop.Views
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {
this.OneWayBind(ViewModel, vm => vm.CheckUpdateItems, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
@@ -29,7 +29,7 @@ namespace v2rayN.Desktop.Views
case EViewAction.DispatcherCheckUpdate: case EViewAction.DispatcherCheckUpdate:
if (obj is null) return false; if (obj is null) return false;
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
ViewModel?.UpdateViewResult((CheckUpdateItem)obj), ViewModel?.UpdateViewResult((CheckUpdateModel)obj),
DispatcherPriority.Default); DispatcherPriority.Default);
break; break;

View File

@@ -161,10 +161,7 @@
<RowDefinition Height="8" /> <RowDefinition Height="8" />
<RowDefinition Height="1*" /> <RowDefinition Height="1*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock <TextBlock Grid.Row="0" Text="{Binding Name}" />
Grid.Row="0"
Text="{Binding Name}"
TextWrapping="WrapWithOverflow" />
<DockPanel Grid.Row="2"> <DockPanel Grid.Row="2">
<TextBlock <TextBlock
DockPanel.Dock="Right" DockPanel.Dock="Right"

View File

@@ -114,7 +114,7 @@
<MenuItem x:Name="menuClose" Padding="8,0"> <MenuItem x:Name="menuClose" Padding="8,0">
<MenuItem.Header> <MenuItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Static resx:ResUI.menuClose}" /> <TextBlock Text="{x:Static resx:ResUI.menuExit}" />
</StackPanel> </StackPanel>
</MenuItem.Header> </MenuItem.Header>
</MenuItem> </MenuItem>

View File

@@ -7,6 +7,7 @@ using Avalonia.Interactivity;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading; using Avalonia.Threading;
using DialogHostAvalonia; using DialogHostAvalonia;
using MsBox.Avalonia.Enums;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
using System.ComponentModel; using System.ComponentModel;
@@ -29,13 +30,10 @@ namespace v2rayN.Desktop.Views
_config = AppHandler.Instance.Config; _config = AppHandler.Instance.Config;
_manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.BottomRight }; _manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.BottomRight };
//ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false);
this.Closing += MainWindow_Closing; this.Closing += MainWindow_Closing;
this.KeyDown += MainWindow_KeyDown; this.KeyDown += MainWindow_KeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click; menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click; menuPromotion.Click += menuPromotion_Click;
menuClose.Click += menuClose_Click;
menuCheckUpdate.Click += MenuCheckUpdate_Click; menuCheckUpdate.Click += MenuCheckUpdate_Click;
menuBackupAndRestore.Click += MenuBackupAndRestore_Click; menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
@@ -82,6 +80,7 @@ namespace v2rayN.Desktop.Views
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ExitCmd, v => v.menuClose).DisposeWith(disposables);
switch (_config.UiItem.MainGirdOrientation) switch (_config.UiItem.MainGirdOrientation)
{ {
@@ -114,6 +113,8 @@ namespace v2rayN.Desktop.Views
this.Title = $"{Utils.GetVersion()} - {(AppHandler.Instance.IsAdministrator ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; this.Title = $"{Utils.GetVersion()} - {(AppHandler.Instance.IsAdministrator ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
if (Utils.IsWindows()) if (Utils.IsWindows())
{ {
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
menuGlobalHotkeySetting.IsVisible = false; menuGlobalHotkeySetting.IsVisible = false;
} }
else else
@@ -158,7 +159,9 @@ namespace v2rayN.Desktop.Views
private void OnProgramStarted(object state, bool timeout) private void OnProgramStarted(object state, bool timeout)
{ {
ShowHideWindow(true); Dispatcher.UIThread.Post(() =>
ShowHideWindow(true),
DispatcherPriority.Default);
} }
private void DelegateSnackMsg(string content) private void DelegateSnackMsg(string content)
@@ -240,6 +243,14 @@ namespace v2rayN.Desktop.Views
Locator.Current.GetService<ProfilesViewModel>()?.AutofitColumnWidthAsync(), Locator.Current.GetService<ProfilesViewModel>()?.AutofitColumnWidthAsync(),
DispatcherPriority.Default); DispatcherPriority.Default);
break; break;
case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.menuExitTips) == ButtonResult.No)
{
return false;
}
StorageUI();
break;
} }
return await Task.FromResult(true); return await Task.FromResult(true);
@@ -302,12 +313,6 @@ namespace v2rayN.Desktop.Views
} }
} }
private void menuClose_Click(object? sender, RoutedEventArgs e)
{
StorageUI();
ShowHideWindow(false);
}
private void menuPromotion_Click(object? sender, RoutedEventArgs e) private void menuPromotion_Click(object? sender, RoutedEventArgs e)
{ {
Utils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}"); Utils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
@@ -374,8 +379,16 @@ namespace v2rayN.Desktop.Views
} }
else else
{ {
this.Hide(); if (Utils.IsWindows())
{
this.Hide();
}
else
{
this.WindowState = WindowState.Minimized;
}
} }
_config.UiItem.ShowInTaskbar = bl; _config.UiItem.ShowInTaskbar = bl;
} }

View File

@@ -83,6 +83,7 @@
Classes="TextArea" Classes="TextArea"
IsReadOnly="True" IsReadOnly="True"
TextAlignment="Left" TextAlignment="Left"
VerticalAlignment="Stretch"
TextWrapping="Wrap"> TextWrapping="Wrap">
<TextBox.ContextMenu> <TextBox.ContextMenu>
<ContextMenu> <ContextMenu>

View File

@@ -377,7 +377,7 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!--
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
@@ -390,7 +390,8 @@
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" /> Classes="Margin8" />
<TextBlock <!--
<TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="2" Grid.Column="2"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -488,18 +489,7 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
Classes="Margin8" /> Classes="Margin8" />
<TextBlock
Grid.Row="9"
Grid.Column="0"
VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
<ToggleSwitch
x:Name="togEnableCheckPreReleaseUpdate"
Grid.Row="9"
Grid.Column="1"
HorizontalAlignment="Left"
Classes="Margin8" />
<TextBlock <TextBlock
Grid.Row="11" Grid.Row="11"
@@ -533,7 +523,6 @@
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Classes="Margin8"
IsVisible="False"
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" /> Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
<ComboBox <ComboBox
x:Name="cmbcurrentFontFamily" x:Name="cmbcurrentFontFamily"
@@ -541,15 +530,13 @@
Grid.Column="1" Grid.Column="1"
Width="200" Width="200"
Classes="Margin8" Classes="Margin8"
IsVisible="False"
MaxDropDownHeight="1000" /> MaxDropDownHeight="1000" />
<TextBlock <TextBlock
Grid.Row="16" Grid.Row="16"
Grid.Column="2" Grid.Column="2"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Classes="Margin8"
IsVisible="False" Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyLinuxTip}"
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyTip}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <TextBlock
@@ -677,7 +664,6 @@
Classes="Margin8" Classes="Margin8"
Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}" Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</TabItem> </TabItem>

View File

@@ -15,7 +15,6 @@ namespace v2rayN.Desktop.Views
btnCancel.Click += (s, e) => this.Close(); btnCancel.Click += (s, e) => this.Close();
_config = AppHandler.Instance.Config; _config = AppHandler.Instance.Config;
// var lstFonts = GetFonts(Utils.GetFontsPath());
ViewModel = new OptionSettingViewModel(UpdateViewHandler); ViewModel = new OptionSettingViewModel(UpdateViewHandler);
@@ -100,9 +99,6 @@ namespace v2rayN.Desktop.Views
cmbMainGirdOrientation.Items.Add(it.ToString()); cmbMainGirdOrientation.Items.Add(it.ToString());
} }
//lstFonts.ForEach(it => { cmbcurrentFontFamily.Items.Add(it); });
//cmbcurrentFontFamily.Items.Add(string.Empty);
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {
this.Bind(ViewModel, vm => vm.localPort, v => v.txtlocalPort.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.localPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
@@ -127,7 +123,7 @@ namespace v2rayN.Desktop.Views
this.Bind(ViewModel, vm => vm.hyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.hyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.enableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.enableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.AutoRun, v => v.togAutoRun.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoRun, v => v.togAutoRun.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableStatistics, v => v.togEnableStatistics.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableStatistics, v => v.togEnableStatistics.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.KeepOlderDedupl, v => v.togKeepOlderDedupl.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.KeepOlderDedupl, v => v.togKeepOlderDedupl.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.IgnoreGeoUpdateCore, v => v.togIgnoreGeoUpdateCore.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.IgnoreGeoUpdateCore, v => v.togIgnoreGeoUpdateCore.IsChecked).DisposeWith(disposables);
@@ -135,7 +131,6 @@ namespace v2rayN.Desktop.Views
this.Bind(ViewModel, vm => vm.EnableUpdateSubOnlyRemarksExist, v => v.togEnableUpdateSubOnlyRemarksExist.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableUpdateSubOnlyRemarksExist, v => v.togEnableUpdateSubOnlyRemarksExist.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableSecurityProtocolTls13, v => v.togEnableSecurityProtocolTls13.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableSecurityProtocolTls13, v => v.togEnableSecurityProtocolTls13.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentFontFamily, v => v.cmbcurrentFontFamily.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CurrentFontFamily, v => v.cmbcurrentFontFamily.SelectedValue).DisposeWith(disposables);
@@ -179,61 +174,55 @@ namespace v2rayN.Desktop.Views
switch (action) switch (action)
{ {
case EViewAction.CloseWindow: case EViewAction.CloseWindow:
// WindowsUtils.SetAutoRun(Global.AutoRunRegPath, Global.AutoRunName, togAutoRun.IsChecked ?? false);
this.Close(true); this.Close(true);
break; break;
case EViewAction.InitSettingFont:
await InitSettingFont();
break;
} }
return await Task.FromResult(true); return await Task.FromResult(true);
} }
//private List<string> GetFonts(string path) private async Task InitSettingFont()
//{ {
// var lstFonts = new List<string>(); var lstFonts = await GetFonts();
// try lstFonts.ForEach(it => { cmbcurrentFontFamily.Items.Add(it); });
// { cmbcurrentFontFamily.Items.Add(string.Empty);
// string[] searchPatterns = { "*.ttf", "*.ttc" }; }
// var files = new List<string>();
// foreach (var pattern in searchPatterns) private async Task<List<string>> GetFonts()
// { {
// files.AddRange(Directory.GetFiles(path, pattern)); var lstFonts = new List<string>();
// } try
// var culture = _config.uiItem.currentLanguage == Global.Languages[0] ? "zh-cn" : "en-us"; {
// var culture2 = "en-us"; if (Utils.IsWindows())
// foreach (var ttf in files) {
// { return lstFonts;
// var families = Fonts.GetFontFamilies(Utils.GetFontsPath(ttf)); }
// foreach (FontFamily family in families) else if (Utils.IsLinux())
// { {
// var typefaces = family.GetTypefaces(); var result = await Utils.GetLinuxFontFamily("zh");
// foreach (Typeface typeface in typefaces) if (result.IsNullOrEmpty())
// { {
// typeface.TryGetGlyphTypeface(out GlyphTypeface glyph); return lstFonts;
// //var fontFace = glyph.Win32FaceNames[new CultureInfo("en-us")]; }
// //if (!fontFace.Equals("Regular") && !fontFace.Equals("Normal"))
// //{ var lst = result.Split(Environment.NewLine)
// // continue; .Where(t => t.IsNotEmpty())
// //} .ToList()
// var fontFamily = glyph.Win32FamilyNames[new CultureInfo(culture)]; .Select(t => t.Split(",").FirstOrDefault() ?? "")
// if (Utils.IsNullOrEmpty(fontFamily)) .OrderBy(t => t)
// { .ToList();
// fontFamily = glyph.Win32FamilyNames[new CultureInfo(culture2)]; return lst;
// if (Utils.IsNullOrEmpty(fontFamily)) }
// { }
// continue; catch (Exception ex)
// } {
// } Logging.SaveLog("fill fonts error", ex);
// lstFonts.Add(fontFamily); }
// break; return lstFonts;
// } }
// }
// }
// }
// catch (Exception ex)
// {
// Logging.SaveLog("fill fonts error", ex);
// }
// return lstFonts;
//}
private void ClbdestOverride_SelectionChanged(object? sender, SelectionChangedEventArgs e) private void ClbdestOverride_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {

View File

@@ -189,6 +189,8 @@ namespace v2rayN.Desktop.Views
private void LstProfiles_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) private void LstProfiles_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{ {
var source = e.Source as Border;
if (source == null || source.Name != "CellBorder") return;
if (_config.UiItem.DoubleClick2Activate) if (_config.UiItem.DoubleClick2Activate)
{ {
ViewModel?.SetDefaultServer(); ViewModel?.SetDefaultServer();

View File

@@ -52,6 +52,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@@ -114,7 +115,7 @@
<TextBox <TextBox
x:Name="txtAutoUpdateInterval" x:Name="txtAutoUpdateInterval"
Width="200" Width="100"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Classes="Margin8"
DockPanel.Dock="Right" DockPanel.Dock="Right"
@@ -233,13 +234,27 @@
<TextBlock <TextBlock
Grid.Row="12" Grid.Row="12"
Grid.Column="0" Grid.Column="0"
VerticalAlignment="Center"
Classes="Margin8"
Text="{x:Static resx:ResUI.LvMemo}" />
<TextBox
x:Name="txtMemo"
Grid.Row="12"
Grid.Column="1"
VerticalAlignment="Center"
Classes="Margin8"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="13"
Grid.Column="0"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
VerticalAlignment="Center" VerticalAlignment="Center"
Classes="Margin8" Classes="Margin8"
Text="{x:Static resx:ResUI.LvMoreUrl}" /> Text="{x:Static resx:ResUI.LvMoreUrl}" />
<TextBox <TextBox
x:Name="txtMoreUrl" x:Name="txtMoreUrl"
Grid.Row="13" Grid.Row="14"
Grid.Column="1" Grid.Column="1"
MinHeight="100" MinHeight="100"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@@ -41,6 +41,7 @@ namespace v2rayN.Desktop.Views
this.Bind(ViewModel, vm => vm.SelectedSource.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.NextProfile, v => v.txtNextProfile.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.NextProfile, v => v.txtNextProfile.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Memo, v => v.txtMemo.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
}); });

View File

@@ -8,6 +8,7 @@
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright> <Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<AssemblyName>v2rayN</AssemblyName>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -19,16 +20,16 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.1.4" /> <PackageReference Include="Avalonia" Version="11.2.0" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.4" /> <PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.0" />
<PackageReference Include="Avalonia.Desktop" Version="11.1.4" /> <PackageReference Include="Avalonia.Desktop" Version="11.2.0" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.1.4" /> <PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.0" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.4" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.0" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.1.4" /> <PackageReference Include="Avalonia.ReactiveUI" Version="11.2.0" />
<PackageReference Include="DialogHost.Avalonia" Version="0.8.1" /> <PackageReference Include="DialogHost.Avalonia" Version="0.8.1" />
<PackageReference Include="MessageBox.Avalonia" Version="3.1.6.13" /> <PackageReference Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageReference Include="Semi.Avalonia" Version="11.1.0.4" /> <PackageReference Include="Semi.Avalonia" Version="11.2.0" />
<PackageReference Include="Semi.Avalonia.DataGrid" Version="11.1.0.4" /> <PackageReference Include="Semi.Avalonia.DataGrid" Version="11.2.0" />
<PackageReference Include="ReactiveUI" Version="20.1.63" /> <PackageReference Include="ReactiveUI" Version="20.1.63" />
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
</ItemGroup> </ItemGroup>

View File

@@ -1,13 +1,9 @@
using Microsoft.Win32; using Microsoft.Win32;
using Microsoft.Win32.TaskScheduler;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Security.Principal;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Windows; using System.Windows;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Media; using System.Windows.Media;
@@ -55,49 +51,6 @@ namespace v2rayN
} }
} }
/// <summary>
/// Auto Start via TaskService
/// </summary>
/// <param name="taskName"></param>
/// <param name="fileName"></param>
/// <param name="description"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void AutoStart(string taskName, string fileName, string description)
{
if (Utils.IsNullOrEmpty(taskName))
{
return;
}
string TaskName = taskName;
var logonUser = WindowsIdentity.GetCurrent().Name;
string taskDescription = description;
string deamonFileName = fileName;
using var taskService = new TaskService();
var tasks = taskService.RootFolder.GetTasks(new Regex(TaskName));
foreach (var t in tasks)
{
taskService.RootFolder.DeleteTask(t.Name);
}
if (Utils.IsNullOrEmpty(fileName))
{
return;
}
var task = taskService.NewTask();
task.RegistrationInfo.Description = taskDescription;
task.Settings.DisallowStartIfOnBatteries = false;
task.Settings.StopIfGoingOnBatteries = false;
task.Settings.RunOnlyIfIdle = false;
task.Settings.IdleSettings.StopOnIdleEnd = false;
task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
task.Triggers.Add(new LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) });
task.Principal.RunLevel = TaskRunLevel.Highest;
task.Actions.Add(new ExecAction(deamonFileName.AppendQuotes(), null, Path.GetDirectoryName(deamonFileName)));
taskService.RootFolder.RegisterTaskDefinition(TaskName, task);
}
[DllImport("dwmapi.dll")] [DllImport("dwmapi.dll")]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize); public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
@@ -116,58 +69,6 @@ namespace v2rayN
return value is int i && i > 0; return value is int i && i > 0;
} }
public static string? RegReadValue(string path, string name, string def)
{
RegistryKey? regKey = null;
try
{
regKey = Registry.CurrentUser.OpenSubKey(path, false);
string? value = regKey?.GetValue(name) as string;
if (Utils.IsNullOrEmpty(value))
{
return def;
}
else
{
return value;
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
finally
{
regKey?.Close();
}
return def;
}
public static void RegWriteValue(string path, string name, object value)
{
RegistryKey? regKey = null;
try
{
regKey = Registry.CurrentUser.CreateSubKey(path);
if (Utils.IsNullOrEmpty(value.ToString()))
{
regKey?.DeleteValue(name, false);
}
else
{
regKey?.SetValue(name, value);
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
finally
{
regKey?.Close();
}
}
public static void RemoveTunDevice() public static void RemoveTunDevice()
{ {
try try
@@ -209,42 +110,6 @@ namespace v2rayN
DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref attribute, attributeSize); DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref attribute, attributeSize);
} }
/// <summary>
/// 开机自动启动
/// </summary>
/// <param name="run"></param>
/// <returns></returns>
public static void SetAutoRun(string AutoRunRegPath, string AutoRunName, bool run)
{
try
{
var autoRunName = $"{AutoRunName}_{Utils.GetMd5(Utils.StartupPath())}";
//delete first
RegWriteValue(AutoRunRegPath, autoRunName, "");
if (Utils.IsAdministrator())
{
AutoStart(autoRunName, "", "");
}
if (run)
{
string exePath = Utils.GetExePath();
if (Utils.IsAdministrator())
{
AutoStart(autoRunName, exePath, "");
}
else
{
RegWriteValue(AutoRunRegPath, autoRunName, exePath.AppendQuotes());
}
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
#region Windows API #region Windows API
[Flags] [Flags]

View File

@@ -15,7 +15,7 @@ namespace v2rayN.Views
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {
this.OneWayBind(ViewModel, vm => vm.CheckUpdateItems, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
@@ -30,7 +30,7 @@ namespace v2rayN.Views
if (obj is null) return false; if (obj is null) return false;
Application.Current?.Dispatcher.Invoke((() => Application.Current?.Dispatcher.Invoke((() =>
{ {
ViewModel?.UpdateViewResult((CheckUpdateItem)obj); ViewModel?.UpdateViewResult((CheckUpdateModel)obj);
}), DispatcherPriority.Normal); }), DispatcherPriority.Normal);
break; break;

View File

@@ -284,6 +284,8 @@ namespace v2rayN.Views
switch (e.Key) switch (e.Key)
{ {
case Key.V: case Key.V:
if (_backupAndRestoreView?.IsVisible == true) return;
var clipboardData = WindowsUtils.GetClipboardData(); var clipboardData = WindowsUtils.GetClipboardData();
ViewModel?.AddServerViaClipboardAsync(clipboardData); ViewModel?.AddServerViaClipboardAsync(clipboardData);
break; break;
@@ -365,17 +367,17 @@ namespace v2rayN.Views
var bl = blShow ?? !_config.UiItem.ShowInTaskbar; var bl = blShow ?? !_config.UiItem.ShowInTaskbar;
if (bl) if (bl)
{ {
Application.Current.MainWindow.Show(); this?.Show();
if (Application.Current.MainWindow.WindowState == WindowState.Minimized) if (this?.WindowState == WindowState.Minimized)
{ {
Application.Current.MainWindow.WindowState = WindowState.Normal; this.WindowState = WindowState.Normal;
} }
Application.Current.MainWindow.Activate(); this?.Activate();
Application.Current.MainWindow.Focus(); this?.Focus();
} }
else else
{ {
Application.Current.MainWindow.Hide(); this?.Hide();
} }
_config.UiItem.ShowInTaskbar = bl; _config.UiItem.ShowInTaskbar = bl;
} }

View File

@@ -69,6 +69,8 @@
</WrapPanel> </WrapPanel>
<TextBox <TextBox
Name="txtMsg" Name="txtMsg"
VerticalAlignment="Stretch"
AcceptsReturn="True"
BorderThickness="0" BorderThickness="0"
FontSize="{DynamicResource StdFontSize-1}" FontSize="{DynamicResource StdFontSize-1}"
HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"

View File

@@ -661,20 +661,6 @@
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<TextBlock
Grid.Row="9"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
<ToggleButton
x:Name="togEnableCheckPreReleaseUpdate"
Grid.Row="9"
Grid.Column="1"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<TextBlock <TextBlock
Grid.Row="10" Grid.Row="10"
Grid.Column="0" Grid.Column="0"

View File

@@ -17,7 +17,6 @@ namespace v2rayN.Views
this.Owner = Application.Current.MainWindow; this.Owner = Application.Current.MainWindow;
_config = AppHandler.Instance.Config; _config = AppHandler.Instance.Config;
var lstFonts = GetFonts(Utils.GetFontsPath());
ViewModel = new OptionSettingViewModel(UpdateViewHandler); ViewModel = new OptionSettingViewModel(UpdateViewHandler);
@@ -102,9 +101,6 @@ namespace v2rayN.Views
cmbMainGirdOrientation.Items.Add(it.ToString()); cmbMainGirdOrientation.Items.Add(it.ToString());
} }
lstFonts.ForEach(it => { cmbcurrentFontFamily.Items.Add(it); });
cmbcurrentFontFamily.Items.Add(string.Empty);
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {
this.Bind(ViewModel, vm => vm.localPort, v => v.txtlocalPort.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.localPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
@@ -145,7 +141,6 @@ namespace v2rayN.Views
this.Bind(ViewModel, vm => vm.EnableUpdateSubOnlyRemarksExist, v => v.togEnableUpdateSubOnlyRemarksExist.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableUpdateSubOnlyRemarksExist, v => v.togEnableUpdateSubOnlyRemarksExist.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableSecurityProtocolTls13, v => v.togEnableSecurityProtocolTls13.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableSecurityProtocolTls13, v => v.togEnableSecurityProtocolTls13.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableDragDropSort, v => v.togEnableDragDropSort.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableDragDropSort, v => v.togEnableDragDropSort.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables);
@@ -188,14 +183,24 @@ namespace v2rayN.Views
switch (action) switch (action)
{ {
case EViewAction.CloseWindow: case EViewAction.CloseWindow:
WindowsUtils.SetAutoRun(Global.AutoRunRegPath, Global.AutoRunName, togAutoRun.IsChecked ?? false);
this.DialogResult = true; this.DialogResult = true;
break; break;
case EViewAction.InitSettingFont:
await InitSettingFont();
break;
} }
return await Task.FromResult(true); return await Task.FromResult(true);
} }
private List<string> GetFonts(string path) private async Task InitSettingFont()
{
var lstFonts = await GetFonts(Utils.GetFontsPath());
lstFonts.ForEach(it => { cmbcurrentFontFamily.Items.Add(it); });
cmbcurrentFontFamily.Items.Add(string.Empty);
}
private async Task<List<string>> GetFonts(string path)
{ {
var lstFonts = new List<string>(); var lstFonts = new List<string>();
try try
@@ -241,7 +246,7 @@ namespace v2rayN.Views
{ {
Logging.SaveLog("fill fonts error", ex); Logging.SaveLog("fill fonts error", ex);
} }
return lstFonts; return lstFonts.OrderBy(t => t).ToList();
} }
private void ClbdestOverride_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) private void ClbdestOverride_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)

View File

@@ -1,14 +1,14 @@
<reactiveui:ReactiveWindow <reactiveui:ReactiveWindow
x:Class="v2rayN.Views.SubEditWindow" x:Class="v2rayN.Views.SubEditWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:v2rayN.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib" xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
xmlns:conv="clr-namespace:v2rayN.Converters"
Title="{x:Static resx:ResUI.menuSubSetting}" Title="{x:Static resx:ResUI.menuSubSetting}"
Width="700" Width="700"
Height="600" Height="600"
@@ -70,6 +70,7 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@@ -165,7 +166,7 @@
<TextBox <TextBox
x:Name="txtAutoUpdateInterval" x:Name="txtAutoUpdateInterval"
Width="200" Width="100"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
VerticalAlignment="Top" VerticalAlignment="Top"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.SubUrlTips}" materialDesign:HintAssist.Hint="{x:Static resx:ResUI.SubUrlTips}"
@@ -300,6 +301,24 @@
AcceptsReturn="True" AcceptsReturn="True"
Style="{StaticResource MyOutlinedTextBox}" Style="{StaticResource MyOutlinedTextBox}"
ToolTip="{x:Static resx:ResUI.TipPreSocksPort}" /> ToolTip="{x:Static resx:ResUI.TipPreSocksPort}" />
<TextBlock
Grid.Row="12"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.LvMemo}" />
<TextBox
x:Name="txtMemo"
Grid.Row="12"
Grid.Column="1"
Margin="{StaticResource Margin4}"
VerticalAlignment="Top"
AcceptsReturn="True"
Style="{StaticResource MyOutlinedTextBox}"
TextWrapping="Wrap" />
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</DockPanel> </DockPanel>

View File

@@ -34,6 +34,7 @@ namespace v2rayN.Views
this.Bind(ViewModel, vm => vm.SelectedSource.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.NextProfile, v => v.txtNextProfile.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.NextProfile, v => v.txtNextProfile.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Memo, v => v.txtMemo.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
}); });

View File

@@ -16,7 +16,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="MaterialDesignThemes" Version="5.1.0" /> <PackageReference Include="MaterialDesignThemes" Version="5.1.0" />
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.1.4" /> <PackageReference Include="H.NotifyIcon.Wpf" Version="2.1.4" />
<PackageReference Include="TaskScheduler" Version="2.11.0" />
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageReference Include="ReactiveUI.WPF" Version="20.1.63" /> <PackageReference Include="ReactiveUI.WPF" Version="20.1.63" />
</ItemGroup> </ItemGroup>