Compare commits

...

18 Commits

Author SHA1 Message Date
2dust
d727ff40bb Code clean 2025-10-31 20:25:54 +08:00
2dust
1b5069a933 Code clean 2025-10-31 20:25:50 +08:00
2dust
18ea6fdc00 Code clean 2025-10-31 20:25:45 +08:00
2dust
67494108ad up 7.15.7 2025-10-31 19:05:57 +08:00
2dust
38b2a7d2ca Rename QRCodeWindowsUtils 2025-10-31 17:50:28 +08:00
dependabot[bot]
bf3703bca1 Bump actions/download-artifact from 4 to 6 (#8225)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-30 20:36:43 +08:00
2dust
554632cc07 Bug fix
https://github.com/2dust/v2rayN/issues/8207
2025-10-30 20:34:47 +08:00
2dust
12fc3e9566 Bug fix 2025-10-30 20:00:15 +08:00
2dust
c2ef3a4a8c Add Design.IsDesignMode to the desktop version 2025-10-29 20:21:41 +08:00
2dust
86eb8297dd Update JsonUtils.cs 2025-10-29 20:20:44 +08:00
2dust
c63d4e83f9 Use OperatingSystem replace RuntimeInformation 2025-10-29 20:20:40 +08:00
JieXu
bf1fb0f92e RPM file remove x86-64-v1 Support. Update French translation. (#8216)
* Update ResUI.fr.resx

* Update build-linux.yml

* Update package-rhel.sh

* 更新 build-linux.yml

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx
2025-10-29 09:21:37 +08:00
dependabot[bot]
3c4865982b Bump actions/upload-artifact from 4.6.2 to 5.0.0 (#8211)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.2 to 5.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.6.2...v5.0.0)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-28 13:55:57 +08:00
Aron Yang
22c233f0cd Fix TUN mode cleanup on Linux/macOS (#8202) 2025-10-26 11:09:23 +08:00
JieXu
b2d6282755 Remove AppImage. Update package.sh (#8201)
* Refactor AppRun script generation in packaging

* Update minimum kernel version requirement to 6.13

* Update minimum kernel version requirement to 5.14

* Revise runtime dependencies with version constraints

Updated runtime dependencies for package-rhel.sh to include version constraints and additional requirements.

* Modify package dependencies in package-debian.sh

Updated package dependencies to include libc6, fontconfig, coreutils, and bash.

* Remove AppImage packaging and upload steps

Removed AppImage packaging and upload steps from the workflow.

* Delete package-appimage.sh

* Simplify environment checks in Utils.cs

Removed checks for APPIMAGE environment variable and mount path.

* Update v2rayN.slnx

* Remove package scripts from v2rayN solution

Removed references to package-appimage.sh and pkg2appimage.yml from the solution file.
2025-10-26 10:13:49 +08:00
2dust
c8d89e3dce Adjusted the items in the configuration right-click menu 2025-10-25 20:10:42 +08:00
DHR60
d3b1810eab Update Directory.Packages.props (#8191) 2025-10-25 10:27:31 +08:00
JieXu
51409a3e28 Add French support | Ajouter le support du français | 添加法语支持 (#8186)
* Add files via upload

* Add files via upload

* Update Global.cs

* Add French resource file to project

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Delete v2rayN/AmazTool/Resx/Resource.fr.resx
2025-10-24 19:38:40 +08:00
85 changed files with 2011 additions and 405 deletions

View File

@@ -9,6 +9,12 @@ on:
push:
branches:
- master
tags:
- 'v*'
- 'V*'
permissions:
contents: write
env:
OutputArch: "linux-64"
@@ -21,7 +27,6 @@ jobs:
strategy:
matrix:
configuration: [Release]
runs-on: ubuntu-24.04
steps:
@@ -31,21 +36,21 @@ jobs:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup
- name: Setup .NET
uses: actions/setup-dotnet@v5.0.0
with:
dotnet-version: '8.0.x'
- name: Build
run: |
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o $OutputPath64
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o $OutputPathArm64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o "$OutputPath64"
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o "$OutputPathArm64"
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPath64"
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-linux
path: |
@@ -56,8 +61,8 @@ jobs:
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-debian.sh
./package-debian.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
./package-debian.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }}
./package-debian.sh "$OutputArch" "$OutputPath64" "${{ github.event.inputs.release_tag }}"
./package-debian.sh "$OutputArchArm" "$OutputPathArm64" "${{ github.event.inputs.release_tag }}"
- name: Upload deb to release
uses: svenstaro/upload-release-action@v2
@@ -68,28 +73,13 @@ jobs:
file_glob: true
prerelease: true
- name: Package AppImage
if: github.event.inputs.release_tag != ''
run: |
chmod a+x package-appimage.sh
./package-appimage.sh
- name: Upload AppImage to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.AppImage
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true
# release zip archive
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
./package-release-zip.sh $OutputArchArm $OutputPathArm64
./package-release-zip.sh "$OutputArch" "$OutputPath64"
./package-release-zip.sh "$OutputArchArm" "$OutputPathArm64"
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
@@ -100,36 +90,62 @@ jobs:
file_glob: true
prerelease: true
# release RHEL package
- name: Package RPM (RHEL-family)
if: github.event.inputs.release_tag != ''
rpm:
needs: build
if: |
(github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag != '') ||
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
runs-on: ubuntu-24.04
container:
image: quay.io/almalinuxorg/10-base:latest
options: --platform=linux/amd64/v2
env:
RELEASE_TAG: ${{ github.event.inputs.release_tag != '' && github.event.inputs.release_tag || github.ref_name }}
steps:
- name: Prepare tools (Red Hat)
run: |
chmod 755 package-rhel.sh
# Build for both x86_64 and aarch64 in one go (explicit version passed; no --buildfrom)
./package-rhel.sh "${{ github.event.inputs.release_tag }}" --arch all
dnf -y makecache
dnf -y install epel-release
dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
- name: Checkout repo (for scripts)
uses: actions/checkout@v5.0.0
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Restore build artifacts
uses: actions/download-artifact@v6
with:
name: v2rayN-linux
path: ${{ github.workspace }}/v2rayN/Release
- name: Ensure script permissions
run: chmod 755 package-rhel.sh
- name: Package RPM (RHEL-family)
run: ./package-rhel.sh "${RELEASE_TAG}" --arch all
- name: Collect RPMs into workspace
if: github.event.inputs.release_tag != ''
run: |
mkdir -p "${{ github.workspace }}/dist/rpm"
rsync -av "$HOME/rpmbuild/RPMS/" "${{ github.workspace }}/dist/rpm/"
# Rename to requested filenames
find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.x86_64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-x64.rpm" \; || true
find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.aarch64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true
mkdir -p "$GITHUB_WORKSPACE/dist/rpm"
rsync -av "$HOME/rpmbuild/RPMS/" "$GITHUB_WORKSPACE/dist/rpm/" || true
find "$GITHUB_WORKSPACE/dist/rpm" -name "v2rayN-*-1*.x86_64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm/v2rayN-linux-rhel-64.rpm" \; || true
find "$GITHUB_WORKSPACE/dist/rpm" -name "v2rayN-*-1*.aarch64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true
echo "==== Dist tree ===="
ls -R "$GITHUB_WORKSPACE/dist/rpm" || true
- name: Upload RPM artifacts
if: github.event.inputs.release_tag != ''
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-rpm
path: |
${{ github.workspace }}/dist/rpm/**/*.rpm
path: dist/rpm/**/*.rpm
- name: Upload RPMs to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/dist/rpm/**/*.rpm
tag: ${{ github.event.inputs.release_tag }}
file: dist/rpm/**/*.rpm
tag: ${{ env.RELEASE_TAG }}
file_glob: true
prerelease: true

View File

@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-macos
path: |

View File

@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-windows-desktop
path: |

View File

@@ -46,7 +46,7 @@ jobs:
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-windows
path: |

View File

@@ -1,67 +0,0 @@
#!/bin/bash
set -euo pipefail
# Install deps
sudo apt update -y
sudo apt install -y libfuse2 wget file
# Get tools
wget -qO appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x appimagetool
# x86_64 AppDir
APPDIR_X64="AppDir-x86_64"
rm -rf "$APPDIR_X64"
mkdir -p "$APPDIR_X64/usr/lib/v2rayN" "$APPDIR_X64/usr/bin" "$APPDIR_X64/usr/share/applications" "$APPDIR_X64/usr/share/pixmaps"
cp -rf "$OutputPath64"/* "$APPDIR_X64/usr/lib/v2rayN" || true
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/usr/share/pixmaps/v2rayN.png" || true
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/v2rayN.png" || true
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_X64/AppRun"
chmod +x "$APPDIR_X64/AppRun"
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_X64/usr/bin/v2rayN"
cat > "$APPDIR_X64/v2rayN.desktop" <<EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
Exec=v2rayN
Icon=v2rayN
Terminal=false
Type=Application
Categories=Network;
EOF
install -Dm644 "$APPDIR_X64/v2rayN.desktop" "$APPDIR_X64/usr/share/applications/v2rayN.desktop"
ARCH=x86_64 ./appimagetool "$APPDIR_X64" "v2rayN-${OutputArch}.AppImage"
file "v2rayN-${OutputArch}.AppImage" | grep -q 'x86-64'
# aarch64 AppDir
APPDIR_ARM64="AppDir-aarch64"
rm -rf "$APPDIR_ARM64"
mkdir -p "$APPDIR_ARM64/usr/lib/v2rayN" "$APPDIR_ARM64/usr/bin" "$APPDIR_ARM64/usr/share/applications" "$APPDIR_ARM64/usr/share/pixmaps"
cp -rf "$OutputPathArm64"/* "$APPDIR_ARM64/usr/lib/v2rayN" || true
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/usr/share/pixmaps/v2rayN.png" || true
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/v2rayN.png" || true
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_ARM64/AppRun"
chmod +x "$APPDIR_ARM64/AppRun"
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_ARM64/usr/bin/v2rayN"
cat > "$APPDIR_ARM64/v2rayN.desktop" <<EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
Exec=v2rayN
Icon=v2rayN
Terminal=false
Type=Application
Categories=Network;
EOF
install -Dm644 "$APPDIR_ARM64/v2rayN.desktop" "$APPDIR_ARM64/usr/share/applications/v2rayN.desktop"
# aarch64 runtime
wget -qO runtime-aarch64 https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-aarch64
chmod +x runtime-aarch64
# build aarch64 AppImage
ARCH=aarch64 ./appimagetool --runtime-file ./runtime-aarch64 "$APPDIR_ARM64" "v2rayN-${OutputArchArm}.AppImage"
file "v2rayN-${OutputArchArm}.AppImage" | grep -q 'ARM aarch64'

View File

@@ -28,7 +28,7 @@ Package: v2rayN
Version: $Version
Architecture: $Arch2
Maintainer: https://github.com/2dust/v2rayN
Depends: desktop-file-utils, xdg-utils
Depends: libc6 (>= 2.34), fontconfig (>= 2.13.1), desktop-file-utils (>= 0.26), xdg-utils (>= 1.1.3), coreutils (>= 8.32), bash (>= 5.1)
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
EOF

View File

@@ -19,6 +19,23 @@ else
exit 1
fi
# ======================== Kernel version check (require >= 6.11) =======================
MIN_KERNEL_MAJOR=6
MIN_KERNEL_MINOR=11
KERNEL_FULL=$(uname -r)
KERNEL_MAJOR=$(echo "$KERNEL_FULL" | cut -d. -f1)
KERNEL_MINOR=$(echo "$KERNEL_FULL" | cut -d. -f2)
echo "[INFO] Detected kernel version: $KERNEL_FULL"
if (( KERNEL_MAJOR < MIN_KERNEL_MAJOR )) || { (( KERNEL_MAJOR == MIN_KERNEL_MAJOR )) && (( KERNEL_MINOR < MIN_KERNEL_MINOR )); }; then
echo "[ERROR] Kernel $KERNEL_FULL is too old. Requires Linux >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
echo "Please upgrade your system or use a newer container (e.g. Fedora 42+, RHEL 10+, Debian 13+)."
exit 1
fi
echo "[OK] Kernel version >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
# ===== Config & Parse arguments =========================================================
VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty
WITH_CORE="both" # Default: bundle both xray+sing-box
@@ -614,8 +631,13 @@ ExclusiveArch: aarch64 x86_64
Source0: __PKGROOT__.tar.gz
# Runtime dependencies (Avalonia / X11 / Fonts / GL)
Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrender, libXfixes, libXinerama, libxkbcommon
Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL, xdg-utils
Requires: freetype, cairo, pango, openssl, mesa-libEGL, mesa-libGL
Requires: glibc >= 2.34
Requires: fontconfig >= 2.13.1
Requires: desktop-file-utils >= 0.26
Requires: xdg-utils >= 1.1.3
Requires: coreutils >= 8.32
Requires: bash >= 5.1
%description
v2rayN Linux for Red Hat Enterprise Linux

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.15.6</Version>
<Version>7.15.7</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -9,16 +9,16 @@
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.8" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.8" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.8" />
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
<PackageVersion Include="CliWrap" Version="3.9.0" />
<PackageVersion Include="Downloader" Version="4.0.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.2" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageVersion Include="QRCoder" Version="1.7.0" />
<PackageVersion Include="ReactiveUI" Version="20.4.1" />
<PackageVersion Include="ReactiveUI" Version="22.2.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
<PackageVersion Include="ReactiveUI.WPF" Version="22.2.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.3.7" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7" />

View File

@@ -35,9 +35,13 @@ public class JsonUtils
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T DeepCopy<T>(T obj)
public static T? DeepCopy<T>(T? obj)
{
return Deserialize<T>(Serialize(obj, false))!;
if (obj is null)
{
return default;
}
return Deserialize<T>(Serialize(obj, false));
}
/// <summary>
@@ -67,7 +71,7 @@ public class JsonUtils
/// </summary>
/// <param name="strJson"></param>
/// <returns></returns>
public static JsonNode? ParseJson(string strJson)
public static JsonNode? ParseJson(string? strJson)
{
try
{
@@ -116,7 +120,7 @@ public class JsonUtils
/// <param name="obj"></param>
/// <param name="options"></param>
/// <returns></returns>
public static string Serialize(object? obj, JsonSerializerOptions options)
public static string Serialize(object? obj, JsonSerializerOptions? options)
{
var result = string.Empty;
try
@@ -125,7 +129,7 @@ public class JsonUtils
{
return result;
}
result = JsonSerializer.Serialize(obj, options);
result = JsonSerializer.Serialize(obj, options ?? _defaultSerializeOptions);
}
catch (Exception ex)
{

View File

@@ -424,7 +424,7 @@ public class Utils
// Handle IPv6 addresses, e.g., "[2001:db8::1]:443"
if (authority.StartsWith("[") && authority.Contains("]"))
{
int closingBracketIndex = authority.LastIndexOf(']');
var closingBracketIndex = authority.LastIndexOf(']');
if (closingBracketIndex < authority.Length - 1 && authority[closingBracketIndex + 1] == ':')
{
// Port exists
@@ -963,13 +963,13 @@ public class Utils
#region Platform
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static bool IsWindows() => OperatingSystem.IsWindows();
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
public static bool IsLinux() => OperatingSystem.IsLinux();
public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsOSX() => OperatingSystem.IsMacOS();
public static bool IsNonWindows() => !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static bool IsNonWindows() => !OperatingSystem.IsWindows();
public static string GetExeName(string name)
{
@@ -994,11 +994,6 @@ public class Utils
return false;
}
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE")))
{
return true;
}
var exePath = GetExePath();
var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? "";
var p = baseDir.Replace('\\', '/');
@@ -1008,11 +1003,6 @@ public class Utils
return false;
}
if (p.Contains("/.mount_", StringComparison.Ordinal))
{
return true;
}
if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase))
{
return true;

View File

@@ -1,6 +1,3 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ServiceLib.Common;
/*
* See:

View File

@@ -427,6 +427,7 @@ public class Global
"zh-Hant",
"en",
"fa-Ir",
"fr",
"ru",
"hu"
];

View File

@@ -447,13 +447,13 @@ public static class ConfigHandler
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> MoveServer(Config config, List<ProfileItem> lstProfile, int index, EMove eMove, int pos = -1)
{
int count = lstProfile.Count;
var count = lstProfile.Count;
if (index < 0 || index > lstProfile.Count - 1)
{
return -1;
}
for (int i = 0; i < lstProfile.Count; i++)
for (var i = 0; i < lstProfile.Count; i++)
{
ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
}
@@ -527,7 +527,7 @@ public static class ConfigHandler
return -1;
}
var ext = Path.GetExtension(fileName);
string newFileName = $"{Utils.GetGuid()}{ext}";
var newFileName = $"{Utils.GetGuid()}{ext}";
//newFileName = Path.Combine(Utile.GetTempPath(), newFileName);
try
@@ -1356,7 +1356,7 @@ public static class ConfigHandler
}
continue;
}
var profileItem = FmtHandler.ResolveConfig(str, out string msg);
var profileItem = FmtHandler.ResolveConfig(str, out var msg);
if (profileItem is null)
{
continue;
@@ -1440,7 +1440,7 @@ public static class ConfigHandler
{
await RemoveServersViaSubid(config, subid, isSub);
}
int count = 0;
var count = 0;
foreach (var it in lstProfiles)
{
it.Subid = subid;
@@ -1530,7 +1530,7 @@ public static class ConfigHandler
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
if (lstSsServer?.Count > 0)
{
int counter = 0;
var counter = 0;
foreach (var ssItem in lstSsServer)
{
ssItem.Subid = subid;
@@ -1705,7 +1705,7 @@ public static class ConfigHandler
var maxSort = 0;
if (await SQLiteHelper.Instance.TableAsync<SubItem>().CountAsync() > 0)
{
var lstSubs = (await AppManager.Instance.SubItems());
var lstSubs = await AppManager.Instance.SubItems();
maxSort = lstSubs.LastOrDefault()?.Sort ?? 0;
}
item.Sort = maxSort + 1;
@@ -1867,7 +1867,7 @@ public static class ConfigHandler
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> MoveRoutingRule(List<RulesItem> rules, int index, EMove eMove, int pos = -1)
{
int count = rules.Count;
var count = rules.Count;
if (index < 0 || index > rules.Count - 1)
{
return -1;

View File

@@ -58,7 +58,7 @@ public static class CoreConfigHandler
File.Delete(fileName);
}
string addressFileName = node.Address;
var addressFileName = node.Address;
if (!File.Exists(addressFileName))
{
addressFileName = Utils.GetConfigPath(addressFileName);

View File

@@ -37,7 +37,7 @@ public class FmtHandler
try
{
string str = config.TrimEx();
var str = config.TrimEx();
if (str.IsNullOrEmpty())
{
msg = ResUI.FailedReadConfiguration;

View File

@@ -33,9 +33,9 @@ public class Hysteria2Fmt : BaseFmt
{
if (item == null)
return null;
string url = string.Empty;
var url = string.Empty;
string remark = string.Empty;
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);

View File

@@ -85,7 +85,7 @@ public class ActionPrecheckManager(Config config)
break;
case EConfigType.VLESS:
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id) && item.Id.Length > 30)
if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30))
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
if (!Global.Flows.Contains(item.Flow))
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));

View File

@@ -34,7 +34,7 @@ public class CoreAdminManager
StringBuilder sb = new();
sb.AppendLine("#!/bin/bash");
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
sb.AppendLine($"sudo -S {cmdLine}");
sb.AppendLine($"exec sudo -S -- {cmdLine}");
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
var procService = new ProcessService(

View File

@@ -28,7 +28,7 @@ public class ProfileItem : ReactiveObject
public string GetSummary()
{
var summary = $"[{(ConfigType).ToString()}] ";
var summary = $"[{ConfigType.ToString()}] ";
if (IsComplex())
{
summary += $"[{CoreType.ToString()}]{Remarks}";

File diff suppressed because it is too large Load Diff

View File

@@ -427,7 +427,7 @@
<value>路由设置</value>
</data>
<data name="menuServers" xml:space="preserve">
<value>配置文件</value>
<value>配置</value>
</data>
<data name="menuSetting" xml:space="preserve">
<value>设置</value>

View File

@@ -28,15 +28,15 @@ fi
kill_children() {
local parent=$1
local children=$(ps -o pid --no-headers --ppid "$parent")
# Output information about processes being terminated
echo "Processing children of PID: $parent..."
# Process each child
for child in $children; do
# Recursively find and kill child's children first
kill_children "$child"
# Force kill the child process
echo "Terminating child process: $child"
kill -9 "$child" 2>/dev/null || true
@@ -47,6 +47,18 @@ echo "============================================"
echo "Starting termination of process $PID and all its children"
echo "============================================"
# Try graceful termination first
echo "Attempting graceful termination (SIGTERM) of PID: $PID"
kill -15 "$PID" 2>/dev/null || true
sleep 1
# If still running, fall back to kill_children
if ps -p $PID > /dev/null; then
echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its children and itself"
else
echo "Process $PID exited cleanly after SIGTERM"
exit 0
fi
# Find and kill all child processes
kill_children "$PID"

View File

@@ -42,6 +42,20 @@ echo "============================================"
echo "Starting termination of process $PID and all its descendants"
echo "============================================"
# Try graceful termination first
echo "Attempting graceful termination (SIGTERM) of PID: $PID"
kill -15 "$PID" 2>/dev/null || true
sleep 1
# If still running, fall back to kill_descendants
# Use the macOS-native 'kill -0' check
if kill -0 $PID 2>/dev/null; then
echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its descendants and itself"
else
echo "Process $PID exited cleanly after SIGTERM"
exit 0
fi
# Find and kill all descendant processes
kill_descendants "$PID"

View File

@@ -57,6 +57,9 @@
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.fr.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.hu.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
@@ -79,4 +82,4 @@
</EmbeddedResource>
</ItemGroup>
</Project>
</Project>

View File

@@ -94,8 +94,8 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.InitialConfiguration;
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
@@ -200,8 +200,8 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.InitialConfiguration;
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;

View File

@@ -87,7 +87,7 @@ public partial class CoreConfigV2rayService
}
var customOutboundsNode = new JsonArray();
foreach (var outbound in v2rayConfig.outbounds)
{
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
@@ -112,7 +112,7 @@ public partial class CoreConfigV2rayService
}
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
}
if (fullConfigTemplateNode["outbounds"] is JsonArray templateOutbounds)
{
foreach (var outbound in templateOutbounds)
@@ -120,7 +120,7 @@ public partial class CoreConfigV2rayService
customOutboundsNode.Add(outbound?.DeepClone());
}
}
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));

View File

@@ -347,8 +347,8 @@ public partial class CoreConfigV2rayService
if (obj is null)
{
List<string> servers = [];
string[] arrDNS = normalDNS.Split(',');
foreach (string str in arrDNS)
var arrDNS = normalDNS.Split(',');
foreach (var str in arrDNS)
{
servers.Add(str);
}

View File

@@ -48,7 +48,7 @@ public partial class CoreConfigV2rayService
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
{
string result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
var result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
if (result.IsNullOrEmpty())
{
return new();

View File

@@ -453,16 +453,16 @@ public partial class CoreConfigV2rayService
};
//request Host
string request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
string[] arrHost = host.Split(',');
string host2 = string.Join(",".AppendQuotes(), arrHost);
var request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
var arrHost = host.Split(',');
var host2 = string.Join(",".AppendQuotes(), arrHost);
request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
//Path
string pathHttp = @"/";
var pathHttp = @"/";
if (path.IsNotEmpty())
{
string[] arrPath = path.Split(',');
var arrPath = path.Split(',');
pathHttp = string.Join(",".AppendQuotes(), arrPath);
}
request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
@@ -623,10 +623,10 @@ public partial class CoreConfigV2rayService
// Cache for chain proxies to avoid duplicate generation
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
int prevIndex = 0; // Index for prev outbounds
var prevIndex = 0; // Index for prev outbounds
// Process nodes
int index = 0;
var index = 0;
foreach (var node in nodes)
{
index++;

View File

@@ -6,7 +6,7 @@ public partial class CoreConfigV2rayService
{
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
{
string tag = EInboundProtocol.api.ToString();
var tag = EInboundProtocol.api.ToString();
Metrics4Ray apiObj = new();
Policy4Ray policyObj = new();
SystemPolicy4Ray policySystemSetting = new();

View File

@@ -167,7 +167,7 @@ public class UpdateService
try
{
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
string filePath = string.Empty;
var filePath = string.Empty;
foreach (var name in coreInfo.CoreExes)
{
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
@@ -180,14 +180,14 @@ public class UpdateService
if (!File.Exists(filePath))
{
string msg = string.Format(ResUI.NotFoundCore, @"", "", "");
var msg = string.Format(ResUI.NotFoundCore, @"", "", "");
//ShowMsg(true, msg);
return new SemanticVersion("");
}
var result = await Utils.GetCliWrapOutput(filePath, coreInfo.VersionArg);
var echo = result ?? "";
string version = string.Empty;
var version = string.Empty;
switch (type)
{
case ECoreType.v2fly:

View File

@@ -211,7 +211,7 @@ public class ClashProxiesViewModel : MyReactiveObject
}
//from api
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies)
foreach (var kv in _proxies)
{
if (!Global.allowSelectType.Contains(kv.Value.type.ToLower()))
{
@@ -319,7 +319,7 @@ public class ClashProxiesViewModel : MyReactiveObject
//from providers
if (_providers != null)
{
foreach (KeyValuePair<string, ProvidersItem> kv in _providers)
foreach (var kv in _providers)
{
if (Global.proxyVehicleType.Contains(kv.Value.vehicleType.ToLower()))
{

View File

@@ -273,12 +273,12 @@ public class OptionSettingViewModel : MyReactiveObject
NoticeManager.Instance.Enqueue(ResUI.FillLocalListeningPort);
return;
}
var needReboot = (EnableStatistics != _config.GuiItem.EnableStatistics
var needReboot = EnableStatistics != _config.GuiItem.EnableStatistics
|| DisplayRealTimeSpeed != _config.GuiItem.DisplayRealTimeSpeed
|| EnableDragDropSort != _config.UiItem.EnableDragDropSort
|| EnableHWA != _config.GuiItem.EnableHWA
|| CurrentFontFamily != _config.UiItem.CurrentFontFamily
|| MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation);
|| MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation;
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
@@ -375,7 +375,7 @@ public class OptionSettingViewModel : MyReactiveObject
private async Task SaveCoreType()
{
for (int k = 1; k <= _config.CoreTypeItem.Count; k++)
for (var k = 1; k <= _config.CoreTypeItem.Count; k++)
{
var item = _config.CoreTypeItem[k - 1];
var type = string.Empty;

View File

@@ -658,7 +658,7 @@ public class ProfilesViewModel : MyReactiveObject
}
_dicHeaderSort.TryAdd(colName, true);
_dicHeaderSort.TryGetValue(colName, out bool asc);
_dicHeaderSort.TryGetValue(colName, out var asc);
if (await ConfigHandler.SortServers(_config, _config.SubIndexId, colName, asc) != 0)
{
return;

View File

@@ -215,7 +215,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
private async Task SaveRoutingAsync()
{
string remarks = SelectedRouting.Remarks;
var remarks = SelectedRouting.Remarks;
if (remarks.IsNullOrEmpty())
{
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
@@ -286,7 +286,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
return;
}
DownloadService downloadHandle = new DownloadService();
var downloadHandle = new DownloadService();
var result = await downloadHandle.TryDownloadString(url, true, "");
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result);
if (ret == 0)
@@ -298,7 +298,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
private async Task<int> AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData)
{
bool blReplace = false;
var blReplace = false;
if (await _updateView?.Invoke(EViewAction.AddBatchRoutingRulesYesNo, null) == false)
{
blReplace = true;

View File

@@ -313,10 +313,10 @@ public class StatusBarViewModel : MyReactiveObject
}
BlServers = true;
for (int k = 0; k < lstModel.Count; k++)
for (var k = 0; k < lstModel.Count; k++)
{
ProfileItem it = lstModel[k];
string name = it.GetSummary();
var name = it.GetSummary();
var item = new ComboItem() { ID = it.IndexId, Text = name };
Servers.Add(item);
@@ -394,10 +394,10 @@ public class StatusBarViewModel : MyReactiveObject
{
await SysProxyHandler.UpdateSysProxy(_config, false);
BlSystemProxyClear = (type == ESysProxyType.ForcedClear);
BlSystemProxySet = (type == ESysProxyType.ForcedChange);
BlSystemProxyNothing = (type == ESysProxyType.Unchanged);
BlSystemProxyPac = (type == ESysProxyType.Pac);
BlSystemProxyClear = type == ESysProxyType.ForcedClear;
BlSystemProxySet = type == ESysProxyType.ForcedChange;
BlSystemProxyNothing = type == ESysProxyType.Unchanged;
BlSystemProxyPac = type == ESysProxyType.Pac;
if (blChange)
{

View File

@@ -10,15 +10,17 @@ public partial class App : Application
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
DataContext = StatusBarViewModel.Instance;
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
AppManager.Instance.InitComponents();
if (!Design.IsDesignMode)
{
AppManager.Instance.InitComponents();
DataContext = StatusBarViewModel.Instance;
}
desktop.Exit += OnExit;
desktop.MainWindow = new MainWindow();

View File

@@ -3,7 +3,7 @@ global using System.Collections.Generic;
global using System.Globalization;
global using System.IO;
global using System.Linq;
global using System.Reactive.Disposables;
global using System.Reactive.Disposables.Fluent;
global using System.Reactive.Linq;
global using System.Text;
global using System.Threading;
@@ -17,13 +17,13 @@ global using Avalonia.Markup.Xaml;
global using Avalonia.Media;
global using Avalonia.Media.Imaging;
global using Avalonia.Platform;
global using Avalonia.ReactiveUI;
global using Avalonia.Styling;
global using Avalonia.Threading;
global using ReactiveUI;
global using ReactiveUI.Fody.Helpers;
global using DynamicData;
global using MsBox.Avalonia.Enums;
global using ReactiveUI;
global using ReactiveUI.Avalonia;
global using ReactiveUI.Fody.Helpers;
global using ServiceLib;
global using ServiceLib.Base;
global using ServiceLib.Common;

View File

@@ -54,12 +54,19 @@ internal class Program
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
{
return AppBuilder.Configure<App>()
.UsePlatformDetect()
//.WithInterFont()
.WithFontByDefault()
.LogToTrace()
.UseReactiveUI()
.With(new MacOSPlatformOptions { ShowInDock = AppManager.Instance.Config.UiItem.MacOSShowInDock });
var builder = AppBuilder.Configure<App>()
.UsePlatformDetect()
//.WithInterFont()
.WithFontByDefault()
.LogToTrace()
.UseReactiveUI();
if (OperatingSystem.IsMacOS())
{
var showInDock = Design.IsDesignMode || AppManager.Instance.Config.UiItem.MacOSShowInDock;
builder = builder.With(new MacOSPlatformOptions { ShowInDock = showInDock });
}
return builder;
}
}

View File

@@ -13,8 +13,8 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
lstChild.SelectionChanged += LstChild_SelectionChanged;
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
@@ -32,11 +32,11 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
switch (profileItem.ConfigType)
{
case EConfigType.PolicyGroup:
this.Title = ResUI.TbConfigTypePolicyGroup;
Title = ResUI.TbConfigTypePolicyGroup;
break;
case EConfigType.ProxyChain:
this.Title = ResUI.TbConfigTypeProxyChain;
Title = ResUI.TbConfigTypeProxyChain;
gridPolicyGroup.IsVisible = false;
break;
}
@@ -64,7 +64,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
menuSelectAllChild.Click += (s, e) => lstChild.SelectAll();
// Keyboard shortcuts when focus is within grid
this.AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
lstChild.LoadingRow += LstChild_LoadingRow;
}
@@ -78,7 +78,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -14,8 +14,8 @@ public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
@@ -39,7 +39,7 @@ public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
case EViewAction.BrowseServer:

View File

@@ -13,8 +13,8 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
@@ -196,7 +196,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
this.Title = $"{profileItem.ConfigType}";
Title = $"{profileItem.ConfigType}";
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
@@ -204,7 +204,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -7,7 +7,7 @@ public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewMode
InitializeComponent();
ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
this.KeyDown += ClashProxiesView_KeyDown;
KeyDown += ClashProxiesView_KeyDown;
this.WhenActivated(disposables =>
{

View File

@@ -12,7 +12,7 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
_config = AppManager.Instance.Config;
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
btnCancel.Click += (s, e) => Close();
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms;
@@ -77,7 +77,7 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -12,7 +12,7 @@ public partial class FullConfigTemplateWindow : WindowBase<FullConfigTemplateVie
_config = AppManager.Instance.Config;
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
btnCancel.Click += (s, e) => Close();
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
@@ -36,7 +36,7 @@ public partial class FullConfigTemplateWindow : WindowBase<FullConfigTemplateVie
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -17,8 +17,8 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
HotkeyManager.Instance.IsPause = true;
Loaded += Window_Loaded;
this.Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
btnCancel.Click += (s, e) => this.Close();
Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
btnCancel.Click += (s, e) => Close();
this.WhenActivated(disposables =>
{
@@ -34,7 +34,7 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -21,7 +21,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
_config = AppManager.Instance.Config;
_manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.TopRight };
this.KeyDown += MainWindow_KeyDown;
KeyDown += MainWindow_KeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click;
menuCheckUpdate.Click += MenuCheckUpdate_Click;
@@ -153,14 +153,14 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
if (Utils.IsWindows())
{
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
HotkeyManager.Instance.Init(_config, OnHotkeyHandler);
}
else
{
this.Title = $"{Utils.GetVersion()}";
Title = $"{Utils.GetVersion()}";
menuRebootAsAdmin.IsVisible = false;
menuSettingsSetUWP.IsVisible = false;
@@ -168,9 +168,9 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
}
menuAddServerViaScan.IsVisible = false;
if (_config.UiItem.AutoHideStartup)
if (_config.UiItem.AutoHideStartup && Utils.IsWindows())
{
this.WindowState = WindowState.Minimized;
WindowState = WindowState.Minimized;
}
AddHelpMenuItem();
@@ -402,32 +402,32 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
public void ShowHideWindow(bool? blShow)
{
var bl = blShow ??
Utils.IsLinux()
(Utils.IsLinux()
? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized))
: !_config.UiItem.ShowInTaskbar;
: !_config.UiItem.ShowInTaskbar);
if (bl)
{
this.Show();
if (this.WindowState == WindowState.Minimized)
Show();
if (WindowState == WindowState.Minimized)
{
this.WindowState = WindowState.Normal;
WindowState = WindowState.Normal;
}
this.Activate();
this.Focus();
Activate();
Focus();
}
else
{
if (Utils.IsLinux() && _config.UiItem.Hide2TrayWhenClose == false)
{
this.WindowState = WindowState.Minimized;
WindowState = WindowState.Minimized;
return;
}
foreach (var ownedWindow in this.OwnedWindows)
foreach (var ownedWindow in OwnedWindows)
{
ownedWindow.Close();
}
this.Hide();
Hide();
}
_config.UiItem.ShowInTaskbar = bl;

View File

@@ -11,7 +11,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
InitializeComponent();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
btnCancel.Click += (s, e) => Close();
_config = AppManager.Instance.Config;
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
@@ -153,7 +153,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
case EViewAction.InitSettingFont:

View File

@@ -75,6 +75,15 @@
<PathIcon Data="{StaticResource SemiIconBolt}" />
</Button.Content>
</Button>
<Button
x:Name="menuMixedTestServer"
Margin="{StaticResource MarginLr4}"
Classes="IconButton Success"
ToolTip.Tip="{x:Static resx:ResUI.menuMixedTestServer}">
<Button.Content>
<PathIcon Data="{StaticResource building_ping}" />
</Button.Content>
</Button>
</WrapPanel>
<DataGrid
@@ -99,15 +108,12 @@
<MenuItem x:Name="menuCopyServer" Header="{x:Static resx:ResUI.menuCopyServer}" />
<MenuItem x:Name="menuRemoveServer" Header="{x:Static resx:ResUI.menuRemoveServer}" />
<MenuItem x:Name="menuRemoveDuplicateServer" Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
<MenuItem x:Name="menuRemoveInvalidServerResult" Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
<Separator />
<MenuItem x:Name="menuMixedTestServer" Header="{x:Static resx:ResUI.menuMixedTestServer}" />
<MenuItem x:Name="menuTcpingServer" Header="{x:Static resx:ResUI.menuTcpingServer}" />
<MenuItem x:Name="menuRealPingServer" Header="{x:Static resx:ResUI.menuRealPingServer}" />
<MenuItem x:Name="menuSpeedServer" Header="{x:Static resx:ResUI.menuSpeedServer}" />
<MenuItem Header="{x:Static resx:ResUI.menuTestServerResult}">
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
<MenuItem x:Name="menuRemoveInvalidServerResult" Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
</MenuItem>
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
<Separator />
<MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}">
<MenuItem>

View File

@@ -13,8 +13,8 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
@@ -61,7 +61,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -14,9 +14,9 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
this.KeyDown += RoutingRuleSettingWindow_KeyDown;
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
KeyDown += RoutingRuleSettingWindow_KeyDown;
lstRules.SelectionChanged += lstRules_SelectionChanged;
lstRules.DoubleTapped += LstRules_DoubleTapped;
menuRuleSelectAll.Click += menuRuleSelectAll_Click;
@@ -64,7 +64,7 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
case EViewAction.ShowYesNo:

View File

@@ -12,9 +12,9 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
InitializeComponent();
Loaded += Window_Loaded;
this.Closing += RoutingSettingWindow_Closing;
btnCancel.Click += (s, e) => this.Close();
this.KeyDown += RoutingSettingWindow_KeyDown;
Closing += RoutingSettingWindow_Closing;
btnCancel.Click += (s, e) => Close();
KeyDown += RoutingSettingWindow_KeyDown;
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.DoubleTapped += LstRoutings_DoubleTapped;
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
@@ -48,7 +48,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
case EViewAction.ShowYesNo:
@@ -116,7 +116,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
private void btnCancel_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
this.Close(ViewModel?.IsModified);
Close(ViewModel?.IsModified);
}
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)

View File

@@ -14,7 +14,7 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
InitializeComponent();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
btnCancel.Click += (s, e) => Close();
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
@@ -45,7 +45,7 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -14,8 +14,8 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
menuClose.Click += menuClose_Click;
Loaded += Window_Loaded;
this.Closing += SubSettingWindow_Closing;
this.KeyDown += SubSettingWindow_KeyDown;
Closing += SubSettingWindow_Closing;
KeyDown += SubSettingWindow_KeyDown;
ViewModel = new SubSettingViewModel(UpdateViewHandler);
lstSubscription.DoubleTapped += LstSubscription_DoubleTapped;
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
@@ -37,7 +37,7 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close();
Close();
break;
case EViewAction.ShowYesNo:
@@ -89,7 +89,7 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
private void menuClose_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
this.Close(ViewModel?.IsModified);
Close(ViewModel?.IsModified);
}
private void SubSettingWindow_Closing(object? sender, WindowClosingEventArgs e)

View File

@@ -9,7 +9,7 @@ public partial class SudoPasswordInputView : UserControl
{
InitializeComponent();
this.Loaded += (s, e) => txtPassword.Focus();
Loaded += (s, e) => txtPassword.Focus();
btnSave.Click += async (_, _) => await SavePasswordAsync();

View File

@@ -15,7 +15,7 @@
</PackageReference>
<PackageReference Include="Avalonia.Desktop" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" />
<PackageReference Include="Avalonia.ReactiveUI" />
<PackageReference Include="ReactiveUI.Avalonia" />
<PackageReference Include="MessageBox.Avalonia" />
<PackageReference Include="Semi.Avalonia" />
<PackageReference Include="Semi.Avalonia.AvaloniaEdit" />

View File

@@ -26,11 +26,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub Action", "GitHub Act
..\.github\workflows\build-osx.yml = ..\.github\workflows\build-osx.yml
..\.github\workflows\build-windows-desktop.yml = ..\.github\workflows\build-windows-desktop.yml
..\.github\workflows\build-windows.yml = ..\.github\workflows\build-windows.yml
..\package-appimage.sh = ..\package-appimage.sh
..\package-debian.sh = ..\package-debian.sh
..\package-osx.sh = ..\package-osx.sh
..\package-release-zip.sh = ..\package-release-zip.sh
..\pkg2appimage.yml = ..\pkg2appimage.yml
..\.github\workflows\winget-publish.yml = ..\.github\workflows\winget-publish.yml
EndProjectSection
EndProject

View File

@@ -6,11 +6,9 @@
<File Path="../.github/workflows/build-windows-desktop.yml" />
<File Path="../.github/workflows/build-windows.yml" />
<File Path="../.github/workflows/winget-publish.yml" />
<File Path="../package-appimage.sh" />
<File Path="../package-debian.sh" />
<File Path="../package-osx.sh" />
<File Path="../package-release-zip.sh" />
<File Path="../pkg2appimage.yml" />
</Folder>
<Folder Name="/Solution Files/">
<File Path="Directory.Build.props" />

View File

@@ -9,7 +9,7 @@ public partial class App : Application
public App()
{
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
DispatcherUnhandledException += App_DispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}
@@ -23,7 +23,7 @@ public partial class App : Application
var exePathKey = Utils.GetMd5(Utils.GetExePath());
var rebootas = (e.Args ?? Array.Empty<string>()).Any(t => t == Global.RebootAs);
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out bool bCreatedNew);
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out var bCreatedNew);
if (!rebootas && !bCreatedNew)
{
ProgramStarted.Set();

View File

@@ -2,9 +2,9 @@ using System.Drawing;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace v2rayN;
namespace v2rayN.Common;
public class QRCodeUtils
public class QRCodeWindowsUtils
{
public static ImageSource? GetQRCode(string? strContent)
{
@@ -14,7 +14,7 @@ public class QRCodeUtils
}
try
{
var qrCodeImage = ServiceLib.Common.QRCodeUtils.GenQRCode(strContent);
var qrCodeImage = QRCodeUtils.GenQRCode(strContent);
return qrCodeImage is null ? null : ByteToImage(qrCodeImage);
}
catch (Exception ex)

View File

@@ -1,6 +1,6 @@
using Microsoft.Win32;
namespace v2rayN;
namespace v2rayN.Common;
internal class UI
{

View File

@@ -3,7 +3,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
namespace v2rayN;
namespace v2rayN.Common;
internal static class WindowsUtils
{
@@ -40,13 +40,13 @@ internal static class WindowsUtils
}
[DllImport("dwmapi.dll")]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
public static extern int DwmSetWindowAttribute(nint hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
public static ImageSource IconToImageSource(Icon icon)
{
return Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
new System.Windows.Int32Rect(0, 0, icon.Width, icon.Height),
new Int32Rect(0, 0, icon.Width, icon.Height),
BitmapSizeOptions.FromEmptyOptions());
}
@@ -65,9 +65,9 @@ internal static class WindowsUtils
private static void SetDarkBorder(Window window, bool dark)
{
// Make sure the handle is created before the window is shown
IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
int attribute = dark ? 1 : 0;
uint attributeSize = (uint)Marshal.SizeOf(attribute);
var hWnd = new WindowInteropHelper(window).EnsureHandle();
var attribute = dark ? 1 : 0;
var attributeSize = (uint)Marshal.SizeOf(attribute);
DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, ref attribute, attributeSize);
DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref attribute, attributeSize);
}

View File

@@ -6,7 +6,7 @@ global using System.Diagnostics;
global using System.Globalization;
global using System.IO;
global using System.Linq;
global using System.Reactive.Disposables;
global using System.Reactive.Disposables.Fluent;
global using System.Reactive.Linq;
global using System.Runtime.InteropServices;
global using System.Text;
@@ -31,3 +31,4 @@ global using ServiceLib.Manager;
global using ServiceLib.Models;
global using ServiceLib.Resx;
global using ServiceLib.ViewModels;
global using v2rayN.Common;

View File

@@ -43,7 +43,7 @@ public sealed class HotkeyManager
modifiers |= KeyModifiers.Alt;
}
key = key << 16 | (int)modifiers;
key = (key << 16) | (int)modifiers;
if (!_hotkeyTriggerDic.ContainsKey(key))
{
_hotkeyTriggerDic.Add(key, new() { item.EGlobalHotkey });
@@ -103,7 +103,7 @@ public sealed class HotkeyManager
private (int fsModifiers, int vKey, string hotkeyStr, List<string> Names) GetHotkeyInfo(int hotkeyCode)
{
var fsModifiers = hotkeyCode & 0xffff;
var vKey = hotkeyCode >> 16 & 0xffff;
var vKey = (hotkeyCode >> 16) & 0xffff;
var hotkeyStr = new StringBuilder();
var names = new List<string>();

View File

@@ -60,18 +60,18 @@ public sealed class WindowsManager
return null;
}
Color color = ColorTranslator.FromHtml("#3399CC");
int index = (int)config.SystemProxyItem.SysProxyType;
var color = ColorTranslator.FromHtml("#3399CC");
var index = (int)config.SystemProxyItem.SysProxyType;
if (index > 0)
{
color = (new[] { Color.Red, Color.Purple, Color.DarkGreen, Color.Orange, Color.DarkSlateBlue, Color.RoyalBlue })[index - 1];
}
int width = 128;
int height = 128;
var width = 128;
var height = 128;
Bitmap bitmap = new(width, height);
Graphics graphics = Graphics.FromImage(bitmap);
var graphics = Graphics.FromImage(bitmap);
SolidBrush drawBrush = new(color);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
@@ -79,7 +79,7 @@ public sealed class WindowsManager
graphics.DrawImage(new Bitmap(item.CustomIcon), 0, 0, width, height);
graphics.FillEllipse(drawBrush, width / 2, width / 2, width / 2, width / 2);
Icon createdIcon = Icon.FromHandle(bitmap.GetHicon());
var createdIcon = Icon.FromHandle(bitmap.GetHicon());
drawBrush.Dispose();
graphics.Dispose();

View File

@@ -6,9 +6,9 @@ public partial class AddGroupServerWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
this.PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
lstChild.SelectionChanged += LstChild_SelectionChanged;
menuSelectAllChild.Click += MenuSelectAllChild_Click;
@@ -27,11 +27,11 @@ public partial class AddGroupServerWindow
switch (profileItem.ConfigType)
{
case EConfigType.PolicyGroup:
this.Title = ResUI.TbConfigTypePolicyGroup;
Title = ResUI.TbConfigTypePolicyGroup;
break;
case EConfigType.ProxyChain:
this.Title = ResUI.TbConfigTypeProxyChain;
Title = ResUI.TbConfigTypeProxyChain;
gridPolicyGroup.Visibility = Visibility.Collapsed;
break;
}
@@ -61,7 +61,7 @@ public partial class AddGroupServerWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -6,8 +6,8 @@ public partial class AddServer2Window
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
@@ -32,11 +32,11 @@ public partial class AddServer2Window
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.BrowseServer:
if (UI.OpenFileDialog(out string fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true)
if (UI.OpenFileDialog(out var fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true)
{
return false;
}

View File

@@ -8,8 +8,8 @@ public partial class AddServerWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
@@ -191,7 +191,7 @@ public partial class AddServerWindow
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
this.Title = $"{profileItem.ConfigType}";
Title = $"{profileItem.ConfigType}";
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
}
@@ -200,7 +200,7 @@ public partial class AddServerWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -28,7 +28,7 @@ public partial class BackupAndRestoreView
private void MenuLocalBackup_Click(object sender, RoutedEventArgs e)
{
if (UI.SaveFileDialog(out string fileName, "Zip|*.zip") != true)
if (UI.SaveFileDialog(out var fileName, "Zip|*.zip") != true)
{
return;
}
@@ -37,7 +37,7 @@ public partial class BackupAndRestoreView
private void MenuLocalRestore_Click(object sender, RoutedEventArgs e)
{
if (UI.OpenFileDialog(out string fileName, "Zip|*.zip|All|*.*") != true)
if (UI.OpenFileDialog(out var fileName, "Zip|*.zip|All|*.*") != true)
{
return;
}

View File

@@ -8,7 +8,7 @@ public partial class DNSSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
_config = AppManager.Instance.Config;
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
@@ -78,7 +78,7 @@ public partial class DNSSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -8,7 +8,7 @@ public partial class FullConfigTemplateWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
_config = AppManager.Instance.Config;
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
@@ -35,7 +35,7 @@ public partial class FullConfigTemplateWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -11,14 +11,14 @@ public partial class GlobalHotkeySettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler);
btnReset.Click += btnReset_Click;
HotkeyManager.Instance.IsPause = true;
this.Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
this.WhenActivated(disposables =>
{
@@ -35,7 +35,7 @@ public partial class GlobalHotkeySettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -19,8 +19,8 @@ public partial class MainWindow
ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false);
App.Current.SessionEnding += Current_SessionEnding;
this.Closing += MainWindow_Closing;
this.PreviewKeyDown += MainWindow_PreviewKeyDown;
Closing += MainWindow_Closing;
PreviewKeyDown += MainWindow_PreviewKeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click;
menuClose.Click += menuClose_Click;
@@ -150,10 +150,10 @@ public partial class MainWindow
.DisposeWith(disposables);
});
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
if (_config.UiItem.AutoHideStartup)
{
this.WindowState = WindowState.Minimized;
WindowState = WindowState.Minimized;
}
if (!_config.GuiItem.EnableHWA)
@@ -187,35 +187,35 @@ public partial class MainWindow
case EViewAction.AddServerWindow:
if (obj is null)
return false;
return (new AddServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
return new AddServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddServer2Window:
if (obj is null)
return false;
return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
return new AddServer2Window((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddGroupServerWindow:
if (obj is null)
return false;
return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
return new AddGroupServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.DNSSettingWindow:
return (new DNSSettingWindow().ShowDialog() ?? false);
return new DNSSettingWindow().ShowDialog() ?? false;
case EViewAction.RoutingSettingWindow:
return (new RoutingSettingWindow().ShowDialog() ?? false);
return new RoutingSettingWindow().ShowDialog() ?? false;
case EViewAction.OptionSettingWindow:
return (new OptionSettingWindow().ShowDialog() ?? false);
return new OptionSettingWindow().ShowDialog() ?? false;
case EViewAction.FullConfigTemplateWindow:
return (new FullConfigTemplateWindow().ShowDialog() ?? false);
return new FullConfigTemplateWindow().ShowDialog() ?? false;
case EViewAction.GlobalHotkeySettingWindow:
return (new GlobalHotkeySettingWindow().ShowDialog() ?? false);
return new GlobalHotkeySettingWindow().ShowDialog() ?? false;
case EViewAction.SubSettingWindow:
return (new SubSettingWindow().ShowDialog() ?? false);
return new SubSettingWindow().ShowDialog() ?? false;
case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync();
@@ -328,7 +328,7 @@ public partial class MainWindow
if (Application.Current?.MainWindow is Window window)
{
var bytes = QRCodeUtils.CaptureScreen(window);
var bytes = QRCodeWindowsUtils.CaptureScreen(window);
await ViewModel?.ScanScreenResult(bytes);
}
@@ -372,7 +372,7 @@ public partial class MainWindow
this?.Show();
if (this?.WindowState == WindowState.Minimized)
{
this.WindowState = WindowState.Normal;
WindowState = WindowState.Normal;
}
this?.Activate();
this?.Focus();

View File

@@ -31,10 +31,10 @@ public partial class MsgView
case EViewAction.DispatcherShowMsg:
if (obj is null)
return false;
Application.Current?.Dispatcher.Invoke((() =>
Application.Current?.Dispatcher.Invoke(() =>
{
ShowMsg(obj);
}), DispatcherPriority.ApplicationIdle);
}, DispatcherPriority.ApplicationIdle);
break;
}
return await Task.FromResult(true);

View File

@@ -10,7 +10,7 @@ public partial class OptionSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
_config = AppManager.Instance.Config;
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
@@ -136,7 +136,7 @@ public partial class OptionSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.InitSettingFont:
@@ -168,12 +168,12 @@ public partial class OptionSettingWindow
foreach (var ttf in files)
{
var families = Fonts.GetFontFamilies(Utils.GetFontsPath(ttf));
foreach (FontFamily family in families)
foreach (var family in families)
{
var typefaces = family.GetTypefaces();
foreach (Typeface typeface in typefaces)
foreach (var typeface in typefaces)
{
typeface.TryGetGlyphTypeface(out GlyphTypeface glyph);
typeface.TryGetGlyphTypeface(out var glyph);
//var fontFace = glyph.Win32FaceNames[new CultureInfo("en-us")];
//if (!fontFace.Equals("Regular") && !fontFace.Equals("Normal"))
//{

View File

@@ -71,7 +71,7 @@ public partial class ProfilesSelectWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -87,6 +87,15 @@
ToolTip="{x:Static resx:ResUI.menuFastRealPing}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="LightningBolt" />
</Button>
<Button
x:Name="menuMixedTestServer"
Width="30"
Height="30"
Margin="{StaticResource MarginLeftRight4}"
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
ToolTip="{x:Static resx:ResUI.menuMixedTestServer}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Speedometer" />
</Button>
</WrapPanel>
<DataGrid
x:Name="lstProfiles"
@@ -131,11 +140,11 @@
x:Name="menuRemoveDuplicateServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
<Separator />
<MenuItem
x:Name="menuMixedTestServer"
x:Name="menuRemoveInvalidServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMixedTestServer}" />
Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
<Separator />
<MenuItem
x:Name="menuTcpingServer"
Height="{StaticResource MenuItemHeight}"
@@ -148,16 +157,10 @@
x:Name="menuSpeedServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSpeedServer}" />
<MenuItem Header="{x:Static resx:ResUI.menuTestServerResult}">
<MenuItem
x:Name="menuSortServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSortServerResult}" />
<MenuItem
x:Name="menuRemoveInvalidServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
</MenuItem>
<MenuItem
x:Name="menuSortServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSortServerResult}" />
<Separator />
<MenuItem
x:Name="menuMoveToGroup"

View File

@@ -127,7 +127,7 @@ public partial class ProfilesView
case EViewAction.SaveFileDialog:
if (obj is null)
return false;
if (UI.SaveFileDialog(out string fileName, "Config|*.json") != true)
if (UI.SaveFileDialog(out var fileName, "Config|*.json") != true)
{
return false;
}
@@ -137,17 +137,17 @@ public partial class ProfilesView
case EViewAction.AddServerWindow:
if (obj is null)
return false;
return (new AddServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
return new AddServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddServer2Window:
if (obj is null)
return false;
return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
return new AddServer2Window((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddGroupServerWindow:
if (obj is null)
return false;
return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
return new AddGroupServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.ShareServer:
if (obj is null)
@@ -158,7 +158,7 @@ public partial class ProfilesView
case EViewAction.SubEditWindow:
if (obj is null)
return false;
return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false;
return new SubEditWindow((SubItem)obj).ShowDialog() ?? false;
case EViewAction.DispatcherRefreshServersBiz:
Application.Current?.Dispatcher.Invoke(RefreshServersBiz, DispatcherPriority.Normal);
@@ -170,7 +170,7 @@ public partial class ProfilesView
public async void ShareServer(string url)
{
var img = QRCodeUtils.GetQRCode(url);
var img = QRCodeWindowsUtils.GetQRCode(url);
var dialog = new QrcodeView()
{
imgQrcode = { Source = img },
@@ -415,8 +415,8 @@ public partial class ProfilesView
private void LstProfiles_MouseMove(object sender, MouseEventArgs e)
{
// Get the current mouse position
Point mousePos = e.GetPosition(null);
Vector diff = startPoint - mousePos;
var mousePos = e.GetPosition(null);
var diff = startPoint - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
@@ -429,7 +429,7 @@ public partial class ProfilesView
if (listViewItem == null)
return; // Abort
// Find the data behind the ListViewItem
ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
var item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
if (item == null)
return; // Abort
// Initialize the drag & drop operation
@@ -462,7 +462,7 @@ public partial class ProfilesView
return;
}
// Find the data behind the Item
ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
var item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
if (item == null)
return;
// Move item into observable collection

View File

@@ -6,8 +6,8 @@ public partial class RoutingRuleDetailsWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
@@ -54,7 +54,7 @@ public partial class RoutingRuleDetailsWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -6,9 +6,9 @@ public partial class RoutingRuleSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
this.PreviewKeyDown += RoutingRuleSettingWindow_PreviewKeyDown;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
PreviewKeyDown += RoutingRuleSettingWindow_PreviewKeyDown;
lstRules.SelectionChanged += lstRules_SelectionChanged;
lstRules.MouseDoubleClick += LstRules_MouseDoubleClick;
menuRuleSelectAll.Click += menuRuleSelectAll_Click;
@@ -57,7 +57,7 @@ public partial class RoutingRuleSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.ShowYesNo:
@@ -80,11 +80,11 @@ public partial class RoutingRuleSettingWindow
if (obj is null)
return false;
return (new RoutingRuleDetailsWindow((RulesItem)obj)).ShowDialog() ?? false;
return new RoutingRuleDetailsWindow((RulesItem)obj).ShowDialog() ?? false;
case EViewAction.ImportRulesFromFile:
if (UI.OpenFileDialog(out string fileName, "Rules|*.json|All|*.*") != true)
if (UI.OpenFileDialog(out var fileName, "Rules|*.json|All|*.*") != true)
{
return false;
}
@@ -174,7 +174,7 @@ public partial class RoutingRuleSettingWindow
private void btnBrowseCustomIcon_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (UI.OpenFileDialog(out string fileName,
if (UI.OpenFileDialog(out var fileName,
"PNG,ICO|*.png;*.ico") != true)
{
return;
@@ -185,7 +185,7 @@ public partial class RoutingRuleSettingWindow
private void btnBrowseCustomRulesetPath4Singbox_Click(object sender, RoutedEventArgs e)
{
if (UI.OpenFileDialog(out string fileName,
if (UI.OpenFileDialog(out var fileName,
"Config|*.json|All|*.*") != true)
{
return;

View File

@@ -6,9 +6,9 @@ public partial class RoutingSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Closing += RoutingSettingWindow_Closing;
this.PreviewKeyDown += RoutingSettingWindow_PreviewKeyDown;
Owner = Application.Current.MainWindow;
Closing += RoutingSettingWindow_Closing;
PreviewKeyDown += RoutingSettingWindow_PreviewKeyDown;
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.MouseDoubleClick += LstRoutings_MouseDoubleClick;
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
@@ -44,7 +44,7 @@ public partial class RoutingSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.ShowYesNo:
@@ -58,7 +58,7 @@ public partial class RoutingSettingWindow
if (obj is null)
return false;
return (new RoutingRuleSettingWindow((RoutingItem)obj)).ShowDialog() ?? false;
return new RoutingRuleSettingWindow((RoutingItem)obj).ShowDialog() ?? false;
}
return await Task.FromResult(true);
}
@@ -67,7 +67,7 @@ public partial class RoutingSettingWindow
{
if (ViewModel?.IsModified == true)
{
this.DialogResult = true;
DialogResult = true;
}
}
@@ -122,11 +122,11 @@ public partial class RoutingSettingWindow
{
if (ViewModel?.IsModified == true)
{
this.DialogResult = true;
DialogResult = true;
}
else
{
this.Close();
Close();
}
}
}

View File

@@ -71,11 +71,11 @@ public partial class StatusBarView
switch (action)
{
case EViewAction.DispatcherRefreshIcon:
Application.Current?.Dispatcher.Invoke((async () =>
Application.Current?.Dispatcher.Invoke(async () =>
{
tbNotify.Icon = await WindowsManager.Instance.GetNotifyIcon(_config);
Application.Current.MainWindow.Icon = WindowsManager.Instance.GetAppIcon(_config);
}), DispatcherPriority.Normal);
}, DispatcherPriority.Normal);
break;
case EViewAction.SetClipboardData:

View File

@@ -6,8 +6,8 @@ public partial class SubEditWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
@@ -39,7 +39,7 @@ public partial class SubEditWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -8,10 +8,10 @@ public partial class SubSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
ViewModel = new SubSettingViewModel(UpdateViewHandler);
this.Closing += SubSettingWindow_Closing;
Closing += SubSettingWindow_Closing;
lstSubscription.MouseDoubleClick += LstSubscription_MouseDoubleClick;
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
menuClose.Click += menuClose_Click;
@@ -34,7 +34,7 @@ public partial class SubSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.ShowYesNo:
@@ -47,7 +47,7 @@ public partial class SubSettingWindow
case EViewAction.SubEditWindow:
if (obj is null)
return false;
return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false;
return new SubEditWindow((SubItem)obj).ShowDialog() ?? false;
case EViewAction.ShareSub:
if (obj is null)
@@ -64,7 +64,7 @@ public partial class SubSettingWindow
{
return;
}
var img = QRCodeUtils.GetQRCode(url);
var img = QRCodeWindowsUtils.GetQRCode(url);
var dialog = new QrcodeView()
{
imgQrcode = { Source = img },
@@ -78,7 +78,7 @@ public partial class SubSettingWindow
{
if (ViewModel?.IsModified == true)
{
this.DialogResult = true;
DialogResult = true;
}
}
@@ -99,11 +99,11 @@ public partial class SubSettingWindow
{
if (ViewModel?.IsModified == true)
{
this.DialogResult = true;
DialogResult = true;
}
else
{
this.Close();
Close();
}
}
}