mirror of
https://github.com/2dust/v2rayN.git
synced 2025-08-23 19:36:55 +00:00
Compare commits
4 commits
7b7fe0ef46
...
e104f9f9b2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e104f9f9b2 | ||
![]() |
876381a7fb | ||
![]() |
4f711b1bd3 | ||
![]() |
89893c0945 |
34 changed files with 1351 additions and 732 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -397,4 +397,5 @@ FodyWeavers.xsd
|
|||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
|
511
package-rhel.sh
Normal file
511
package-rhel.sh
Normal file
|
@ -0,0 +1,511 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ===== Require Red Hat Enterprise Linux / Rocky Linux / AlmaLinux / CentOS Stream =======
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
case "$ID" in
|
||||
rhel|rocky|almalinux|centos)
|
||||
echo "[OK] Detected supported system: $NAME $VERSION_ID"
|
||||
;;
|
||||
*)
|
||||
echo "[ERROR] Unsupported system: $NAME ($ID)."
|
||||
echo "This script only supports Red Hat Enterprise Linux / Rocky Linux / AlmaLinux / CentOS Stream."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
echo "[ERROR] Cannot detect system (missing /etc/os-release)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ===== 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
|
||||
AUTOSTART=0 # 1 = enable system-wide autostart (/etc/xdg/autostart)
|
||||
FORCE_NETCORE=0 # --netcore => skip archive bundle, use separate downloads
|
||||
|
||||
# If the first argument starts with --, don’t treat it as version number
|
||||
if [[ "${VERSION_ARG:-}" == --* ]]; then
|
||||
VERSION_ARG=""
|
||||
fi
|
||||
# Take the first non --* argument as version, discard it
|
||||
if [[ -n "${VERSION_ARG:-}" ]]; then shift || true; fi
|
||||
|
||||
# Parse remaining optional arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--with-core) WITH_CORE="${2:-both}"; shift 2;;
|
||||
--autostart) AUTOSTART=1; shift;;
|
||||
--xray-ver) XRAY_VER="${2:-}"; shift 2;; # Specify xray version (optional)
|
||||
--singbox-ver) SING_VER="${2:-}"; shift 2;; # Specify sing-box version (optional)
|
||||
--netcore) FORCE_NETCORE=1; shift;; # NEW: force old mode (no bundle zip)
|
||||
*)
|
||||
if [[ -z "${VERSION_ARG:-}" ]]; then VERSION_ARG="$1"; fi
|
||||
shift;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ===== Environment check ===============================================================
|
||||
arch="$(uname -m)"
|
||||
[[ "$arch" == "aarch64" || "$arch" == "x86_64" ]] || { echo "Only supports aarch64 / x86_64"; exit 1; }
|
||||
|
||||
# Dependencies (packaging shouldn’t be run as root, but this line needs sudo)
|
||||
sudo dnf -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar || sudo dnf -y install dotnet-sdk
|
||||
command -v curl >/dev/null
|
||||
|
||||
# Root directory = the script's location
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Git submodules (tolerant)
|
||||
if [[ -f .gitmodules ]]; then
|
||||
git submodule sync --recursive || true
|
||||
git submodule update --init --recursive || true
|
||||
fi
|
||||
|
||||
# ===== Locate project ================================================================
|
||||
PROJECT="v2rayN.Desktop/v2rayN.Desktop.csproj"
|
||||
if [[ ! -f "$PROJECT" ]]; then
|
||||
PROJECT="$(find . -maxdepth 3 -name 'v2rayN.Desktop.csproj' | head -n1 || true)"
|
||||
fi
|
||||
[[ -f "$PROJECT" ]] || { echo "v2rayN.Desktop.csproj not found"; exit 1; }
|
||||
|
||||
# ===== Resolve GUI version & auto checkout ============================================
|
||||
# Rules:
|
||||
# - If VERSION_ARG provided: try to checkout that tag (vX.Y.Z or X.Y.Z). If not found, ask which channel (Latest vs Pre-release),
|
||||
# default to Latest, then fetch the chosen channel's latest tag and checkout.
|
||||
# - If VERSION_ARG not provided: ask the channel first (default Latest), then checkout to that tag.
|
||||
# - If not a git repo, warn and continue without switching (keep current branch).
|
||||
VERSION="" # final GUI version string without 'v' prefix
|
||||
|
||||
choose_channel() {
|
||||
# Print menu to stderr first, then read from stdin; only echo the chosen token to stdout.
|
||||
local ch="latest" sel=""
|
||||
if [[ -t 0 ]]; then
|
||||
>&2 echo "[?] Choose v2rayN release channel:"
|
||||
>&2 echo " 1) Latest (stable) [default]"
|
||||
>&2 echo " 2) Pre-release (preview)"
|
||||
read -r -p "Enter 1 or 2 (default 1): " sel
|
||||
case "${sel:-}" in
|
||||
2) ch="prerelease" ;;
|
||||
*) ch="latest" ;;
|
||||
esac
|
||||
else
|
||||
ch="latest"
|
||||
fi
|
||||
echo "$ch"
|
||||
}
|
||||
|
||||
get_latest_tag_latest() {
|
||||
# Use GitHub API: /releases/latest → tag_name
|
||||
curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases/latest" \
|
||||
| grep -Eo '"tag_name":\s*"v?[^"]+"' \
|
||||
| head -n1 \
|
||||
| sed -E 's/.*"tag_name":\s*"v?([^"]+)".*/\1/'
|
||||
}
|
||||
|
||||
get_latest_tag_prerelease() {
|
||||
# Use GitHub API: /releases?per_page=20 and pick the newest prerelease=true's tag_name
|
||||
local json
|
||||
json="$(curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases?per_page=20")" || return 1
|
||||
echo "$json" \
|
||||
| awk -v RS='},' '/"prerelease":[[:space:]]*true/ { if (match($0, /"tag_name":[[:space:]]*"v?[^"]+"/, m)) { t=m[0]; sub(/.*"tag_name":[[:space:]]*"?v?/, "", t); sub(/".*/, "", t); print t; exit } }'
|
||||
}
|
||||
|
||||
git_try_checkout() {
|
||||
# Try a series of refs and checkout when found.
|
||||
# Args: version-like string (may contain leading 'v')
|
||||
local want="$1" ref=""
|
||||
if git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
git fetch --tags --force --prune --depth=1 || true
|
||||
if git rev-parse "refs/tags/v${want}" >/dev/null 2>&1; then
|
||||
ref="v${want}"
|
||||
elif git rev-parse "refs/tags/${want}" >/dev/null 2>&1; then
|
||||
ref="${want}"
|
||||
elif git rev-parse --verify "${want}" >/dev/null 2>&1; then
|
||||
ref="${want}"
|
||||
fi
|
||||
if [[ -n "$ref" ]]; then
|
||||
echo "[OK] Found ref '${ref}', checking out..."
|
||||
git checkout -f "${ref}"
|
||||
if [[ -f .gitmodules ]]; then
|
||||
git submodule sync --recursive || true
|
||||
git submodule update --init --recursive || true
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
if git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
if [[ -n "${VERSION_ARG:-}" ]]; then
|
||||
echo "[*] Trying to switch v2rayN repo to version: ${VERSION_ARG}"
|
||||
if git_try_checkout "${VERSION_ARG#v}"; then
|
||||
VERSION="${VERSION_ARG#v}"
|
||||
else
|
||||
echo "[WARN] Tag '${VERSION_ARG}' not found."
|
||||
ch="$(choose_channel)"
|
||||
echo "[*] Resolving ${ch} tag from GitHub releases..."
|
||||
tag=""
|
||||
if [[ "$ch" == "prerelease" ]]; then
|
||||
tag="$(get_latest_tag_prerelease || true)"
|
||||
else
|
||||
tag="$(get_latest_tag_latest || true)"
|
||||
fi
|
||||
[[ -n "$tag" ]] || { echo "[ERROR] Failed to resolve latest tag for channel '${ch}'."; exit 1; }
|
||||
echo "[*] Latest tag for '${ch}': ${tag}"
|
||||
git_try_checkout "$tag" || { echo "[ERROR] Failed to checkout '${tag}'."; exit 1; }
|
||||
VERSION="${tag#v}"
|
||||
fi
|
||||
else
|
||||
# No explicit GUI version passed: ask channel first
|
||||
ch="$(choose_channel)"
|
||||
echo "[*] Resolving ${ch} tag from GitHub releases..."
|
||||
tag=""
|
||||
if [[ "$ch" == "prerelease" ]]; then
|
||||
tag="$(get_latest_tag_prerelease || true)"
|
||||
else
|
||||
tag="$(get_latest_tag_latest || true)"
|
||||
fi
|
||||
[[ -n "$tag" ]] || { echo "[ERROR] Failed to resolve latest tag for channel '${ch}'."; exit 1; }
|
||||
echo "[*] Latest tag for '${ch}': ${tag}"
|
||||
git_try_checkout "$tag" || { echo "[ERROR] Failed to checkout '${tag}'."; exit 1; }
|
||||
VERSION="${tag#v}"
|
||||
fi
|
||||
else
|
||||
echo "[WARN] Current directory is not a git repo; cannot checkout version. Proceeding on current tree."
|
||||
VERSION="${VERSION_ARG:-}"
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
|
||||
VERSION="$(git describe --tags --abbrev=0)"
|
||||
else
|
||||
VERSION="0.0.0+git"
|
||||
fi
|
||||
fi
|
||||
VERSION="${VERSION#v}"
|
||||
fi
|
||||
echo "[*] GUI version resolved as: ${VERSION}"
|
||||
|
||||
# ===== .NET publish (non-single file, self-contained) ===========================================
|
||||
dotnet clean "$PROJECT" -c Release
|
||||
rm -rf "$(dirname "$PROJECT")/bin/Release/net8.0" || true
|
||||
|
||||
dotnet restore "$PROJECT"
|
||||
dotnet publish "$PROJECT" \
|
||||
-c Release -r "$( [[ "$arch" == "aarch64" ]] && echo linux-arm64 || echo linux-x64 )" \
|
||||
-p:PublishSingleFile=false \
|
||||
-p:SelfContained=true \
|
||||
-p:IncludeNativeLibrariesForSelfExtract=true
|
||||
|
||||
RID_DIR="$( [[ "$arch" == "aarch64" ]] && echo linux-arm64 || echo linux-x64 )"
|
||||
PUBDIR="$(dirname "$PROJECT")/bin/Release/net8.0/${RID_DIR}/publish"
|
||||
[[ -d "$PUBDIR" ]]
|
||||
|
||||
# ===== Download Core(Optional) ========================================================
|
||||
download_xray() {
|
||||
local outdir="$1" ver="${XRAY_VER:-}" url tmp zipname="xray.zip"
|
||||
mkdir -p "$outdir"
|
||||
if [[ -z "$ver" ]]; then
|
||||
# Latest version
|
||||
ver="$(curl -fsSL https://api.github.com/repos/XTLS/Xray-core/releases/latest \
|
||||
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
|
||||
fi
|
||||
[[ -n "$ver" ]] || { echo "[xray] Failed to get version"; return 1; }
|
||||
|
||||
if [[ "$arch" == "aarch64" ]]; then
|
||||
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-arm64-v8a.zip"
|
||||
else
|
||||
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-64.zip"
|
||||
fi
|
||||
echo "[+] Download xray: $url"
|
||||
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' RETURN
|
||||
curl -fL "$url" -o "$tmp/$zipname"
|
||||
unzip -q "$tmp/$zipname" -d "$tmp"
|
||||
install -Dm755 "$tmp/xray" "$outdir/xray"
|
||||
}
|
||||
|
||||
download_singbox() {
|
||||
local outdir="$1" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
|
||||
mkdir -p "$outdir"
|
||||
if [[ -z "$ver" ]]; then
|
||||
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest \
|
||||
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
|
||||
fi
|
||||
[[ -n "$ver" ]] || { echo "[sing-box] Failed to get version"; return 1; }
|
||||
|
||||
if [[ "$arch" == "aarch64" ]]; then
|
||||
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-arm64.tar.gz"
|
||||
else
|
||||
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-amd64.tar.gz"
|
||||
fi
|
||||
echo "[+] Download sing-box: $url"
|
||||
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' RETURN
|
||||
curl -fL "$url" -o "$tmp/$tarname"
|
||||
tar -C "$tmp" -xzf "$tmp/$tarname"
|
||||
bin="$(find "$tmp" -type f -name 'sing-box' | head -n1 || true)"
|
||||
[[ -n "$bin" ]] || { echo "[!] sing-box unpack failed"; return 1; }
|
||||
install -Dm755 "$bin" "$outdir/sing-box"
|
||||
}
|
||||
|
||||
# === Geo rule download (ZIP-LIKE LAYOUT for netcore mode) =====================
|
||||
# Make netcore output match the ZIP bundle structure:
|
||||
# - All geo databases at outroot/bin/ (geosite.dat, geoip.dat, Country.mmdb, geoip-only-cn-private.dat, geoip.metadb)
|
||||
# - All *.srs rule-sets at outroot/bin/srss/
|
||||
# (Binaries stay under outroot/bin/xray and outroot/bin/sing_box as before.)
|
||||
download_geo_assets() {
|
||||
local outroot="$1"
|
||||
local bin_dir="$outroot/bin"
|
||||
local srss_dir="$bin_dir/srss"
|
||||
mkdir -p "$bin_dir" "$srss_dir"
|
||||
|
||||
echo "[+] Download Xray Geo (geosite/geoip/...) to ${bin_dir}"
|
||||
curl -fsSL -o "$bin_dir/geosite.dat" \
|
||||
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geosite.dat"
|
||||
curl -fsSL -o "$bin_dir/geoip.dat" \
|
||||
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geoip.dat"
|
||||
curl -fsSL -o "$bin_dir/geoip-only-cn-private.dat" \
|
||||
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat"
|
||||
curl -fsSL -o "$bin_dir/Country.mmdb" \
|
||||
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb"
|
||||
|
||||
echo "[+] Download sing-box rule DB & rule-sets to ZIP-like paths"
|
||||
curl -fsSL -o "$bin_dir/geoip.metadb" \
|
||||
"https://github.com/MetaCubeX/meta-rules-dat/releases/latest/download/geoip.metadb" || true
|
||||
|
||||
for f in \
|
||||
geoip-private.srs geoip-cn.srs geoip-facebook.srs geoip-fastly.srs \
|
||||
geoip-google.srs geoip-netflix.srs geoip-telegram.srs geoip-twitter.srs; do
|
||||
curl -fsSL -o "$srss_dir/$f" \
|
||||
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geoip/$f" || true
|
||||
done
|
||||
|
||||
for f in \
|
||||
geosite-cn.srs geosite-gfw.srs geosite-greatfire.srs \
|
||||
geosite-geolocation-cn.srs geosite-category-ads-all.srs geosite-private.srs; do
|
||||
curl -fsSL -o "$srss_dir/$f" \
|
||||
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true
|
||||
done
|
||||
}
|
||||
|
||||
# === NEW: Prefer the all-in-one v2rayN bundle zip first =======================
|
||||
download_v2rayn_bundle() {
|
||||
local outroot="$1"
|
||||
local url=""
|
||||
if [[ "$arch" == "aarch64" ]]; then
|
||||
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-arm64.zip"
|
||||
else
|
||||
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-64.zip"
|
||||
fi
|
||||
echo "[+] Try v2rayN bundle archive: $url"
|
||||
local tmp zipname
|
||||
tmp="$(mktemp -d)"; zipname="$tmp/v2rayn.zip"
|
||||
curl -fL "$url" -o "$zipname" || { echo "[!] Bundle download failed"; return 1; }
|
||||
unzip -q "$zipname" -d "$tmp" || { echo "[!] Bundle unzip failed"; return 1; }
|
||||
|
||||
# Normalize layout: copy 'bin/' if present; otherwise copy all
|
||||
if [[ -d "$tmp/bin" ]]; then
|
||||
mkdir -p "$outroot/bin"
|
||||
rsync -a "$tmp/bin/" "$outroot/bin/"
|
||||
else
|
||||
rsync -a "$tmp/" "$outroot/"
|
||||
fi
|
||||
|
||||
# --- CLEANUPS (keep bundle-only adjustments) -------------------------------
|
||||
rm -f "$outroot/v2rayn.zip" 2>/dev/null || true
|
||||
find "$outroot" -type d -name "mihomo" -prune -exec rm -rf {} + 2>/dev/null || true
|
||||
|
||||
local nested_dir
|
||||
nested_dir="$(find "$outroot" -maxdepth 1 -type d -name 'v2rayN-linux-*' | head -n1 || true)"
|
||||
if [[ -n "${nested_dir:-}" && -d "$nested_dir/bin" ]]; then
|
||||
mkdir -p "$outroot/bin"
|
||||
rsync -a "$nested_dir/bin/" "$outroot/bin/"
|
||||
rm -rf "$nested_dir"
|
||||
fi
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
echo "[+] Bundle extracted to $outroot"
|
||||
}
|
||||
|
||||
# ===== Copy publish files to RPM build root ==================================================
|
||||
rpmdev-setuptree
|
||||
TOPDIR="${HOME}/rpmbuild"
|
||||
SPECDIR="${TOPDIR}/SPECS"
|
||||
SOURCEDIR="${TOPDIR}/SOURCES"
|
||||
|
||||
PKGROOT="v2rayN-publish"
|
||||
WORKDIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
mkdir -p "$WORKDIR/$PKGROOT"
|
||||
cp -a "$PUBDIR/." "$WORKDIR/$PKGROOT/"
|
||||
|
||||
# icon(Optional)
|
||||
ICON_CANDIDATE="$(dirname "$PROJECT")/../v2rayN.Desktop/v2rayN.png"
|
||||
[[ -f "$ICON_CANDIDATE" ]] && cp "$ICON_CANDIDATE" "$WORKDIR/$PKGROOT/v2rayn.png" || true
|
||||
|
||||
# bin directory structure
|
||||
mkdir -p "$WORKDIR/$PKGROOT/bin/xray" "$WORKDIR/$PKGROOT/bin/sing_box"
|
||||
|
||||
# ====== NEW decision: prefer bundle zip unless --netcore, else fall back ======
|
||||
if [[ "$FORCE_NETCORE" -eq 0 ]]; then
|
||||
if download_v2rayn_bundle "$WORKDIR/$PKGROOT"; then
|
||||
echo "[*] Using v2rayN bundle archive."
|
||||
else
|
||||
echo "[*] Bundle failed, fallback to separate core + rules."
|
||||
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
|
||||
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)"
|
||||
fi
|
||||
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
|
||||
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
|
||||
fi
|
||||
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
|
||||
fi
|
||||
else
|
||||
echo "[*] --netcore specified: use separate core + rules."
|
||||
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
|
||||
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)"
|
||||
fi
|
||||
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
|
||||
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
|
||||
fi
|
||||
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
|
||||
fi
|
||||
|
||||
tar -C "$WORKDIR" -czf "$SOURCEDIR/$PKGROOT.tar.gz" "$PKGROOT"
|
||||
|
||||
# ===== Generate SPEC (heredoc with placeholders) ===================================
|
||||
SPECFILE="$SPECDIR/v2rayN.spec"
|
||||
cat > "$SPECFILE" <<'SPEC'
|
||||
%global debug_package %{nil}
|
||||
%undefine _debuginfo_subpackages
|
||||
%undefine _debugsource_packages
|
||||
# Ignore outdated LTTng dependencies incorrectly reported by the .NET runtime (to avoid installation failures)
|
||||
%global __requires_exclude ^liblttng-ust\.so\..*$
|
||||
|
||||
Name: v2rayN
|
||||
Version: __VERSION__
|
||||
Release: 1%{?dist}
|
||||
Summary: v2rayN (Avalonia) GUI client for Linux (x86_64/aarch64)
|
||||
License: GPL-3.0-only
|
||||
URL: https://github.com/2dust/v2rayN
|
||||
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
|
||||
|
||||
%description
|
||||
v2rayN GUI client built with Avalonia.
|
||||
Installs self-contained publish under /opt/v2rayN and a launcher 'v2rayn'.
|
||||
Cores (if bundled): /opt/v2rayN/bin/xray, /opt/v2rayN/bin/sing_box.
|
||||
Geo files for Xray are placed at /opt/v2rayN/bin/xray; launcher will symlink them into user's XDG data dir on first run.
|
||||
|
||||
%prep
|
||||
%setup -q -n __PKGROOT__
|
||||
|
||||
%build
|
||||
# no build
|
||||
|
||||
%install
|
||||
install -dm0755 %{buildroot}/opt/v2rayN
|
||||
cp -a * %{buildroot}/opt/v2rayN/
|
||||
|
||||
# Launcher (prioritize ELF first, then fall back to DLL; also create Geo symlinks for the user)
|
||||
install -dm0755 %{buildroot}%{_bindir}
|
||||
cat > %{buildroot}%{_bindir}/v2rayn << 'EOF'
|
||||
#!/usr/bin/bash
|
||||
set -euo pipefail
|
||||
DIR="/opt/v2rayN"
|
||||
|
||||
# --- SYMLINK GEO into user's XDG dir (new) ---
|
||||
XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||
USR_GEO_DIR="$XDG_DATA_HOME/v2rayN/bin"
|
||||
SYS_XRAY_DIR="$DIR/bin/xray"
|
||||
mkdir -p "$USR_GEO_DIR"
|
||||
for f in geosite.dat geoip.dat geoip-only-cn-private.dat Country.mmdb; do
|
||||
if [[ -f "$SYS_XRAY_DIR/$f" && ! -e "$USR_GEO_DIR/$f" ]]; then
|
||||
ln -s "$SYS_XRAY_DIR/$f" "$USR_GEO_DIR/$f" || true
|
||||
fi
|
||||
done
|
||||
# --- end GEO ---
|
||||
|
||||
# Prefer native ELF(apphost)
|
||||
if [[ -x "$DIR/v2rayN" ]]; then exec "$DIR/v2rayN" "$@"; fi
|
||||
|
||||
# DLL fallback (for framework-dependent publish)
|
||||
for dll in v2rayN.Desktop.dll v2rayN.dll; do
|
||||
if [[ -f "$DIR/$dll" ]]; then exec /usr/bin/dotnet "$DIR/$dll" "$@"; fi
|
||||
done
|
||||
|
||||
echo "v2rayN launcher: no executable found in $DIR" >&2
|
||||
ls -l "$DIR" >&2 || true
|
||||
exit 1
|
||||
EOF
|
||||
chmod 0755 %{buildroot}%{_bindir}/v2rayn
|
||||
|
||||
# Desktop File
|
||||
install -dm0755 %{buildroot}%{_datadir}/applications
|
||||
cat > %{buildroot}%{_datadir}/applications/v2rayn.desktop << 'EOF'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=v2rayN
|
||||
Comment=GUI client for Xray / sing-box
|
||||
Exec=v2rayn
|
||||
Icon=v2rayn
|
||||
Terminal=false
|
||||
Categories=Network;
|
||||
EOF
|
||||
|
||||
# icon
|
||||
if [ -f "%{_builddir}/__PKGROOT__/v2rayn.png" ]; then
|
||||
install -dm0755 %{buildroot}%{_datadir}/icons/hicolor/256x256/apps
|
||||
install -m0644 %{_builddir}/__PKGROOT__/v2rayn.png %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/v2rayn.png
|
||||
fi
|
||||
|
||||
%post
|
||||
/usr/bin/update-desktop-database %{_datadir}/applications >/dev/null 2>&1 || true
|
||||
/usr/bin/gtk-update-icon-cache -f %{_datadir}/icons/hicolor >/dev/null 2>&1 || true
|
||||
|
||||
%postun
|
||||
/usr/bin/update-desktop-database %{_datadir}/applications >/dev/null 2>&1 || true
|
||||
/usr/bin/gtk-update-icon-cache -f %{_datadir}/icons/hicolor >/dev/null 2>&1 || true
|
||||
|
||||
%files
|
||||
%{_bindir}/v2rayn
|
||||
/opt/v2rayN
|
||||
%{_datadir}/applications/v2rayn.desktop
|
||||
%{_datadir}/icons/hicolor/256x256/apps/v2rayn.png
|
||||
SPEC
|
||||
|
||||
# Optional: system-wide autostart (append block, keep original logic unchanged)
|
||||
if [[ "$AUTOSTART" -eq 1 ]]; then
|
||||
cat >> "$SPECFILE" <<'SPEC'
|
||||
# System-wide autostart entry
|
||||
%install
|
||||
install -dm0755 %{buildroot}/etc/xdg/autostart
|
||||
cat > %{buildroot}/etc/xdg/autostart/v2rayn.desktop << 'EOF'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=v2rayN (Autostart)
|
||||
Exec=v2rayn
|
||||
X-GNOME-Autostart-enabled=true
|
||||
NoDisplay=false
|
||||
EOF
|
||||
|
||||
%files
|
||||
%config(noreplace) /etc/xdg/autostart/v2rayn.desktop
|
||||
SPEC
|
||||
fi
|
||||
|
||||
# Injecting version/package root placeholders
|
||||
sed -i "s/__VERSION__/${VERSION}/g" "$SPECFILE"
|
||||
sed -i "s/__PKGROOT__/${PKGROOT}/g" "$SPECFILE"
|
||||
|
||||
# ===== Build RPM ================================================================
|
||||
rpmbuild -ba "$SPECFILE"
|
||||
|
||||
echo "Build done. RPM at:"
|
||||
ls -1 "${TOPDIR}/RPMS/$( [[ "$arch" == "aarch64" ]] && echo aarch64 || echo x86_64 )/v2rayN-${VERSION}-1"*.rpm
|
|
@ -289,6 +289,31 @@ public class Global
|
|||
"sing_box"
|
||||
];
|
||||
|
||||
public static readonly HashSet<EConfigType> XraySupportConfigType =
|
||||
[
|
||||
EConfigType.VMess,
|
||||
EConfigType.VLESS,
|
||||
EConfigType.Shadowsocks,
|
||||
EConfigType.Trojan,
|
||||
EConfigType.WireGuard,
|
||||
EConfigType.SOCKS,
|
||||
EConfigType.HTTP,
|
||||
];
|
||||
|
||||
public static readonly HashSet<EConfigType> SingboxSupportConfigType =
|
||||
[
|
||||
EConfigType.VMess,
|
||||
EConfigType.VLESS,
|
||||
EConfigType.Shadowsocks,
|
||||
EConfigType.Trojan,
|
||||
EConfigType.Hysteria2,
|
||||
EConfigType.TUIC,
|
||||
EConfigType.Anytls,
|
||||
EConfigType.WireGuard,
|
||||
EConfigType.SOCKS,
|
||||
EConfigType.HTTP,
|
||||
];
|
||||
|
||||
public static readonly List<string> DomainStrategies =
|
||||
[
|
||||
AsIs,
|
||||
|
|
962
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
962
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
@ -1059,6 +1059,18 @@
|
|||
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||
<value>لطفاً مطمئن شوید که ملاحظات وجود دارند و منحصر به فرد هستند</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
||||
<value>مسیریابی خودکار</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStrictRoute" xml:space="preserve">
|
||||
<value>مسیریابی سختگیرانه</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStack" xml:space="preserve">
|
||||
<value>پشته شبکه</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||
<value>MTU</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
||||
<value>فعال سازی additional Inbound</value>
|
||||
</data>
|
||||
|
@ -1497,4 +1509,4 @@
|
|||
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
||||
<value>This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
@ -1059,6 +1059,18 @@
|
|||
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||
<value>Kérjük, győződjön meg arról, hogy a konfigurációs megjegyzések léteznek és egyediek</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
||||
<value>Automatikus útválasztás</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStrictRoute" xml:space="preserve">
|
||||
<value>Szigorú útválasztás</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStack" xml:space="preserve">
|
||||
<value>Hálózati verem</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||
<value>MTU</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
||||
<value>További bejövő engedélyezése</value>
|
||||
</data>
|
||||
|
@ -1497,4 +1509,4 @@
|
|||
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
||||
<value>This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
@ -1059,6 +1059,18 @@
|
|||
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||
<value>Please make sure the Configuration remarks exist and are unique</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
||||
<value>Auto Route</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStrictRoute" xml:space="preserve">
|
||||
<value>Strict Route</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStack" xml:space="preserve">
|
||||
<value>Stack</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||
<value>MTU</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
||||
<value>Enable additional Inbound</value>
|
||||
</data>
|
||||
|
@ -1497,4 +1509,4 @@
|
|||
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
||||
<value>This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
@ -1059,6 +1059,18 @@
|
|||
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||
<value>Убедитесь, что примечание существует и является уникальным</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
||||
<value>Автоматическая маршрутизация</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStrictRoute" xml:space="preserve">
|
||||
<value>Строгая маршрутизация</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStack" xml:space="preserve">
|
||||
<value>Сетевой стек</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||
<value>MTU</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
||||
<value>Включить дополнительный входящий канал</value>
|
||||
</data>
|
||||
|
@ -1497,4 +1509,4 @@
|
|||
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
||||
<value>Эта функция предназначена для продвинутых пользователей и особых случаев. После включения игнорируются базовые настройки ядра, DNS и маршрутизации. Вы должны самостоятельно корректно задать порт системного прокси, учёт трафика и другие связанные параметры — всё настраивается вручную.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
@ -1056,6 +1056,18 @@
|
|||
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||
<value>请确保配置文件别名存在并唯一</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
||||
<value>自动路由</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStrictRoute" xml:space="preserve">
|
||||
<value>严格路由</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStack" xml:space="preserve">
|
||||
<value>协议栈</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||
<value>MTU</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
||||
<value>启用额外监听端口</value>
|
||||
</data>
|
||||
|
@ -1494,4 +1506,4 @@
|
|||
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
||||
<value>此功能供高级用户和有特殊需求的用户使用。 启用此功能后,将忽略 Core 的基础设置,DNS 设置 ,路由设置。你需要保证系统代理的端口和流量统计等功能的配置正确,一切都由你来设置。</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
@ -1056,6 +1056,18 @@
|
|||
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||
<value>請確保設定檔別名存在並且唯一</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
|
||||
<value>自動路由</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStrictRoute" xml:space="preserve">
|
||||
<value>嚴格路由</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunStack" xml:space="preserve">
|
||||
<value>協定堆疊</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunMtu" xml:space="preserve">
|
||||
<value>MTU</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
||||
<value>啟用額外偵聽連接埠</value>
|
||||
</data>
|
||||
|
@ -1494,4 +1506,4 @@
|
|||
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
|
||||
<value>This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you.</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
|
|
@ -133,7 +133,7 @@ public partial class CoreConfigSingboxService(Config config)
|
|||
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!Global.SingboxSupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -381,7 +381,7 @@ public partial class CoreConfigSingboxService(Config config)
|
|||
var proxyProfiles = new List<ProfileItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!Global.SingboxSupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -372,7 +372,7 @@ public partial class CoreConfigSingboxService
|
|||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
&& Global.SingboxSupportConfigType.Contains(prevNode.ConfigType))
|
||||
{
|
||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||
var prevServer = await GenServer(prevNode);
|
||||
|
@ -463,7 +463,7 @@ public partial class CoreConfigSingboxService
|
|||
{
|
||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom)
|
||||
&& Global.SingboxSupportConfigType.Contains(prevNode.ConfigType))
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
|
@ -558,7 +558,7 @@ public partial class CoreConfigSingboxService
|
|||
// Next proxy
|
||||
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom)
|
||||
&& Global.SingboxSupportConfigType.Contains(nextNode.ConfigType))
|
||||
{
|
||||
nextOutbound ??= await GenServer(nextNode);
|
||||
nextOutbound.tag = outbound.tag;
|
||||
|
|
|
@ -338,7 +338,7 @@ public partial class CoreConfigSingboxService
|
|||
|
||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||
if (node == null
|
||||
|| node.ConfigType == EConfigType.Custom)
|
||||
|| !Global.SingboxSupportConfigType.Contains(node.ConfigType))
|
||||
{
|
||||
return Global.ProxyTag;
|
||||
}
|
||||
|
|
|
@ -110,11 +110,7 @@ public partial class CoreConfigV2rayService(Config config)
|
|||
var proxyProfiles = new List<ProfileItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)
|
||||
if (!Global.XraySupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -250,7 +246,7 @@ public partial class CoreConfigV2rayService(Config config)
|
|||
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
if (!Global.XraySupportConfigType.Contains(it.ConfigType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -187,9 +187,9 @@ public partial class CoreConfigV2rayService
|
|||
foreach (var profile in new[] { subItem.PrevProfile, subItem.NextProfile })
|
||||
{
|
||||
var profileNode = await AppManager.Instance.GetProfileItemViaRemarks(profile);
|
||||
if (profileNode is not null &&
|
||||
profileNode.ConfigType is not (EConfigType.Custom or EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) &&
|
||||
Utils.IsDomain(profileNode.Address))
|
||||
if (profileNode is not null
|
||||
&& Global.XraySupportConfigType.Contains(profileNode.ConfigType)
|
||||
&& Utils.IsDomain(profileNode.Address))
|
||||
{
|
||||
directDomainList.Add(profileNode.Address);
|
||||
}
|
||||
|
@ -217,11 +217,11 @@ public partial class CoreConfigV2rayService
|
|||
AddDnsServers(directDNSAddress, directGeositeList);
|
||||
AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs);
|
||||
|
||||
var useDirectDns = rules?.LastOrDefault() is { } lastRule &&
|
||||
lastRule.OutboundTag == Global.DirectTag &&
|
||||
(lastRule.Port == "0-65535" ||
|
||||
lastRule.Network == "tcp,udp" ||
|
||||
lastRule.Ip?.Contains("0.0.0.0/0") == true);
|
||||
var useDirectDns = rules?.LastOrDefault() is { } lastRule
|
||||
&& lastRule.OutboundTag == Global.DirectTag
|
||||
&& (lastRule.Port == "0-65535"
|
||||
|| lastRule.Network == "tcp,udp"
|
||||
|| lastRule.Ip?.Contains("0.0.0.0/0") == true);
|
||||
|
||||
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
|
||||
v2rayConfig.dns.servers.AddRange(defaultDnsServers);
|
||||
|
@ -391,9 +391,7 @@ public partial class CoreConfigV2rayService
|
|||
// Previous proxy
|
||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& Global.SingboxSupportConfigType.Contains(prevNode.ConfigType)
|
||||
&& Utils.IsDomain(prevNode.Address))
|
||||
{
|
||||
domainList.Add(prevNode.Address);
|
||||
|
@ -402,9 +400,7 @@ public partial class CoreConfigV2rayService
|
|||
// Next proxy
|
||||
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC
|
||||
&& Global.SingboxSupportConfigType.Contains(nextNode.ConfigType)
|
||||
&& Utils.IsDomain(nextNode.Address))
|
||||
{
|
||||
domainList.Add(nextNode.Address);
|
||||
|
|
|
@ -529,10 +529,7 @@ public partial class CoreConfigV2rayService
|
|||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& prevNode.ConfigType != EConfigType.Anytls)
|
||||
&& Global.XraySupportConfigType.Contains(prevNode.ConfigType))
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
|
@ -605,10 +602,7 @@ public partial class CoreConfigV2rayService
|
|||
{
|
||||
var prevNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& prevNode.ConfigType != EConfigType.Anytls)
|
||||
&& Global.XraySupportConfigType.Contains(prevNode.ConfigType))
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
|
@ -675,10 +669,7 @@ public partial class CoreConfigV2rayService
|
|||
// Next proxy
|
||||
var nextNode = await AppManager.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC
|
||||
&& nextNode.ConfigType != EConfigType.Anytls)
|
||||
&& Global.XraySupportConfigType.Contains(nextNode.ConfigType))
|
||||
{
|
||||
if (nextOutbound == null)
|
||||
{
|
||||
|
|
|
@ -126,10 +126,7 @@ public partial class CoreConfigV2rayService
|
|||
|
||||
var node = await AppManager.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||
if (node == null
|
||||
|| node.ConfigType == EConfigType.Custom
|
||||
|| node.ConfigType == EConfigType.Hysteria2
|
||||
|| node.ConfigType == EConfigType.TUIC
|
||||
|| node.ConfigType == EConfigType.Anytls)
|
||||
|| !Global.XraySupportConfigType.Contains(node.ConfigType))
|
||||
{
|
||||
return Global.ProxyTag;
|
||||
}
|
||||
|
|
|
@ -356,8 +356,8 @@ public class SpeedtestService
|
|||
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
|
||||
{
|
||||
List<List<ServerTestItem>> lstTest = new();
|
||||
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)).ToList();
|
||||
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls).ToList();
|
||||
var lst1 = lstSelected.Where(t => Global.XraySupportConfigType.Contains(t.ConfigType)).ToList();
|
||||
var lst2 = lstSelected.Where(t => Global.SingboxSupportConfigType.Contains(t.ConfigType) && !Global.XraySupportConfigType.Contains(t.ConfigType)).ToList();
|
||||
|
||||
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
|
||||
{
|
||||
|
|
|
@ -4,12 +4,12 @@ using Avalonia.ReactiveUI;
|
|||
using Avalonia.Win32.Input;
|
||||
using GlobalHotKeys;
|
||||
|
||||
namespace v2rayN.Desktop.Handler;
|
||||
namespace v2rayN.Desktop.Manager;
|
||||
|
||||
public sealed class HotkeyHandler
|
||||
public sealed class HotkeyManager
|
||||
{
|
||||
private static readonly Lazy<HotkeyHandler> _instance = new(() => new());
|
||||
public static HotkeyHandler Instance = _instance.Value;
|
||||
private static readonly Lazy<HotkeyManager> _instance = new(() => new());
|
||||
public static HotkeyManager Instance = _instance.Value;
|
||||
private readonly Dictionary<int, EGlobalHotkey> _hotkeyTriggerDic = new();
|
||||
private HotKeyManager? _hotKeyManager;
|
||||
|
|
@ -523,7 +523,7 @@
|
|||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Mtu" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
|
||||
<TextBox
|
||||
x:Name="txtShortId9"
|
||||
Grid.Row="5"
|
||||
|
|
|
@ -5,7 +5,7 @@ using Avalonia.Input;
|
|||
using Avalonia.Interactivity;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Handler;
|
||||
using v2rayN.Desktop.Manager;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
|
@ -21,8 +21,8 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
|
|||
|
||||
btnReset.Click += btnReset_Click;
|
||||
|
||||
HotkeyHandler.Instance.IsPause = true;
|
||||
this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false;
|
||||
HotkeyManager.Instance.IsPause = true;
|
||||
this.Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
|
||||
btnCancel.Click += (s, e) => this.Close();
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
|
|
|
@ -13,7 +13,7 @@ using ServiceLib.Manager;
|
|||
using Splat;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
using v2rayN.Desktop.Handler;
|
||||
using v2rayN.Desktop.Manager;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
|
@ -143,7 +143,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
|
||||
|
||||
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
|
||||
HotkeyHandler.Instance.Init(_config, OnHotkeyHandler);
|
||||
HotkeyManager.Instance.Init(_config, OnHotkeyHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -235,7 +235,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
StorageUI();
|
||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
HotkeyHandler.Instance.Dispose();
|
||||
HotkeyManager.Instance.Dispose();
|
||||
desktop.Shutdown();
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -736,7 +736,7 @@
|
|||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Auto Route" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunAutoRoute}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togAutoRoute"
|
||||
Grid.Row="2"
|
||||
|
@ -749,7 +749,7 @@
|
|||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Strict Route" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStrictRoute}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togStrictRoute"
|
||||
Grid.Row="3"
|
||||
|
@ -762,7 +762,7 @@
|
|||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Stack" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStack}" />
|
||||
<ComboBox
|
||||
x:Name="cmbStack"
|
||||
Grid.Row="4"
|
||||
|
@ -776,7 +776,7 @@
|
|||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="Mtu" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
|
||||
<ComboBox
|
||||
x:Name="cmbMtu"
|
||||
Grid.Row="5"
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.Windows.Media.Imaging;
|
|||
|
||||
namespace v2rayN;
|
||||
|
||||
public class QRCodeHelper
|
||||
public class QRCodeUtils
|
||||
{
|
||||
public static ImageSource? GetQRCode(string? strContent)
|
||||
{
|
||||
|
@ -17,7 +17,7 @@ public class QRCodeHelper
|
|||
}
|
||||
try
|
||||
{
|
||||
var qrCodeImage = QRCodeUtils.GenQRCode(strContent);
|
||||
var qrCodeImage = ServiceLib.Common.QRCodeUtils.GenQRCode(strContent);
|
||||
return qrCodeImage is null ? null : ByteToImage(qrCodeImage);
|
||||
}
|
||||
catch
|
|
@ -6,12 +6,12 @@ using System.Windows.Input;
|
|||
using System.Windows.Interop;
|
||||
using ServiceLib.Manager;
|
||||
|
||||
namespace v2rayN.Handler;
|
||||
namespace v2rayN.Manager;
|
||||
|
||||
public sealed class HotkeyHandler
|
||||
public sealed class HotkeyManager
|
||||
{
|
||||
private static readonly Lazy<HotkeyHandler> _instance = new(() => new());
|
||||
public static HotkeyHandler Instance = _instance.Value;
|
||||
private static readonly Lazy<HotkeyManager> _instance = new(() => new());
|
||||
public static HotkeyManager Instance = _instance.Value;
|
||||
private const int WmHotkey = 0x0312;
|
||||
private readonly Dictionary<int, List<EGlobalHotkey>> _hotkeyTriggerDic = new();
|
||||
|
||||
|
@ -21,7 +21,7 @@ public sealed class HotkeyHandler
|
|||
|
||||
public event Action<EGlobalHotkey>? HotkeyTriggerEvent;
|
||||
|
||||
public HotkeyHandler()
|
||||
public HotkeyManager()
|
||||
{
|
||||
ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage;
|
||||
Init();
|
||||
|
@ -51,7 +51,7 @@ public sealed class HotkeyHandler
|
|||
modifiers |= KeyModifiers.Alt;
|
||||
}
|
||||
|
||||
key = (key << 16) | (int)modifiers;
|
||||
key = key << 16 | (int)modifiers;
|
||||
if (!_hotkeyTriggerDic.ContainsKey(key))
|
||||
{
|
||||
_hotkeyTriggerDic.Add(key, new() { item.EGlobalHotkey });
|
||||
|
@ -77,7 +77,7 @@ public sealed class HotkeyHandler
|
|||
|
||||
Application.Current?.Dispatcher.Invoke(() =>
|
||||
{
|
||||
isSuccess = RegisterHotKey(IntPtr.Zero, _hotkeyCode, hotkeyInfo.fsModifiers, hotkeyInfo.vKey);
|
||||
isSuccess = RegisterHotKey(nint.Zero, _hotkeyCode, hotkeyInfo.fsModifiers, hotkeyInfo.vKey);
|
||||
});
|
||||
foreach (var name in hotkeyInfo.Names)
|
||||
{
|
||||
|
@ -101,7 +101,7 @@ public sealed class HotkeyHandler
|
|||
{
|
||||
Application.Current?.Dispatcher.Invoke(() =>
|
||||
{
|
||||
UnregisterHotKey(IntPtr.Zero, hotkey);
|
||||
UnregisterHotKey(nint.Zero, hotkey);
|
||||
});
|
||||
}
|
||||
Init();
|
||||
|
@ -111,7 +111,7 @@ public sealed class HotkeyHandler
|
|||
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>();
|
||||
|
||||
|
@ -174,10 +174,10 @@ public sealed class HotkeyHandler
|
|||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
|
||||
private static extern bool RegisterHotKey(nint hWnd, int id, int fsModifiers, int vlc);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
private static extern bool UnregisterHotKey(nint hWnd, int id);
|
||||
|
||||
[Flags]
|
||||
private enum KeyModifiers
|
|
@ -1,13 +1,14 @@
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Windows.Media.Imaging;
|
||||
using v2rayN.Manager;
|
||||
|
||||
namespace v2rayN.Handler;
|
||||
namespace v2rayN.Manager;
|
||||
|
||||
public sealed class WindowsHandler
|
||||
public sealed class WindowsManager
|
||||
{
|
||||
private static readonly Lazy<WindowsHandler> instance = new(() => new());
|
||||
public static WindowsHandler Instance => instance.Value;
|
||||
private static readonly Lazy<WindowsManager> instance = new(() => new());
|
||||
public static WindowsManager Instance => instance.Value;
|
||||
private static readonly string _tag = "WindowsHandler";
|
||||
|
||||
public async Task<Icon> GetNotifyIcon(Config config)
|
||||
|
@ -97,8 +98,8 @@ public sealed class WindowsHandler
|
|||
|
||||
public void RegisterGlobalHotkey(Config config, Action<EGlobalHotkey> handler, Action<bool, string>? update)
|
||||
{
|
||||
HotkeyHandler.Instance.UpdateViewEvent += update;
|
||||
HotkeyHandler.Instance.HotkeyTriggerEvent += handler;
|
||||
HotkeyHandler.Instance.Load();
|
||||
HotkeyManager.Instance.UpdateViewEvent += update;
|
||||
HotkeyManager.Instance.HotkeyTriggerEvent += handler;
|
||||
HotkeyManager.Instance.Load();
|
||||
}
|
||||
}
|
|
@ -696,7 +696,7 @@
|
|||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Mtu" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
|
||||
<TextBox
|
||||
x:Name="txtShortId9"
|
||||
Grid.Row="5"
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Windows.Controls;
|
|||
using System.Windows.Input;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -23,8 +23,8 @@ public partial class GlobalHotkeySettingWindow
|
|||
|
||||
btnReset.Click += btnReset_Click;
|
||||
|
||||
HotkeyHandler.Instance.IsPause = true;
|
||||
this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false;
|
||||
HotkeyManager.Instance.IsPause = true;
|
||||
this.Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
Height="700"
|
||||
MinWidth="900"
|
||||
x:TypeArguments="vms:MainWindowViewModel"
|
||||
Icon="/Resources/v2rayN.ico"
|
||||
ResizeMode="CanResizeWithGrip"
|
||||
ShowInTaskbar="True"
|
||||
Style="{StaticResource WindowGlobal}"
|
||||
|
|
|
@ -10,7 +10,7 @@ using MaterialDesignThemes.Wpf;
|
|||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using Splat;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -143,7 +143,7 @@ public partial class MainWindow
|
|||
}
|
||||
|
||||
AddHelpMenuItem();
|
||||
WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
||||
WindowsManager.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
||||
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
|
||||
}
|
||||
|
||||
|
@ -344,7 +344,7 @@ public partial class MainWindow
|
|||
|
||||
if (Application.Current?.MainWindow is Window window)
|
||||
{
|
||||
var bytes = QRCodeHelper.CaptureScreen(window);
|
||||
var bytes = QRCodeUtils.CaptureScreen(window);
|
||||
await ViewModel?.ScanScreenResult(bytes);
|
||||
}
|
||||
|
||||
|
|
|
@ -430,7 +430,7 @@
|
|||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="mtu" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
|
||||
<TextBox Style="{StaticResource DefTextBox}"
|
||||
x:Name="txtKcpmtu"
|
||||
Grid.Row="1"
|
||||
|
@ -1037,7 +1037,7 @@
|
|||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Auto Route" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunAutoRoute}" />
|
||||
<ToggleButton
|
||||
x:Name="togAutoRoute"
|
||||
Grid.Row="2"
|
||||
|
@ -1051,7 +1051,7 @@
|
|||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Strict Route" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStrictRoute}" />
|
||||
<ToggleButton
|
||||
x:Name="togStrictRoute"
|
||||
Grid.Row="3"
|
||||
|
@ -1065,7 +1065,7 @@
|
|||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Stack" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStack}" />
|
||||
<ComboBox
|
||||
x:Name="cmbStack"
|
||||
Grid.Row="4"
|
||||
|
@ -1081,7 +1081,7 @@
|
|||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="Mtu" />
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunMtu}" />
|
||||
<ComboBox
|
||||
x:Name="cmbMtu"
|
||||
Grid.Row="5"
|
||||
|
|
|
@ -180,7 +180,7 @@ public partial class ProfilesView
|
|||
|
||||
public async void ShareServer(string url)
|
||||
{
|
||||
var img = QRCodeHelper.GetQRCode(url);
|
||||
var img = QRCodeUtils.GetQRCode(url);
|
||||
var dialog = new QrcodeView()
|
||||
{
|
||||
imgQrcode = { Source = img },
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Windows.Threading;
|
|||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using Splat;
|
||||
using v2rayN.Handler;
|
||||
using v2rayN.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -96,8 +96,8 @@ public partial class StatusBarView
|
|||
case EViewAction.DispatcherRefreshIcon:
|
||||
Application.Current?.Dispatcher.Invoke((async () =>
|
||||
{
|
||||
tbNotify.Icon = await WindowsHandler.Instance.GetNotifyIcon(_config);
|
||||
Application.Current.MainWindow.Icon = WindowsHandler.Instance.GetAppIcon(_config);
|
||||
tbNotify.Icon = await WindowsManager.Instance.GetNotifyIcon(_config);
|
||||
Application.Current.MainWindow.Icon = WindowsManager.Instance.GetAppIcon(_config);
|
||||
}), DispatcherPriority.Normal);
|
||||
break;
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ public partial class SubSettingWindow
|
|||
{
|
||||
return;
|
||||
}
|
||||
var img = QRCodeHelper.GetQRCode(url);
|
||||
var img = QRCodeUtils.GetQRCode(url);
|
||||
var dialog = new QrcodeView()
|
||||
{
|
||||
imgQrcode = { Source = img },
|
||||
|
|
Loading…
Reference in a new issue