mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-03-17 18:21:02 +03:00
Compare commits
142 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a0fd55af7 | ||
|
|
da70cbcdda | ||
|
|
921b64e1e0 | ||
|
|
c0de0aa108 | ||
|
|
715d475f49 | ||
|
|
e3f09b3ec6 | ||
|
|
0a5fafb84f | ||
|
|
4e084c5ee0 | ||
|
|
d1fe617670 | ||
|
|
7744bdbbe0 | ||
|
|
e1329c8157 | ||
|
|
f31e60af5b | ||
|
|
ed18e3c786 | ||
|
|
579e0fac36 | ||
|
|
92752765ba | ||
|
|
071f51cf6f | ||
|
|
dde3cce120 | ||
|
|
bb1b9858d5 | ||
|
|
a31c27be73 | ||
|
|
85ae3916cb | ||
|
|
cc9b7e64eb | ||
|
|
5e22a49e49 | ||
|
|
07cf1b4db5 | ||
|
|
f6ab5cdcb2 | ||
|
|
b477aded0b | ||
|
|
65318efd67 | ||
|
|
dbd195a46e | ||
|
|
0651ad492f | ||
|
|
a3c5adb1f4 | ||
|
|
a771abcdc2 | ||
|
|
b8b3a089f3 | ||
|
|
8f00067266 | ||
|
|
83bf067d18 | ||
|
|
1729ee337f | ||
|
|
57834840b8 | ||
|
|
99d7b62d79 | ||
|
|
6625aca994 | ||
|
|
ce56be6507 | ||
|
|
fd69b14623 | ||
|
|
7521bbe15f | ||
|
|
3c6ddd7403 | ||
|
|
6820e2f4c7 | ||
|
|
77f3ebaf1a | ||
|
|
e7e244d4f2 | ||
|
|
f4c40d733e | ||
|
|
049c334db3 | ||
|
|
6197832317 | ||
|
|
2fd53f9825 | ||
|
|
ae16b8975b | ||
|
|
171177c76f | ||
|
|
ade1d8c0c7 | ||
|
|
9a194f0850 | ||
|
|
76d5a8b205 | ||
|
|
bc6ce6c7ee | ||
|
|
025cdfa25b | ||
|
|
2f432e941d | ||
|
|
96edca8f74 | ||
|
|
51b250435d | ||
|
|
5a2121501d | ||
|
|
877b3e2ce5 | ||
|
|
421ddc0016 | ||
|
|
2662abc5a3 | ||
|
|
b3e1c8a907 | ||
|
|
2266fde26f | ||
|
|
e58e75eea9 | ||
|
|
eafebdba21 | ||
|
|
7bf5e69444 | ||
|
|
cb0dc46d08 | ||
|
|
9b8209b61b | ||
|
|
b6ba9978e3 | ||
|
|
0d1d7a9b87 | ||
|
|
508dd5b383 | ||
|
|
31a1b7a80b | ||
|
|
ba43424781 | ||
|
|
f899b2a962 | ||
|
|
2dd3d8c11e | ||
|
|
6eea425280 | ||
|
|
5e7d4fd2d6 | ||
|
|
d9fba50606 | ||
|
|
b6035fbbdf | ||
|
|
e67b694f06 | ||
|
|
2333ee2c07 | ||
|
|
61ccc2152e | ||
|
|
f6aca4ca8e | ||
|
|
1707987a7b | ||
|
|
85604dee79 | ||
|
|
a12969be30 | ||
|
|
9f91eada89 | ||
|
|
cb6a6aa42a | ||
|
|
4fec8abad4 | ||
|
|
35571dc8d7 | ||
|
|
a103b83647 | ||
|
|
e1e4bf599b | ||
|
|
cba8aaa410 | ||
|
|
8ced4ddaa2 | ||
|
|
15404ecab4 | ||
|
|
97772f9ac5 | ||
|
|
764fbe2c9d | ||
|
|
e03344d85b | ||
|
|
0e98a51775 | ||
|
|
0a1d3c4afb | ||
|
|
fd9b5f3c57 | ||
|
|
73f6afd4c0 | ||
|
|
50dd2b3aad | ||
|
|
c0e9445602 | ||
|
|
541d9c6b86 | ||
|
|
8c91e5c5ca | ||
|
|
19d1605d8c | ||
|
|
0faf82f109 | ||
|
|
ee5314de20 | ||
|
|
7e8d3bd2ac | ||
|
|
9750e1409c | ||
|
|
67d4e061fb | ||
|
|
f67f2be0cb | ||
|
|
aa42bf548e | ||
|
|
d679e8fa7d | ||
|
|
1357ef5d6f | ||
|
|
30a5d1e0e1 | ||
|
|
9f0985c842 | ||
|
|
be06c0d738 | ||
|
|
f0f50f0f03 | ||
|
|
1850d32f49 | ||
|
|
3999d498be | ||
|
|
bbdce8d57b | ||
|
|
baf70da2fe | ||
|
|
b967d496cc | ||
|
|
2aef79688b | ||
|
|
0451a1c45f | ||
|
|
f7e9057a39 | ||
|
|
d73e0e1e5a | ||
|
|
39dbd89287 | ||
|
|
c04f460bbd | ||
|
|
79a1f888d6 | ||
|
|
57d1b1ecc4 | ||
|
|
8a4a2b5732 | ||
|
|
51a60a7eed | ||
|
|
b26acde450 | ||
|
|
2de81045ea | ||
|
|
a72a8906b0 | ||
|
|
614086a216 | ||
|
|
2ffc2ad85b | ||
|
|
eef091d4e8 |
@@ -1,8 +1,14 @@
|
|||||||
[target.x86_64-pc-windows-msvc]
|
[target.x86_64-pc-windows-msvc]
|
||||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||||
[target.i686-pc-windows-msvc]
|
[target.i686-pc-windows-msvc]
|
||||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
rustflags = ["-C", "target-feature=+crt-static", "-C", "link-args=/NODEFAULTLIB:MSVCRT"]
|
||||||
[target.'cfg(target_os="macos")']
|
[target.'cfg(target_os="macos")']
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C", "link-args=-sectcreate __CGPreLoginApp __cgpreloginapp /dev/null",
|
"-C", "link-args=-sectcreate __CGPreLoginApp __cgpreloginapp /dev/null",
|
||||||
]
|
]
|
||||||
|
#[target.'cfg(target_os="linux")']
|
||||||
|
# glibc-static required, this may fix https://github.com/rustdesk/rustdesk/issues/9103, but I do not want this big change
|
||||||
|
# this is unlikely to help also, because the other so files still use libc dynamically
|
||||||
|
#rustflags = [
|
||||||
|
# "-C", "link-args=-Wl,-Bstatic -lc -Wl,-Bdynamic"
|
||||||
|
#]
|
||||||
|
|||||||
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -4,9 +4,9 @@ env:
|
|||||||
# MIN_SUPPORTED_RUST_VERSION: "1.46.0"
|
# MIN_SUPPORTED_RUST_VERSION: "1.46.0"
|
||||||
# CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
|
# CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
|
||||||
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
# vcpkg version: 2023.10.19
|
# vcpkg version: 2024.06.15
|
||||||
# for multiarch gcc compatibility
|
# for multiarch gcc compatibility
|
||||||
VCPKG_COMMIT_ID: "8eb57355a4ffb410a2e94c07b4dca2dffbee8e50"
|
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -112,6 +112,8 @@ jobs:
|
|||||||
libgstreamer-plugins-base1.0-dev \
|
libgstreamer-plugins-base1.0-dev \
|
||||||
libgtk-3-dev \
|
libgtk-3-dev \
|
||||||
libpulse-dev \
|
libpulse-dev \
|
||||||
|
libva-dev \
|
||||||
|
libvdpau-dev \
|
||||||
libxcb-randr0-dev \
|
libxcb-randr0-dev \
|
||||||
libxcb-shape0-dev \
|
libxcb-shape0-dev \
|
||||||
libxcb-xfixes0-dev \
|
libxcb-xfixes0-dev \
|
||||||
|
|||||||
7
.github/workflows/fdroid.yml
vendored
7
.github/workflows/fdroid.yml
vendored
@@ -19,7 +19,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate RustDesk version file
|
- name: Generate RustDesk version file
|
||||||
run: |
|
run: |
|
||||||
UPSTREAM_VERNAME="$(curl https://api.github.com/repos/rustdesk/rustdesk/releases/latest | jq -r .tag_name | sed 's/^v//')"
|
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
|
||||||
|
UPSTREAM_VERNAME="${GITHUB_REF##refs/tags/}"
|
||||||
|
UPSTREAM_VERNAME="${UPSTREAM_VERNAME##v}"
|
||||||
|
else
|
||||||
|
UPSTREAM_VERNAME="$(curl https://api.github.com/repos/rustdesk/rustdesk/releases/latest | jq -r .tag_name | sed 's/^v//')"
|
||||||
|
fi
|
||||||
UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr '.' ' ' | tr '-' ' ' | while read -r MAJOR MINOR PATCH REV; do [ -z "$MAJOR" ] && MAJOR=0; [ -z "$MINOR" ] && MINOR=0; [ -z "$PATCH" ] && PATCH=0; [ -z "$REV" ] && REV=0; echo "$(( 1000000 * $MAJOR + 10000 * $MINOR + 100 * $PATCH + $REV ))"; done)"
|
UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr '.' ' ' | tr '-' ' ' | while read -r MAJOR MINOR PATCH REV; do [ -z "$MAJOR" ] && MAJOR=0; [ -z "$MINOR" ] && MINOR=0; [ -z "$PATCH" ] && PATCH=0; [ -z "$REV" ] && REV=0; echo "$(( 1000000 * $MAJOR + 10000 * $MINOR + 100 * $PATCH + $REV ))"; done)"
|
||||||
echo "versionName=$UPSTREAM_VERNAME" > rustdesk-version.txt
|
echo "versionName=$UPSTREAM_VERNAME" > rustdesk-version.txt
|
||||||
echo "versionCode=$UPSTREAM_VERCODE" >> rustdesk-version.txt
|
echo "versionCode=$UPSTREAM_VERCODE" >> rustdesk-version.txt
|
||||||
|
|||||||
521
.github/workflows/flutter-build.yml
vendored
521
.github/workflows/flutter-build.yml
vendored
@@ -18,9 +18,11 @@ on:
|
|||||||
# in this file!
|
# in this file!
|
||||||
|
|
||||||
env:
|
env:
|
||||||
WIN_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
|
SCITER_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
|
||||||
RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
|
RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
|
||||||
CARGO_NDK_VERSION: "3.1.2"
|
CARGO_NDK_VERSION: "3.1.2"
|
||||||
|
SCITER_ARMV7_CMAKE_VERSION: "3.29.7"
|
||||||
|
SCITER_NASM_DEBVERSION: "2.14-1"
|
||||||
LLVM_VERSION: "15.0.6"
|
LLVM_VERSION: "15.0.6"
|
||||||
FLUTTER_VERSION: "3.19.6"
|
FLUTTER_VERSION: "3.19.6"
|
||||||
ANDROID_FLUTTER_VERSION: "3.13.9" # >= 3.16 is very slow on my android phone, but work well on most of others. We may switch to new flutter after changing to texture rendering (I believe it can solve my problem).
|
ANDROID_FLUTTER_VERSION: "3.13.9" # >= 3.16 is very slow on my android phone, but work well on most of others. We may switch to new flutter after changing to texture rendering (I believe it can solve my problem).
|
||||||
@@ -29,10 +31,10 @@ env:
|
|||||||
FLUTTER_ELINUX_VERSION: "3.16.9"
|
FLUTTER_ELINUX_VERSION: "3.16.9"
|
||||||
TAG_NAME: "${{ inputs.upload-tag }}"
|
TAG_NAME: "${{ inputs.upload-tag }}"
|
||||||
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
# vcpkg version: 2024.06.15
|
# vcpkg version: 2024.07.12
|
||||||
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
|
VCPKG_COMMIT_ID: "1de2026f28ead93ff1773e6e680387643e914ea1"
|
||||||
VERSION: "1.2.7"
|
VERSION: "1.3.0"
|
||||||
NDK_VERSION: "r26d"
|
NDK_VERSION: "r27"
|
||||||
#signing keys env variable checks
|
#signing keys env variable checks
|
||||||
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
|
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
|
||||||
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
|
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
|
||||||
@@ -65,7 +67,12 @@ jobs:
|
|||||||
job:
|
job:
|
||||||
# - { target: i686-pc-windows-msvc , os: windows-2022 }
|
# - { target: i686-pc-windows-msvc , os: windows-2022 }
|
||||||
# - { target: x86_64-pc-windows-gnu , os: windows-2022 }
|
# - { target: x86_64-pc-windows-gnu , os: windows-2022 }
|
||||||
- { target: x86_64-pc-windows-msvc, os: windows-2022, arch: x86_64, vcpkg-triplet: x64-windows-static }
|
- {
|
||||||
|
target: x86_64-pc-windows-msvc,
|
||||||
|
os: windows-2022,
|
||||||
|
arch: x86_64,
|
||||||
|
vcpkg-triplet: x64-windows-static,
|
||||||
|
}
|
||||||
# - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 }
|
# - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 }
|
||||||
steps:
|
steps:
|
||||||
- name: Export GitHub Actions cache environment variables
|
- name: Export GitHub Actions cache environment variables
|
||||||
@@ -93,7 +100,7 @@ jobs:
|
|||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@v1
|
uses: dtolnay/rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.WIN_RUST_VERSION }}
|
toolchain: ${{ env.SCITER_RUST_VERSION }}
|
||||||
targets: ${{ matrix.job.target }}
|
targets: ${{ matrix.job.target }}
|
||||||
components: "rustfmt"
|
components: "rustfmt"
|
||||||
|
|
||||||
@@ -113,10 +120,25 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
vcpkgDirectory: C:\vcpkg
|
vcpkgDirectory: C:\vcpkg
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
||||||
|
doNotCache: false
|
||||||
|
|
||||||
- name: Install vcpkg dependencies
|
- name: Install vcpkg dependencies
|
||||||
|
env:
|
||||||
|
VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }}
|
||||||
run: |
|
run: |
|
||||||
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
|
if ! $VCPKG_ROOT/vcpkg \
|
||||||
|
install \
|
||||||
|
--triplet ${{ matrix.job.vcpkg-triplet }} \
|
||||||
|
--x-install-root="$VCPKG_ROOT/installed"; then
|
||||||
|
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
|
||||||
|
echo "$_1:"
|
||||||
|
echo "======"
|
||||||
|
cat "$_1"
|
||||||
|
echo "======"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Build rustdesk
|
- name: Build rustdesk
|
||||||
@@ -219,7 +241,12 @@ jobs:
|
|||||||
job:
|
job:
|
||||||
# - { target: i686-pc-windows-msvc , os: windows-2022 }
|
# - { target: i686-pc-windows-msvc , os: windows-2022 }
|
||||||
# - { target: x86_64-pc-windows-gnu , os: windows-2022 }
|
# - { target: x86_64-pc-windows-gnu , os: windows-2022 }
|
||||||
- { target: i686-pc-windows-msvc, os: windows-2022, arch: x86, vcpkg-triplet: x86-windows-static }
|
- {
|
||||||
|
target: i686-pc-windows-msvc,
|
||||||
|
os: windows-2022,
|
||||||
|
arch: x86,
|
||||||
|
vcpkg-triplet: x86-windows-static,
|
||||||
|
}
|
||||||
# - { target: aarch64-pc-windows-msvc, os: windows-2022 }
|
# - { target: aarch64-pc-windows-msvc, os: windows-2022 }
|
||||||
steps:
|
steps:
|
||||||
- name: Export GitHub Actions cache environment variables
|
- name: Export GitHub Actions cache environment variables
|
||||||
@@ -230,7 +257,7 @@ jobs:
|
|||||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install LLVM and Clang
|
- name: Install LLVM and Clang
|
||||||
uses: rustdesk-org/install-llvm-action-32bit@master
|
uses: rustdesk-org/install-llvm-action-32bit@master
|
||||||
@@ -253,10 +280,25 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
vcpkgDirectory: C:\vcpkg
|
vcpkgDirectory: C:\vcpkg
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
||||||
|
doNotCache: false
|
||||||
|
|
||||||
- name: Install vcpkg dependencies
|
- name: Install vcpkg dependencies
|
||||||
|
env:
|
||||||
|
VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }}
|
||||||
run: |
|
run: |
|
||||||
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
|
if ! $VCPKG_ROOT/vcpkg \
|
||||||
|
install \
|
||||||
|
--triplet ${{ matrix.job.vcpkg-triplet }} \
|
||||||
|
--x-install-root="$VCPKG_ROOT/installed"; then
|
||||||
|
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
|
||||||
|
echo "$_1:"
|
||||||
|
echo "======"
|
||||||
|
cat "$_1"
|
||||||
|
echo "======"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Build rustdesk
|
- name: Build rustdesk
|
||||||
@@ -341,7 +383,7 @@ jobs:
|
|||||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install flutter rust bridge deps
|
- name: Install flutter rust bridge deps
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -424,9 +466,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
brew install nasm
|
brew install nasm yasm
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install flutter
|
- name: Install flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
@@ -437,10 +479,23 @@ jobs:
|
|||||||
uses: lukka/run-vcpkg@v11
|
uses: lukka/run-vcpkg@v11
|
||||||
with:
|
with:
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
||||||
|
doNotCache: false
|
||||||
|
|
||||||
- name: Install vcpkg dependencies
|
- name: Install vcpkg dependencies
|
||||||
run: |
|
run: |
|
||||||
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
|
if ! $VCPKG_ROOT/vcpkg \
|
||||||
|
install \
|
||||||
|
--triplet ${{ matrix.job.vcpkg-triplet }} \
|
||||||
|
--x-install-root="$VCPKG_ROOT/installed"; then
|
||||||
|
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
|
||||||
|
echo "$_1:"
|
||||||
|
echo "======"
|
||||||
|
cat "$_1"
|
||||||
|
echo "======"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
@@ -506,7 +561,7 @@ jobs:
|
|||||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# $VCPKG_ROOT/vcpkg install --triplet arm64-ios --x-install-root="$VCPKG_ROOT/installed"
|
# $VCPKG_ROOT/vcpkg install --triplet arm64-ios --x-install-root="$VCPKG_ROOT/installed"
|
||||||
|
|
||||||
@@ -576,7 +631,7 @@ jobs:
|
|||||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Import the codesign cert
|
- name: Import the codesign cert
|
||||||
if: env.MACOS_P12_BASE64 != null
|
if: env.MACOS_P12_BASE64 != null
|
||||||
@@ -651,10 +706,22 @@ jobs:
|
|||||||
uses: lukka/run-vcpkg@v11
|
uses: lukka/run-vcpkg@v11
|
||||||
with:
|
with:
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
||||||
|
doNotCache: false
|
||||||
|
|
||||||
- name: Install vcpkg dependencies
|
- name: Install vcpkg dependencies
|
||||||
run: |
|
run: |
|
||||||
$VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed"
|
if ! $VCPKG_ROOT/vcpkg \
|
||||||
|
install \
|
||||||
|
--x-install-root="$VCPKG_ROOT/installed"; then
|
||||||
|
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
|
||||||
|
echo "$_1:"
|
||||||
|
echo "======"
|
||||||
|
cat "$_1"
|
||||||
|
echo "======"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Show version information (Rust, cargo, Clang)
|
- name: Show version information (Rust, cargo, Clang)
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -770,15 +837,35 @@ jobs:
|
|||||||
arch: aarch64,
|
arch: aarch64,
|
||||||
target: aarch64-linux-android,
|
target: aarch64-linux-android,
|
||||||
os: ubuntu-20.04,
|
os: ubuntu-20.04,
|
||||||
openssl-arch: android-arm64,
|
reltype: release,
|
||||||
|
suffix: "",
|
||||||
}
|
}
|
||||||
- {
|
- {
|
||||||
arch: armv7,
|
arch: armv7,
|
||||||
target: armv7-linux-androideabi,
|
target: armv7-linux-androideabi,
|
||||||
os: ubuntu-20.04,
|
os: ubuntu-20.04,
|
||||||
openssl-arch: android-arm,
|
reltype: release,
|
||||||
|
suffix: "",
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
arch: x86_64,
|
||||||
|
target: x86_64-linux-android,
|
||||||
|
os: ubuntu-20.04,
|
||||||
|
reltype: release,
|
||||||
|
suffix: "",
|
||||||
}
|
}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Free Disk Space (Ubuntu)
|
||||||
|
uses: jlumbroso/free-disk-space@main
|
||||||
|
with:
|
||||||
|
tool-cache: false
|
||||||
|
android: false
|
||||||
|
dotnet: true
|
||||||
|
haskell: true
|
||||||
|
large-packages: false
|
||||||
|
docker-images: true
|
||||||
|
swap-storage: false
|
||||||
|
|
||||||
- name: Export GitHub Actions cache environment variables
|
- name: Export GitHub Actions cache environment variables
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
@@ -822,7 +909,7 @@ jobs:
|
|||||||
wget
|
wget
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install flutter
|
- name: Install flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
@@ -839,17 +926,34 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
vcpkgDirectory: /opt/artifacts/vcpkg
|
vcpkgDirectory: /opt/artifacts/vcpkg
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
||||||
|
doNotCache: false
|
||||||
|
|
||||||
- name: Install vcpkg dependencies
|
- name: Install vcpkg dependencies
|
||||||
run: |
|
run: |
|
||||||
case ${{ matrix.job.target }} in
|
case ${{ matrix.job.target }} in
|
||||||
aarch64-linux-android)
|
aarch64-linux-android)
|
||||||
./flutter/build_android_deps.sh arm64-v8a
|
ANDROID_TARGET=arm64-v8a
|
||||||
;;
|
;;
|
||||||
armv7-linux-androideabi)
|
armv7-linux-androideabi)
|
||||||
./flutter/build_android_deps.sh armeabi-v7a
|
ANDROID_TARGET=armeabi-v7a
|
||||||
|
;;
|
||||||
|
x86_64-linux-android)
|
||||||
|
ANDROID_TARGET=x86_64
|
||||||
|
;;
|
||||||
|
i686-linux-android)
|
||||||
|
ANDROID_TARGET=x86
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
if ! ./flutter/build_android_deps.sh "${ANDROID_TARGET}"; then
|
||||||
|
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
|
||||||
|
echo "$_1:"
|
||||||
|
echo "======"
|
||||||
|
cat "$_1"
|
||||||
|
echo "======"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Restore bridge files
|
- name: Restore bridge files
|
||||||
@@ -894,8 +998,24 @@ jobs:
|
|||||||
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
|
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
|
||||||
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
|
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
|
||||||
;;
|
;;
|
||||||
|
x86_64-linux-android)
|
||||||
|
./flutter/ndk_x64.sh
|
||||||
|
mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64
|
||||||
|
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
|
||||||
|
;;
|
||||||
|
i686-linux-android)
|
||||||
|
./flutter/ndk_x86.sh
|
||||||
|
mkdir -p ./flutter/android/app/src/main/jniLibs/x86
|
||||||
|
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
- name: Upload Rustdesk library to Artifacts
|
||||||
|
uses: actions/upload-artifact@master
|
||||||
|
with:
|
||||||
|
name: librustdesk.so.${{ matrix.job.target }}
|
||||||
|
path: ./target/${{ matrix.job.target }}/release/liblibrustdesk.so
|
||||||
|
|
||||||
- name: Build rustdesk
|
- name: Build rustdesk
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
@@ -911,8 +1031,8 @@ jobs:
|
|||||||
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
|
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
|
||||||
# build flutter
|
# build flutter
|
||||||
pushd flutter
|
pushd flutter
|
||||||
flutter build apk --release --target-platform android-arm64 --split-per-abi
|
flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-arm64 --split-per-abi
|
||||||
mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
|
mv build/app/outputs/flutter-apk/app-arm64-v8a-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
|
||||||
;;
|
;;
|
||||||
armv7-linux-androideabi)
|
armv7-linux-androideabi)
|
||||||
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
|
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
|
||||||
@@ -920,13 +1040,31 @@ jobs:
|
|||||||
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
|
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
|
||||||
# build flutter
|
# build flutter
|
||||||
pushd flutter
|
pushd flutter
|
||||||
flutter build apk --release --target-platform android-arm --split-per-abi
|
flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-arm --split-per-abi
|
||||||
mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
|
mv build/app/outputs/flutter-apk/app-armeabi-v7a-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
|
||||||
|
;;
|
||||||
|
x86_64-linux-android)
|
||||||
|
mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64
|
||||||
|
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/
|
||||||
|
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
|
||||||
|
# build flutter
|
||||||
|
pushd flutter
|
||||||
|
flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-x64 --split-per-abi
|
||||||
|
mv build/app/outputs/flutter-apk/app-x86_64-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
|
||||||
|
;;
|
||||||
|
i686-linux-android)
|
||||||
|
mkdir -p ./flutter/android/app/src/main/jniLibs/x86
|
||||||
|
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/
|
||||||
|
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
|
||||||
|
# build flutter
|
||||||
|
pushd flutter
|
||||||
|
flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-x86 --split-per-abi
|
||||||
|
mv build/app/outputs/flutter-apk/app-x86-${{ matrix.job.reltype }}.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
popd
|
popd
|
||||||
mkdir -p signed-apk; pushd signed-apk
|
mkdir -p signed-apk; pushd signed-apk
|
||||||
mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk .
|
mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk .
|
||||||
|
|
||||||
- uses: r0adkll/sign-android-release@v1
|
- uses: r0adkll/sign-android-release@v1
|
||||||
name: Sign app APK
|
name: Sign app APK
|
||||||
@@ -967,6 +1105,179 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
|
signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
|
||||||
|
|
||||||
|
build-rustdesk-android-universal:
|
||||||
|
needs: [build-rustdesk-android]
|
||||||
|
name: build rustdesk android universal apk
|
||||||
|
if: ${{ inputs.upload-artifact }}
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
env:
|
||||||
|
reltype: release
|
||||||
|
x86_target: "" # can be ",android-x86"
|
||||||
|
suffix: ""
|
||||||
|
steps:
|
||||||
|
- name: Free Disk Space (Ubuntu)
|
||||||
|
uses: jlumbroso/free-disk-space@main
|
||||||
|
with:
|
||||||
|
tool-cache: false
|
||||||
|
android: false
|
||||||
|
dotnet: true
|
||||||
|
haskell: true
|
||||||
|
large-packages: false
|
||||||
|
docker-images: true
|
||||||
|
swap-storage: false
|
||||||
|
|
||||||
|
- name: Export GitHub Actions cache environment variables
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
|
||||||
|
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
clang \
|
||||||
|
cmake \
|
||||||
|
curl \
|
||||||
|
gcc-multilib \
|
||||||
|
git \
|
||||||
|
g++ \
|
||||||
|
g++-multilib \
|
||||||
|
libappindicator3-dev \
|
||||||
|
libasound2-dev \
|
||||||
|
libc6-dev \
|
||||||
|
libclang-10-dev \
|
||||||
|
libgstreamer1.0-dev \
|
||||||
|
libgstreamer-plugins-base1.0-dev \
|
||||||
|
libgtk-3-dev \
|
||||||
|
libpam0g-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libva-dev \
|
||||||
|
libvdpau-dev \
|
||||||
|
libxcb-randr0-dev \
|
||||||
|
libxcb-shape0-dev \
|
||||||
|
libxcb-xfixes0-dev \
|
||||||
|
libxdo-dev \
|
||||||
|
libxfixes-dev \
|
||||||
|
llvm-10-dev \
|
||||||
|
nasm \
|
||||||
|
ninja-build \
|
||||||
|
openjdk-11-jdk-headless \
|
||||||
|
pkg-config \
|
||||||
|
tree \
|
||||||
|
wget
|
||||||
|
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install flutter
|
||||||
|
uses: subosito/flutter-action@v2
|
||||||
|
with:
|
||||||
|
channel: "stable"
|
||||||
|
flutter-version: ${{ env.ANDROID_FLUTTER_VERSION }}
|
||||||
|
|
||||||
|
- name: Restore bridge files
|
||||||
|
uses: actions/download-artifact@master
|
||||||
|
with:
|
||||||
|
name: bridge-artifact
|
||||||
|
path: ./
|
||||||
|
|
||||||
|
- name: Download Rustdesk library from Artifacts
|
||||||
|
uses: actions/download-artifact@master
|
||||||
|
with:
|
||||||
|
name: librustdesk.so.aarch64-linux-android
|
||||||
|
path: ./flutter/android/app/src/main/jniLibs/arm64-v8a
|
||||||
|
|
||||||
|
- name: Download Rustdesk library from Artifacts
|
||||||
|
uses: actions/download-artifact@master
|
||||||
|
with:
|
||||||
|
name: librustdesk.so.armv7-linux-androideabi
|
||||||
|
path: ./flutter/android/app/src/main/jniLibs/armeabi-v7a
|
||||||
|
|
||||||
|
- name: Download Rustdesk library from Artifacts
|
||||||
|
uses: actions/download-artifact@master
|
||||||
|
with:
|
||||||
|
name: librustdesk.so.x86_64-linux-android
|
||||||
|
path: ./flutter/android/app/src/main/jniLibs/x86_64
|
||||||
|
|
||||||
|
- name: Download Rustdesk library from Artifacts
|
||||||
|
if: ${{ env.reltype == 'debug' }}
|
||||||
|
uses: actions/download-artifact@master
|
||||||
|
with:
|
||||||
|
name: librustdesk.so.i686-linux-android
|
||||||
|
path: ./flutter/android/app/src/main/jniLibs/x86
|
||||||
|
|
||||||
|
- name: fix android for flutter 3.13
|
||||||
|
if: $${{ env.ANDROID_FLUTTER_VERSION == '3.13.9' }}
|
||||||
|
run: |
|
||||||
|
sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml
|
||||||
|
cd flutter/lib
|
||||||
|
find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'
|
||||||
|
|
||||||
|
- name: Build rustdesk
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64
|
||||||
|
run: |
|
||||||
|
export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH
|
||||||
|
# temporary use debug sign config
|
||||||
|
sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle
|
||||||
|
mv ./flutter/android/app/src/main/jniLibs/arm64-v8a/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
|
||||||
|
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/
|
||||||
|
mv ./flutter/android/app/src/main/jniLibs/armeabi-v7a/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
|
||||||
|
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/
|
||||||
|
mv ./flutter/android/app/src/main/jniLibs/x86_64/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
|
||||||
|
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/
|
||||||
|
if [ "${{ env.reltype }}" = "debug" ]; then
|
||||||
|
mv ./flutter/android/app/src/main/jniLibs/x86/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
|
||||||
|
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/
|
||||||
|
fi
|
||||||
|
# build flutter
|
||||||
|
pushd flutter
|
||||||
|
flutter build apk "--${{ env.reltype }}" --target-platform android-arm64,android-arm,android-x64${{ env.x86_target }}
|
||||||
|
popd
|
||||||
|
mkdir -p signed-apk
|
||||||
|
mv ./flutter/build/app/outputs/flutter-apk/app-${{ env.reltype }}.apk signed-apk/rustdesk-${{ env.VERSION }}-universal${{ env.suffix }}.apk
|
||||||
|
|
||||||
|
- uses: r0adkll/sign-android-release@v1
|
||||||
|
name: Sign app APK
|
||||||
|
if: env.ANDROID_SIGNING_KEY != null
|
||||||
|
id: sign-rustdesk
|
||||||
|
with:
|
||||||
|
releaseDirectory: ./signed-apk
|
||||||
|
signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }}
|
||||||
|
alias: ${{ secrets.ANDROID_ALIAS }}
|
||||||
|
keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }}
|
||||||
|
keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||||
|
env:
|
||||||
|
# override default build-tools version (29.0.3) -- optional
|
||||||
|
BUILD_TOOLS_VERSION: "30.0.2"
|
||||||
|
|
||||||
|
- name: Upload Artifacts
|
||||||
|
if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
|
||||||
|
uses: actions/upload-artifact@master
|
||||||
|
with:
|
||||||
|
name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk
|
||||||
|
path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}}
|
||||||
|
|
||||||
|
- name: Publish signed apk package
|
||||||
|
if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
prerelease: true
|
||||||
|
tag_name: ${{ env.TAG_NAME }}
|
||||||
|
files: |
|
||||||
|
${{steps.sign-rustdesk.outputs.signedReleaseFile}}
|
||||||
|
|
||||||
|
- name: Publish unsigned apk package
|
||||||
|
if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true'
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
prerelease: true
|
||||||
|
tag_name: ${{ env.TAG_NAME }}
|
||||||
|
files: |
|
||||||
|
signed-apk/rustdesk-${{ env.VERSION }}-universal${{ env.suffix }}.apk
|
||||||
|
|
||||||
build-rustdesk-linux:
|
build-rustdesk-linux:
|
||||||
needs: [generate-bridge-linux]
|
needs: [generate-bridge-linux]
|
||||||
name: build rustdesk linux ${{ matrix.job.target }}
|
name: build rustdesk linux ${{ matrix.job.target }}
|
||||||
@@ -1010,7 +1321,7 @@ jobs:
|
|||||||
sudo apt-get install -y nasm qemu-user-static
|
sudo apt-get install -y nasm qemu-user-static
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set Swap Space
|
- name: Set Swap Space
|
||||||
if: ${{ matrix.job.arch == 'x86_64' }}
|
if: ${{ matrix.job.arch == 'x86_64' }}
|
||||||
@@ -1054,11 +1365,24 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
vcpkgDirectory: /opt/artifacts/vcpkg
|
vcpkgDirectory: /opt/artifacts/vcpkg
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
||||||
|
doNotCache: false
|
||||||
|
|
||||||
- name: Install vcpkg dependencies
|
- name: Install vcpkg dependencies
|
||||||
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
|
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
|
||||||
run: |
|
run: |
|
||||||
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
|
if ! $VCPKG_ROOT/vcpkg \
|
||||||
|
install \
|
||||||
|
--triplet ${{ matrix.job.vcpkg-triplet }} \
|
||||||
|
--x-install-root="$VCPKG_ROOT/installed"; then
|
||||||
|
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
|
||||||
|
echo "$_1:"
|
||||||
|
echo "======"
|
||||||
|
cat "$_1"
|
||||||
|
echo "======"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Restore bridge files
|
- name: Restore bridge files
|
||||||
@@ -1290,6 +1614,7 @@ jobs:
|
|||||||
deb_arch: amd64,
|
deb_arch: amd64,
|
||||||
sciter_arch: x64,
|
sciter_arch: x64,
|
||||||
vcpkg-triplet: x64-linux,
|
vcpkg-triplet: x64-linux,
|
||||||
|
extra_features: ",hwcodec",
|
||||||
}
|
}
|
||||||
- {
|
- {
|
||||||
arch: armv7,
|
arch: armv7,
|
||||||
@@ -1299,6 +1624,7 @@ jobs:
|
|||||||
deb_arch: armhf,
|
deb_arch: armhf,
|
||||||
sciter_arch: arm32,
|
sciter_arch: arm32,
|
||||||
vcpkg-triplet: arm-linux,
|
vcpkg-triplet: arm-linux,
|
||||||
|
extra_features: "",
|
||||||
}
|
}
|
||||||
steps:
|
steps:
|
||||||
- name: Export GitHub Actions cache environment variables
|
- name: Export GitHub Actions cache environment variables
|
||||||
@@ -1308,25 +1634,8 @@ jobs:
|
|||||||
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
|
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
|
||||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y \
|
|
||||||
cmake \
|
|
||||||
crossbuild-essential-armhf \
|
|
||||||
curl \
|
|
||||||
g++ \
|
|
||||||
gcc \
|
|
||||||
git \
|
|
||||||
nasm \
|
|
||||||
ninja-build \
|
|
||||||
pkg-config \
|
|
||||||
unzip \
|
|
||||||
xz-utils \
|
|
||||||
zip
|
|
||||||
|
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Free Space
|
- name: Free Space
|
||||||
run: |
|
run: |
|
||||||
@@ -1336,7 +1645,7 @@ jobs:
|
|||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@v1
|
uses: dtolnay/rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_VERSION }}
|
toolchain: ${{ env.SCITER_RUST_VERSION }}
|
||||||
targets: ${{ matrix.job.target }}
|
targets: ${{ matrix.job.target }}
|
||||||
components: "rustfmt"
|
components: "rustfmt"
|
||||||
|
|
||||||
@@ -1345,18 +1654,6 @@ jobs:
|
|||||||
RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
|
RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
|
||||||
echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV
|
echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Setup vcpkg with Github Actions binary cache
|
|
||||||
uses: lukka/run-vcpkg@v11
|
|
||||||
with:
|
|
||||||
vcpkgDirectory: /opt/artifacts/vcpkg
|
|
||||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
|
||||||
|
|
||||||
- name: Install vcpkg dependencies
|
|
||||||
run: |
|
|
||||||
cp $PWD/res/vcpkg/linux.cmake $VCPKG_ROOT/scripts/toolchains/linux.cmake
|
|
||||||
$VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- uses: rustdesk-org/run-on-arch-action@amd64-support
|
- uses: rustdesk-org/run-on-arch-action@amd64-support
|
||||||
name: Build rustdesk sciter binary for ${{ matrix.job.arch }}
|
name: Build rustdesk sciter binary for ${{ matrix.job.arch }}
|
||||||
id: vcpkg
|
id: vcpkg
|
||||||
@@ -1368,14 +1665,12 @@ jobs:
|
|||||||
ls -l "${PWD}"
|
ls -l "${PWD}"
|
||||||
dockerRunArgs: |
|
dockerRunArgs: |
|
||||||
--volume "${PWD}:/workspace"
|
--volume "${PWD}:/workspace"
|
||||||
--volume "/opt/artifacts:/opt/artifacts"
|
|
||||||
shell: /bin/bash
|
shell: /bin/bash
|
||||||
install: |
|
install: |
|
||||||
apt-get update -y
|
apt-get update
|
||||||
apt-get install -y \
|
apt-get install -y \
|
||||||
build-essential \
|
build-essential \
|
||||||
clang \
|
clang \
|
||||||
cmake \
|
|
||||||
curl \
|
curl \
|
||||||
gcc \
|
gcc \
|
||||||
git \
|
git \
|
||||||
@@ -1398,39 +1693,93 @@ jobs:
|
|||||||
libxcb-xfixes0-dev \
|
libxcb-xfixes0-dev \
|
||||||
libxdo-dev \
|
libxdo-dev \
|
||||||
libxfixes-dev \
|
libxfixes-dev \
|
||||||
nasm \
|
|
||||||
ninja-build \
|
ninja-build \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
python3 \
|
python3 \
|
||||||
|
python3.7 \
|
||||||
rpm \
|
rpm \
|
||||||
unzip \
|
unzip \
|
||||||
wget \
|
wget \
|
||||||
xz-utils
|
xz-utils \
|
||||||
run: |
|
zip
|
||||||
# disable git safe.directory
|
# arm-linux needs CMake and vcokg built from source as there
|
||||||
git config --global --add safe.directory "*"
|
# are no prebuilts available from Kitware and Microsoft
|
||||||
|
if [ "${{ matrix.job.vcpkg-triplet }}" = "arm-linux" ]; then
|
||||||
|
# install gcc/g++ 8 for vcpkg and OpenSSL headers for CMake
|
||||||
|
apt-get install -y gcc-8 g++-8 libssl-dev
|
||||||
|
# bootstrap CMake amd add it to PATH
|
||||||
|
git clone --depth 1 https://github.com/kitware/cmake -b "v${{ env.SCITER_ARMV7_CMAKE_VERSION }}" /tmp/cmake
|
||||||
|
pushd /tmp/cmake
|
||||||
|
./bootstrap --generator='Unix Makefiles' "--prefix=/opt/cmake-${{ env.SCITER_ARMV7_CMAKE_VERSION }}-linux-armhf"
|
||||||
|
make -j1 install
|
||||||
|
popd
|
||||||
|
rm -rf /tmp/cmake
|
||||||
|
export PATH="/opt/cmake-${{ env.SCITER_ARMV7_CMAKE_VERSION }}-linux-armhf/bin:$PATH"
|
||||||
|
fi
|
||||||
|
# bootstrap vcpkg and set VCPKG_ROOT
|
||||||
|
export VCPKG_ROOT=/opt/artifacts/vcpkg
|
||||||
|
mkdir -p /opt/artifacts
|
||||||
|
pushd /opt/artifacts
|
||||||
|
rm -rf vcpkg
|
||||||
|
git clone https://github.com/microsoft/vcpkg
|
||||||
|
pushd vcpkg
|
||||||
|
git reset --hard ${{ env.VCPKG_COMMIT_ID }}
|
||||||
|
# build vcpkg helper executable with gcc-8 for arm-linux but use prebuilt one on x64-linux
|
||||||
|
if [ "${{ matrix.job.vcpkg-triplet }}" = "arm-linux" ]; then
|
||||||
|
CC=/usr/bin/gcc-8 CXX=/usr/bin/g++-8 sh bootstrap-vcpkg.sh -disableMetrics
|
||||||
|
else
|
||||||
|
sh bootstrap-vcpkg.sh -disableMetrics
|
||||||
|
fi
|
||||||
|
popd
|
||||||
|
popd
|
||||||
# rust
|
# rust
|
||||||
pushd /opt
|
pushd /opt
|
||||||
# do not use rustup, because memory overflow in qemu
|
# do not use rustup, because memory overflow in qemu
|
||||||
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
|
wget --output-document rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
|
||||||
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
|
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
|
||||||
cd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }} && ./install.sh
|
pushd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
|
||||||
|
./install.sh
|
||||||
|
popd
|
||||||
rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
|
rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
|
||||||
# edit config
|
popd
|
||||||
|
# install newer nasm for aom
|
||||||
|
wget --output-document nasm.deb "http://ftp.us.debian.org/debian/pool/main/n/nasm/nasm_${{ env.SCITER_NASM_DEBVERSION }}_${{ matrix.job.deb_arch }}.deb"
|
||||||
|
dpkg -i nasm.deb
|
||||||
|
rm -f nasm.deb
|
||||||
|
run: |
|
||||||
|
# disable git safe.directory
|
||||||
|
git config --global --add safe.directory "*"
|
||||||
|
# set python3.7 as default python3
|
||||||
|
update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1
|
||||||
|
# add built CMake to PATH and set VCPKG_FORCE_SYSTEM_BINARIES Afor arm-linux
|
||||||
|
if [ "${{ matrix.job.vcpkg-triplet }}" = "arm-linux" ]; then
|
||||||
|
export PATH="/opt/cmake-${{ env.SCITER_ARMV7_CMAKE_VERSION }}-linux-armhf/bin:$PATH"
|
||||||
|
export VCPKG_FORCE_SYSTEM_BINARIES=1
|
||||||
|
fi
|
||||||
|
# edit cargo config
|
||||||
mkdir -p ~/.cargo/
|
mkdir -p ~/.cargo/
|
||||||
echo """
|
echo """
|
||||||
[source.crates-io]
|
[source.crates-io]
|
||||||
registry = 'https://github.com/rust-lang/crates.io-index'
|
registry = 'https://github.com/rust-lang/crates.io-index'
|
||||||
""" > ~/.cargo/config
|
""" > ~/.cargo/config
|
||||||
cat ~/.cargo/config
|
cat ~/.cargo/config
|
||||||
|
# install dependencies from vcpkg
|
||||||
# build
|
|
||||||
pushd /workspace
|
|
||||||
python3 ./res/inline-sciter.py
|
|
||||||
export VCPKG_ROOT=/opt/artifacts/vcpkg
|
export VCPKG_ROOT=/opt/artifacts/vcpkg
|
||||||
|
if ! $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed"; then
|
||||||
|
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
|
||||||
|
echo "$_1:"
|
||||||
|
echo "======"
|
||||||
|
cat "$_1"
|
||||||
|
echo "======"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# build rustdesk
|
||||||
|
python3 ./res/inline-sciter.py
|
||||||
export CARGO_INCREMENTAL=0
|
export CARGO_INCREMENTAL=0
|
||||||
cargo build --features inline --release --bins --jobs 1
|
cargo build --features inline${{ matrix.job.extra_features }} --release --bins --jobs 1
|
||||||
# package
|
# make debian package
|
||||||
mkdir -p ./Release
|
mkdir -p ./Release
|
||||||
mv ./target/release/rustdesk ./Release/rustdesk
|
mv ./target/release/rustdesk ./Release/rustdesk
|
||||||
wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/${{ matrix.job.sciter_arch }}/libsciter-gtk.so
|
wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/${{ matrix.job.sciter_arch }}/libsciter-gtk.so
|
||||||
@@ -1470,17 +1819,11 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
job:
|
job:
|
||||||
- {
|
- { target: x86_64-unknown-linux-gnu, arch: x86_64 }
|
||||||
target: x86_64-unknown-linux-gnu,
|
- { target: aarch64-unknown-linux-gnu, arch: aarch64 }
|
||||||
arch: x86_64,
|
|
||||||
}
|
|
||||||
- {
|
|
||||||
target: aarch64-unknown-linux-gnu,
|
|
||||||
arch: aarch64,
|
|
||||||
}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Download Binary
|
- name: Download Binary
|
||||||
uses: actions/download-artifact@master
|
uses: actions/download-artifact@master
|
||||||
@@ -1552,7 +1895,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Download Binary
|
- name: Download Binary
|
||||||
uses: actions/download-artifact@master
|
uses: actions/download-artifact@master
|
||||||
@@ -1628,7 +1971,7 @@ jobs:
|
|||||||
RELEASE_NAME: web-basic
|
RELEASE_NAME: web-basic
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Prepare env
|
- name: Prepare env
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/playground.yml
vendored
2
.github/workflows/playground.yml
vendored
@@ -18,7 +18,7 @@ env:
|
|||||||
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
# vcpkg version: 2024.06.15
|
# vcpkg version: 2024.06.15
|
||||||
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
|
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
|
||||||
VERSION: "1.2.7"
|
VERSION: "1.3.0"
|
||||||
NDK_VERSION: "r26d"
|
NDK_VERSION: "r26d"
|
||||||
#signing keys env variable checks
|
#signing keys env variable checks
|
||||||
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
|
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
|
||||||
|
|||||||
363
Cargo.lock
generated
363
Cargo.lock
generated
@@ -123,7 +123,7 @@ checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "android-wakelock"
|
name = "android-wakelock"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/21pages/android-wakelock#d0292e5a367e627c4fa6f1ca6bdfad005dca7d90"
|
source = "git+https://github.com/rustdesk-org/android-wakelock#d0292e5a367e627c4fa6f1ca6bdfad005dca7d90"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jni 0.21.1",
|
"jni 0.21.1",
|
||||||
"log",
|
"log",
|
||||||
@@ -224,7 +224,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "arboard"
|
name = "arboard"
|
||||||
version = "3.4.0"
|
version = "3.4.0"
|
||||||
source = "git+https://github.com/rustdesk-org/arboard#27b4e503caa70ec6306e5270461429f2cf907ad6"
|
source = "git+https://github.com/rustdesk-org/arboard#a04bdb1b368a99691822c33bf0f7ed497d6a7a35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clipboard-win",
|
"clipboard-win",
|
||||||
"core-graphics 0.23.2",
|
"core-graphics 0.23.2",
|
||||||
@@ -234,24 +234,13 @@ dependencies = [
|
|||||||
"objc2-app-kit",
|
"objc2-app-kit",
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"resvg",
|
"serde 1.0.203",
|
||||||
|
"serde_derive",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
"wl-clipboard-rs",
|
"wl-clipboard-rs",
|
||||||
"x11rb 0.13.1",
|
"x11rb 0.13.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arrayref"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arrayvec"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-broadcast"
|
name = "async-broadcast"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@@ -987,11 +976,14 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "clipboard-master"
|
name = "clipboard-master"
|
||||||
version = "4.0.0-beta.6"
|
version = "4.0.0-beta.6"
|
||||||
source = "git+https://github.com/rustdesk-org/clipboard-master#5268c7b3d7728699566ad863da0911f249706f8c"
|
source = "git+https://github.com/rustdesk-org/clipboard-master#4fb62e5b62fb6350d82b571ec7ba94b3cd466695"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"objc",
|
"objc",
|
||||||
"objc-foundation",
|
"objc-foundation",
|
||||||
"objc_id",
|
"objc_id",
|
||||||
|
"wayland-client",
|
||||||
|
"wayland-protocols",
|
||||||
|
"wayland-protocols-wlr",
|
||||||
"windows-win",
|
"windows-win",
|
||||||
"wl-clipboard-rs",
|
"wl-clipboard-rs",
|
||||||
"x11-clipboard 0.9.2",
|
"x11-clipboard 0.9.2",
|
||||||
@@ -1000,9 +992,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clipboard-win"
|
name = "clipboard-win"
|
||||||
version = "5.3.1"
|
version = "5.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad"
|
checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"error-code",
|
"error-code",
|
||||||
]
|
]
|
||||||
@@ -1526,12 +1518,6 @@ dependencies = [
|
|||||||
"dasp_sample",
|
"dasp_sample",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "data-url"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dbus"
|
name = "dbus"
|
||||||
version = "0.9.7"
|
version = "0.9.7"
|
||||||
@@ -1691,7 +1677,7 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libloading 0.7.4",
|
"libloading 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2081,12 +2067,6 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "float-cmp"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -2143,29 +2123,6 @@ dependencies = [
|
|||||||
"libm",
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fontconfig-parser"
|
|
||||||
version = "0.5.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d"
|
|
||||||
dependencies = [
|
|
||||||
"roxmltree 0.19.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fontdb"
|
|
||||||
version = "0.18.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770"
|
|
||||||
dependencies = [
|
|
||||||
"fontconfig-parser",
|
|
||||||
"log",
|
|
||||||
"memmap2",
|
|
||||||
"slotmap",
|
|
||||||
"tinyvec",
|
|
||||||
"ttf-parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -3087,8 +3044,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hwcodec"
|
name = "hwcodec"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
source = "git+https://github.com/21pages/hwcodec#89879f2f02c6f74e88a4a43744a1153aec5b7e7f"
|
source = "git+https://github.com/rustdesk-org/hwcodec#6abd1898f3a03481ed0c038507b5218d6ea94267"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen 0.59.2",
|
"bindgen 0.59.2",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -3213,16 +3170,10 @@ dependencies = [
|
|||||||
"tiff",
|
"tiff",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "imagesize"
|
|
||||||
version = "0.12.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "impersonate_system"
|
name = "impersonate_system"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/21pages/impersonate-system#2f429010a5a10b1fe5eceb553c6672fd53d20167"
|
source = "git+https://github.com/rustdesk-org/impersonate-system#2f429010a5a10b1fe5eceb553c6672fd53d20167"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
@@ -3462,16 +3413,6 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "kurbo"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c"
|
|
||||||
dependencies = [
|
|
||||||
"arrayvec",
|
|
||||||
"smallvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -3558,7 +3499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"windows-targets 0.48.5",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3733,7 +3674,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "machine-uid"
|
name = "machine-uid"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/21pages/machine-uid#381ff579c1dc3a6c54db9dfec47c44bcb0246542"
|
source = "git+https://github.com/rustdesk-org/machine-uid#381ff579c1dc3a6c54db9dfec47c44bcb0246542"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen 0.59.2",
|
"bindgen 0.59.2",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -3777,15 +3718,6 @@ version = "2.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memmap2"
|
|
||||||
version = "0.9.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
@@ -3847,14 +3779,6 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mouce"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "git+https://github.com/rustdesk-org/mouce.git#177625a395cd8fa73964714d0039535cb9b47893"
|
|
||||||
dependencies = [
|
|
||||||
"glob",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "muda"
|
name = "muda"
|
||||||
version = "0.13.5"
|
version = "0.13.5"
|
||||||
@@ -4732,15 +4656,9 @@ version = "0.7.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
|
checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"siphasher 0.2.3",
|
"siphasher",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pico-args"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.5"
|
version = "1.1.5"
|
||||||
@@ -5065,6 +4983,15 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-xml"
|
||||||
|
version = "0.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "0.6.13"
|
version = "0.6.13"
|
||||||
@@ -5414,31 +5341,6 @@ dependencies = [
|
|||||||
"winreg 0.50.0",
|
"winreg 0.50.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "resvg"
|
|
||||||
version = "0.42.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051"
|
|
||||||
dependencies = [
|
|
||||||
"gif",
|
|
||||||
"jpeg-decoder",
|
|
||||||
"log",
|
|
||||||
"pico-args",
|
|
||||||
"rgb",
|
|
||||||
"svgtypes",
|
|
||||||
"tiny-skia",
|
|
||||||
"usvg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rgb"
|
|
||||||
version = "0.8.40"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.8"
|
version = "0.17.8"
|
||||||
@@ -5463,18 +5365,6 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roxmltree"
|
|
||||||
version = "0.19.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roxmltree"
|
|
||||||
version = "0.20.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rpassword"
|
name = "rpassword"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@@ -5572,7 +5462,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustdesk"
|
name = "rustdesk"
|
||||||
version = "1.2.7"
|
version = "1.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-wakelock",
|
"android-wakelock",
|
||||||
"android_logger",
|
"android_logger",
|
||||||
@@ -5618,7 +5508,6 @@ dependencies = [
|
|||||||
"libpulse-simple-binding",
|
"libpulse-simple-binding",
|
||||||
"mac_address",
|
"mac_address",
|
||||||
"magnum-opus",
|
"magnum-opus",
|
||||||
"mouce",
|
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"objc",
|
"objc",
|
||||||
"objc_id",
|
"objc_id",
|
||||||
@@ -5670,7 +5559,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustdesk-portable-packer"
|
name = "rustdesk-portable-packer"
|
||||||
version = "1.2.7"
|
version = "1.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brotli",
|
"brotli",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
@@ -5853,22 +5742,6 @@ version = "1.0.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustybuzz"
|
|
||||||
version = "0.14.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.6.0",
|
|
||||||
"bytemuck",
|
|
||||||
"smallvec",
|
|
||||||
"ttf-parser",
|
|
||||||
"unicode-bidi-mirroring",
|
|
||||||
"unicode-ccc",
|
|
||||||
"unicode-properties",
|
|
||||||
"unicode-script",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@@ -5905,7 +5778,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "sciter-rs"
|
name = "sciter-rs"
|
||||||
version = "0.5.57"
|
version = "0.5.57"
|
||||||
source = "git+https://github.com/open-trade/rust-sciter?branch=dyn#fab913b7c2e779b05c249b0c5de5a08759b2c15d"
|
source = "git+https://github.com/open-trade/rust-sciter?branch=dyn#5322f3a755a0e6bf999fbc60d1efc35246c0f821"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -6159,27 +6032,12 @@ version = "0.3.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "simplecss"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
|
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "siphasher"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@@ -6189,15 +6047,6 @@ dependencies = [
|
|||||||
"autocfg 1.3.0",
|
"autocfg 1.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "slotmap"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
|
|
||||||
dependencies = [
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
@@ -6268,15 +6117,6 @@ version = "0.2.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
|
checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strict-num"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
|
||||||
dependencies = [
|
|
||||||
"float-cmp",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -6338,16 +6178,6 @@ version = "2.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "svgtypes"
|
|
||||||
version = "0.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c"
|
|
||||||
dependencies = [
|
|
||||||
"kurbo",
|
|
||||||
"siphasher 1.0.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "0.15.44"
|
version = "0.15.44"
|
||||||
@@ -6399,7 +6229,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "sysinfo"
|
name = "sysinfo"
|
||||||
version = "0.29.10"
|
version = "0.29.10"
|
||||||
source = "git+https://github.com/rustdesk-org/sysinfo#f45dcc6510d48c3a1401c5a33eedccc8899f67b2"
|
source = "git+https://github.com/rustdesk-org/sysinfo?branch=rlim_max#90b1705d909a4902dbbbdea37ee64db17841077d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -6593,11 +6423,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tfc"
|
name = "tfc"
|
||||||
version = "0.6.1"
|
version = "0.7.0"
|
||||||
source = "git+https://github.com/rustdesk-org/The-Fat-Controller#9dd86151525fd010dc93f6bc9b6aedd1a75cc342"
|
source = "git+https://github.com/rustdesk-org/The-Fat-Controller?branch=history/rebase_upstream_20240722#de9c8ba480f166a9fc90aaa47bb0e84b443ea9c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"core-graphics 0.22.3",
|
"core-graphics 0.23.2",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
"x11 2.19.0",
|
"x11 2.19.0",
|
||||||
@@ -6687,32 +6517,6 @@ dependencies = [
|
|||||||
"time-core",
|
"time-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tiny-skia"
|
|
||||||
version = "0.11.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
|
|
||||||
dependencies = [
|
|
||||||
"arrayref",
|
|
||||||
"arrayvec",
|
|
||||||
"bytemuck",
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"log",
|
|
||||||
"png",
|
|
||||||
"tiny-skia-path",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tiny-skia-path"
|
|
||||||
version = "0.11.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
|
|
||||||
dependencies = [
|
|
||||||
"arrayref",
|
|
||||||
"bytemuck",
|
|
||||||
"strict-num",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
@@ -7004,12 +6808,6 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ttf-parser"
|
|
||||||
version = "0.21.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
@@ -7082,18 +6880,6 @@ version = "0.3.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-bidi-mirroring"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ccc"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
@@ -7109,30 +6895,12 @@ dependencies = [
|
|||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-properties"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-script"
|
|
||||||
version = "0.5.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-vo"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@@ -7195,33 +6963,6 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "usvg"
|
|
||||||
version = "0.42.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.22.1",
|
|
||||||
"data-url",
|
|
||||||
"flate2",
|
|
||||||
"fontdb",
|
|
||||||
"imagesize",
|
|
||||||
"kurbo",
|
|
||||||
"log",
|
|
||||||
"pico-args",
|
|
||||||
"roxmltree 0.20.0",
|
|
||||||
"rustybuzz",
|
|
||||||
"simplecss",
|
|
||||||
"siphasher 1.0.1",
|
|
||||||
"strict-num",
|
|
||||||
"svgtypes",
|
|
||||||
"tiny-skia-path",
|
|
||||||
"unicode-bidi",
|
|
||||||
"unicode-script",
|
|
||||||
"unicode-vo",
|
|
||||||
"xmlwriter",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf16string"
|
name = "utf16string"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -7309,7 +7050,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "wallpaper"
|
name = "wallpaper"
|
||||||
version = "3.2.0"
|
version = "3.2.0"
|
||||||
source = "git+https://github.com/21pages/wallpaper.rs#ce4a0cd3f58327c7cc44d15a63706fb0c022bacf"
|
source = "git+https://github.com/rustdesk-org/wallpaper.rs#ce4a0cd3f58327c7cc44d15a63706fb0c022bacf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
"enquote",
|
"enquote",
|
||||||
@@ -7414,9 +7155,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-backend"
|
name = "wayland-backend"
|
||||||
version = "0.3.4"
|
version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34e9e6b6d4a2bb4e7e69433e0b35c7923b95d4dc8503a84d25ec917a4bbfdf07"
|
checksum = "f90e11ce2ca99c97b940ee83edbae9da2d56a08f9ea8158550fd77fa31722993"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"downcast-rs",
|
"downcast-rs",
|
||||||
@@ -7428,9 +7169,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-client"
|
name = "wayland-client"
|
||||||
version = "0.31.3"
|
version = "0.31.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e63801c85358a431f986cffa74ba9599ff571fc5774ac113ed3b490c19a1133"
|
checksum = "7e321577a0a165911bdcfb39cf029302479d7527b517ee58ab0f6ad09edf0943"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"rustix 0.38.34",
|
"rustix 0.38.34",
|
||||||
@@ -7440,9 +7181,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-protocols"
|
name = "wayland-protocols"
|
||||||
version = "0.32.1"
|
version = "0.32.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83d0f1056570486e26a3773ec633885124d79ae03827de05ba6c85f79904026c"
|
checksum = "62989625a776e827cc0f15d41444a3cea5205b963c3a25be48ae1b52d6b4daaa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
@@ -7452,9 +7193,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-protocols-wlr"
|
name = "wayland-protocols-wlr"
|
||||||
version = "0.3.1"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7dab47671043d9f5397035975fe1cac639e5bca5cc0b3c32d09f01612e34d24"
|
checksum = "fd993de54a40a40fbe5601d9f1fbcaef0aebcc5fda447d7dc8f6dcbaae4f8953"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
@@ -7465,20 +7206,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-scanner"
|
name = "wayland-scanner"
|
||||||
version = "0.31.2"
|
version = "0.31.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67da50b9f80159dec0ea4c11c13e24ef9e7574bd6ce24b01860a175010cea565"
|
checksum = "d7b56f89937f1cf2ee1f1259cf2936a17a1f45d8f0aa1019fae6d470d304cfa6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.86",
|
"proc-macro2 1.0.86",
|
||||||
"quick-xml 0.31.0",
|
"quick-xml 0.34.0",
|
||||||
"quote 1.0.36",
|
"quote 1.0.36",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-sys"
|
name = "wayland-sys"
|
||||||
version = "0.31.2"
|
version = "0.31.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "105b1842da6554f91526c14a2a2172897b7f745a805d62af4ce698706be79c12"
|
checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dlib",
|
"dlib",
|
||||||
"log",
|
"log",
|
||||||
@@ -7498,7 +7239,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "webm"
|
name = "webm"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "git+https://github.com/21pages/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64"
|
source = "git+https://github.com/rustdesk-org/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"webm-sys",
|
"webm-sys",
|
||||||
]
|
]
|
||||||
@@ -7506,7 +7247,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "webm-sys"
|
name = "webm-sys"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
source = "git+https://github.com/21pages/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64"
|
source = "git+https://github.com/rustdesk-org/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
@@ -8220,12 +7961,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xmlwriter"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus"
|
name = "zbus"
|
||||||
version = "3.15.2"
|
version = "3.15.2"
|
||||||
|
|||||||
11
Cargo.toml
11
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustdesk"
|
name = "rustdesk"
|
||||||
version = "1.2.7"
|
version = "1.3.0"
|
||||||
authors = ["rustdesk <info@rustdesk.com>"]
|
authors = ["rustdesk <info@rustdesk.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build= "build.rs"
|
build= "build.rs"
|
||||||
@@ -90,7 +90,7 @@ enigo = { path = "libs/enigo", features = [ "with_serde" ] }
|
|||||||
clipboard = { path = "libs/clipboard" }
|
clipboard = { path = "libs/clipboard" }
|
||||||
ctrlc = "3.2"
|
ctrlc = "3.2"
|
||||||
# arboard = { version = "3.4.0", features = ["wayland-data-control"] }
|
# arboard = { version = "3.4.0", features = ["wayland-data-control"] }
|
||||||
arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control", "image-data"] }
|
arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] }
|
||||||
clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" }
|
clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" }
|
||||||
|
|
||||||
system_shutdown = "4.0"
|
system_shutdown = "4.0"
|
||||||
@@ -114,7 +114,7 @@ winapi = { version = "0.3", features = [
|
|||||||
winreg = "0.11"
|
winreg = "0.11"
|
||||||
windows-service = "0.6"
|
windows-service = "0.6"
|
||||||
virtual_display = { path = "libs/virtual_display" }
|
virtual_display = { path = "libs/virtual_display" }
|
||||||
impersonate_system = { git = "https://github.com/21pages/impersonate-system" }
|
impersonate_system = { git = "https://github.com/rustdesk-org/impersonate-system" }
|
||||||
shared_memory = "0.12"
|
shared_memory = "0.12"
|
||||||
tauri-winrt-notification = "0.1.2"
|
tauri-winrt-notification = "0.1.2"
|
||||||
runas = "1.2"
|
runas = "1.2"
|
||||||
@@ -138,7 +138,7 @@ image = "0.24"
|
|||||||
keepawake = { git = "https://github.com/rustdesk-org/keepawake-rs" }
|
keepawake = { git = "https://github.com/rustdesk-org/keepawake-rs" }
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "windows", target_os = "linux"))'.dependencies]
|
[target.'cfg(any(target_os = "windows", target_os = "linux"))'.dependencies]
|
||||||
wallpaper = { git = "https://github.com/21pages/wallpaper.rs" }
|
wallpaper = { git = "https://github.com/rustdesk-org/wallpaper.rs" }
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
|
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
|
||||||
# https://github.com/rustdesk/rustdesk-server-pro/issues/189, using native-tls for better tls support
|
# https://github.com/rustdesk/rustdesk-server-pro/issues/189, using native-tls for better tls support
|
||||||
@@ -152,7 +152,6 @@ psimple = { package = "libpulse-simple-binding", version = "2.27" }
|
|||||||
pulse = { package = "libpulse-binding", version = "2.27" }
|
pulse = { package = "libpulse-binding", version = "2.27" }
|
||||||
rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" }
|
rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" }
|
||||||
async-process = "1.7"
|
async-process = "1.7"
|
||||||
mouce = { git="https://github.com/rustdesk-org/mouce.git" }
|
|
||||||
evdev = { git="https://github.com/rustdesk-org/evdev" }
|
evdev = { git="https://github.com/rustdesk-org/evdev" }
|
||||||
dbus = "0.9"
|
dbus = "0.9"
|
||||||
dbus-crossroads = "0.5"
|
dbus-crossroads = "0.5"
|
||||||
@@ -166,7 +165,7 @@ once_cell = {version = "1.18", optional = true}
|
|||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android_logger = "0.13"
|
android_logger = "0.13"
|
||||||
jni = "0.21"
|
jni = "0.21"
|
||||||
android-wakelock = { git = "https://github.com/21pages/android-wakelock" }
|
android-wakelock = { git = "https://github.com/rustdesk-org/android-wakelock" }
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable"]
|
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable"]
|
||||||
|
|||||||
@@ -59,19 +59,19 @@ Please download Sciter dynamic library yourself.
|
|||||||
```sh
|
```sh
|
||||||
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
|
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
|
||||||
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
|
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
|
||||||
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
|
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libpam0g-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### openSUSE Tumbleweed
|
### openSUSE Tumbleweed
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
|
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel pam-devel
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fedora 28 (CentOS 8)
|
### Fedora 28 (CentOS 8)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel
|
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel gstreamer1-devel gstreamer1-plugins-base-devel pam-devel
|
||||||
```
|
```
|
||||||
|
|
||||||
### Arch (Manjaro)
|
### Arch (Manjaro)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ AppDir:
|
|||||||
id: rustdesk
|
id: rustdesk
|
||||||
name: rustdesk
|
name: rustdesk
|
||||||
icon: rustdesk
|
icon: rustdesk
|
||||||
version: 1.2.7
|
version: 1.3.0
|
||||||
exec: usr/lib/rustdesk/rustdesk
|
exec: usr/lib/rustdesk/rustdesk
|
||||||
exec_args: $@
|
exec_args: $@
|
||||||
apt:
|
apt:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ AppDir:
|
|||||||
id: rustdesk
|
id: rustdesk
|
||||||
name: rustdesk
|
name: rustdesk
|
||||||
icon: rustdesk
|
icon: rustdesk
|
||||||
version: 1.2.7
|
version: 1.3.0
|
||||||
exec: usr/lib/rustdesk/rustdesk
|
exec: usr/lib/rustdesk/rustdesk
|
||||||
exec_args: $@
|
exec_args: $@
|
||||||
apt:
|
apt:
|
||||||
@@ -37,6 +37,9 @@ AppDir:
|
|||||||
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted
|
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted
|
||||||
universe multiverse
|
universe multiverse
|
||||||
include:
|
include:
|
||||||
|
# https://github.com/rustdesk/rustdesk/issues/9103
|
||||||
|
# Because of APPDIR_LIBRARY_PATH, this libc6 is not used, use LD_PRELOAD: $APPDIR/usr/lib/x86_64-linux-gnu/libc.so.6 may help, If you have time, please have a try.
|
||||||
|
# We modify APPDIR_LIBRARY_PATH to use system lib first because gst crashed if not doing so, but you can try to change it.
|
||||||
- libc6:amd64
|
- libc6:amd64
|
||||||
- libgtk-3-0
|
- libgtk-3-0
|
||||||
- libxcb-randr0
|
- libxcb-randr0
|
||||||
|
|||||||
9
build.py
9
build.py
@@ -31,6 +31,11 @@ def get_deb_arch() -> str:
|
|||||||
return "amd64"
|
return "amd64"
|
||||||
return custom_arch
|
return custom_arch
|
||||||
|
|
||||||
|
def get_deb_extra_depends() -> str:
|
||||||
|
custom_arch = os.environ.get("DEB_ARCH")
|
||||||
|
if custom_arch == "armhf": # for arm32v7 libsciter-gtk.so
|
||||||
|
return ", libatomic1"
|
||||||
|
return ""
|
||||||
|
|
||||||
def system2(cmd):
|
def system2(cmd):
|
||||||
exit_code = os.system(cmd)
|
exit_code = os.system(cmd)
|
||||||
@@ -282,10 +287,10 @@ Version: %s
|
|||||||
Architecture: %s
|
Architecture: %s
|
||||||
Maintainer: rustdesk <info@rustdesk.com>
|
Maintainer: rustdesk <info@rustdesk.com>
|
||||||
Homepage: https://rustdesk.com
|
Homepage: https://rustdesk.com
|
||||||
Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0, libpam0g, libappindicator3-1, gstreamer1.0-pipewire
|
Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0, libpam0g, libappindicator3-1, gstreamer1.0-pipewire%s
|
||||||
Description: A remote control software.
|
Description: A remote control software.
|
||||||
|
|
||||||
""" % (version, get_deb_arch())
|
""" % (version, get_deb_arch(), get_deb_extra_depends())
|
||||||
file = open(control_file_path, "w")
|
file = open(control_file_path, "w")
|
||||||
file.write(content)
|
file.write(content)
|
||||||
file.close()
|
file.close()
|
||||||
|
|||||||
@@ -43,15 +43,13 @@ arm64-v8a)
|
|||||||
FLUTTER_TARGET=android-arm64
|
FLUTTER_TARGET=android-arm64
|
||||||
NDK_TARGET=aarch64-linux-android
|
NDK_TARGET=aarch64-linux-android
|
||||||
RUST_TARGET=aarch64-linux-android
|
RUST_TARGET=aarch64-linux-android
|
||||||
# RUSTDESK_FEATURES='flutter,hwcodec'
|
RUSTDESK_FEATURES='flutter,hwcodec'
|
||||||
RUSTDESK_FEATURES='flutter'
|
|
||||||
;;
|
;;
|
||||||
armeabi-v7a)
|
armeabi-v7a)
|
||||||
FLUTTER_TARGET=android-arm
|
FLUTTER_TARGET=android-arm
|
||||||
NDK_TARGET=arm-linux-androideabi
|
NDK_TARGET=arm-linux-androideabi
|
||||||
RUST_TARGET=armv7-linux-androideabi
|
RUST_TARGET=armv7-linux-androideabi
|
||||||
# RUSTDESK_FEATURES='flutter,hwcodec'
|
RUSTDESK_FEATURES='flutter,hwcodec'
|
||||||
RUSTDESK_FEATURES='flutter'
|
|
||||||
;;
|
;;
|
||||||
x86_64)
|
x86_64)
|
||||||
FLUTTER_TARGET=android-x64
|
FLUTTER_TARGET=android-x64
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import 'mobile/pages/file_manager_page.dart';
|
|||||||
import 'mobile/pages/remote_page.dart';
|
import 'mobile/pages/remote_page.dart';
|
||||||
import 'desktop/pages/remote_page.dart' as desktop_remote;
|
import 'desktop/pages/remote_page.dart' as desktop_remote;
|
||||||
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
||||||
import 'models/input_model.dart';
|
|
||||||
import 'models/model.dart';
|
import 'models/model.dart';
|
||||||
import 'models/platform_model.dart';
|
import 'models/platform_model.dart';
|
||||||
|
|
||||||
@@ -630,10 +629,30 @@ List<Locale> supportedLocales = const [
|
|||||||
Locale('da'),
|
Locale('da'),
|
||||||
Locale('eo'),
|
Locale('eo'),
|
||||||
Locale('tr'),
|
Locale('tr'),
|
||||||
Locale('vi'),
|
|
||||||
Locale('pl'),
|
|
||||||
Locale('kz'),
|
Locale('kz'),
|
||||||
Locale('es'),
|
Locale('es'),
|
||||||
|
Locale('nl'),
|
||||||
|
Locale('nb'),
|
||||||
|
Locale('et'),
|
||||||
|
Locale('eu'),
|
||||||
|
Locale('bg'),
|
||||||
|
Locale('be'),
|
||||||
|
Locale('vn'),
|
||||||
|
Locale('uk'),
|
||||||
|
Locale('fa'),
|
||||||
|
Locale('ca'),
|
||||||
|
Locale('el'),
|
||||||
|
Locale('sv'),
|
||||||
|
Locale('sq'),
|
||||||
|
Locale('sr'),
|
||||||
|
Locale('th'),
|
||||||
|
Locale('sl'),
|
||||||
|
Locale('ro'),
|
||||||
|
Locale('lt'),
|
||||||
|
Locale('lv'),
|
||||||
|
Locale('ar'),
|
||||||
|
Locale('he'),
|
||||||
|
Locale('hr'),
|
||||||
];
|
];
|
||||||
|
|
||||||
String formatDurationToTime(Duration duration) {
|
String formatDurationToTime(Duration duration) {
|
||||||
@@ -647,8 +666,12 @@ String formatDurationToTime(Duration duration) {
|
|||||||
|
|
||||||
closeConnection({String? id}) {
|
closeConnection({String? id}) {
|
||||||
if (isAndroid || isIOS) {
|
if (isAndroid || isIOS) {
|
||||||
gFFI.chatModel.hideChatOverlay();
|
() async {
|
||||||
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
|
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
||||||
|
overlays: SystemUiOverlay.values);
|
||||||
|
gFFI.chatModel.hideChatOverlay();
|
||||||
|
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
|
||||||
|
}();
|
||||||
} else {
|
} else {
|
||||||
if (isWeb) {
|
if (isWeb) {
|
||||||
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
|
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
|
||||||
@@ -1054,6 +1077,49 @@ class CustomAlertDialog extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget createDialogContent(String text) {
|
||||||
|
final RegExp linkRegExp = RegExp(r'(https?://[^\s]+)');
|
||||||
|
final List<TextSpan> spans = [];
|
||||||
|
int start = 0;
|
||||||
|
bool hasLink = false;
|
||||||
|
|
||||||
|
linkRegExp.allMatches(text).forEach((match) {
|
||||||
|
hasLink = true;
|
||||||
|
if (match.start > start) {
|
||||||
|
spans.add(TextSpan(text: text.substring(start, match.start)));
|
||||||
|
}
|
||||||
|
spans.add(TextSpan(
|
||||||
|
text: match.group(0) ?? '',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.blue,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
String linkText = match.group(0) ?? '';
|
||||||
|
linkText = linkText.replaceAll(RegExp(r'[.,;!?]+$'), '');
|
||||||
|
launchUrl(Uri.parse(linkText));
|
||||||
|
},
|
||||||
|
));
|
||||||
|
start = match.end;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (start < text.length) {
|
||||||
|
spans.add(TextSpan(text: text.substring(start)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasLink) {
|
||||||
|
return SelectableText(text, style: const TextStyle(fontSize: 15));
|
||||||
|
}
|
||||||
|
|
||||||
|
return SelectableText.rich(
|
||||||
|
TextSpan(
|
||||||
|
style: TextStyle(color: Colors.black, fontSize: 15),
|
||||||
|
children: spans,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void msgBox(SessionID sessionId, String type, String title, String text,
|
void msgBox(SessionID sessionId, String type, String title, String text,
|
||||||
String link, OverlayDialogManager dialogManager,
|
String link, OverlayDialogManager dialogManager,
|
||||||
{bool? hasCancel, ReconnectHandle? reconnect, int? reconnectTimeout}) {
|
{bool? hasCancel, ReconnectHandle? reconnect, int? reconnectTimeout}) {
|
||||||
@@ -1211,7 +1277,7 @@ Widget msgboxContent(String type, String title, String text) {
|
|||||||
translate(title),
|
translate(title),
|
||||||
style: TextStyle(fontSize: 21),
|
style: TextStyle(fontSize: 21),
|
||||||
).marginOnly(bottom: 10),
|
).marginOnly(bottom: 10),
|
||||||
Text(translateText(text), style: const TextStyle(fontSize: 15)),
|
createDialogContent(translateText(text)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1406,14 +1472,10 @@ class AndroidPermissionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move this to mobile/widgets.
|
|
||||||
// Used only for mobile, pages remote, settings, dialog
|
|
||||||
// TODO remove argument contentPadding, it’s not used, getToggle() has not
|
|
||||||
RadioListTile<T> getRadio<T>(
|
RadioListTile<T> getRadio<T>(
|
||||||
Widget title, T toValue, T curValue, ValueChanged<T?>? onChange,
|
Widget title, T toValue, T curValue, ValueChanged<T?>? onChange,
|
||||||
{EdgeInsetsGeometry? contentPadding, bool? dense}) {
|
{bool? dense}) {
|
||||||
return RadioListTile<T>(
|
return RadioListTile<T>(
|
||||||
contentPadding: contentPadding ?? EdgeInsets.zero,
|
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
controlAffinity: ListTileControlAffinity.trailing,
|
controlAffinity: ListTileControlAffinity.trailing,
|
||||||
title: title,
|
title: title,
|
||||||
@@ -2814,7 +2876,7 @@ Widget buildErrorBanner(BuildContext context,
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: translate(err.value),
|
message: translate(err.value),
|
||||||
child: Text(
|
child: SelectableText(
|
||||||
translate(err.value),
|
translate(err.value),
|
||||||
),
|
),
|
||||||
)).marginSymmetric(vertical: 2),
|
)).marginSymmetric(vertical: 2),
|
||||||
@@ -2935,6 +2997,16 @@ openMonitorInTheSameTab(int i, FFI ffi, PeerInfo pi,
|
|||||||
final displays = i == kAllDisplayValue
|
final displays = i == kAllDisplayValue
|
||||||
? List.generate(pi.displays.length, (index) => index)
|
? List.generate(pi.displays.length, (index) => index)
|
||||||
: [i];
|
: [i];
|
||||||
|
// Try clear image model before switching from all displays
|
||||||
|
// 1. The remote side has multiple displays.
|
||||||
|
// 2. Do not use texture render.
|
||||||
|
// 3. Connect to Display 1.
|
||||||
|
// 4. Switch to multi-displays `kAllDisplayValue`
|
||||||
|
// 5. Switch to Display 2.
|
||||||
|
// Then the remote page will display last picture of Display 1 at the beginning.
|
||||||
|
if (pi.forceTextureRender && i != kAllDisplayValue) {
|
||||||
|
ffi.imageModel.clearImage();
|
||||||
|
}
|
||||||
bind.sessionSwitchDisplay(
|
bind.sessionSwitchDisplay(
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
sessionId: ffi.sessionId,
|
sessionId: ffi.sessionId,
|
||||||
@@ -2968,11 +3040,15 @@ openMonitorInNewTabOrWindow(int i, String peerId, PeerInfo pi,
|
|||||||
kMainWindowId, kWindowEventOpenMonitorSession, jsonEncode(args));
|
kMainWindowId, kWindowEventOpenMonitorSession, jsonEncode(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
setNewConnectWindowFrame(
|
setNewConnectWindowFrame(int windowId, String peerId, int preSessionCount,
|
||||||
int windowId, String peerId, int? display, Rect? screenRect) async {
|
int? display, Rect? screenRect) async {
|
||||||
if (screenRect == null) {
|
if (screenRect == null) {
|
||||||
await restoreWindowPosition(WindowType.RemoteDesktop,
|
// Do not restore window position to new connection if there's a pre-session.
|
||||||
windowId: windowId, display: display, peerId: peerId);
|
// https://github.com/rustdesk/rustdesk/discussions/8825
|
||||||
|
if (preSessionCount == 0) {
|
||||||
|
await restoreWindowPosition(WindowType.RemoteDesktop,
|
||||||
|
windowId: windowId, display: display, peerId: peerId);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await tryMoveToScreenAndSetFullscreen(screenRect);
|
await tryMoveToScreenAndSetFullscreen(screenRect);
|
||||||
}
|
}
|
||||||
@@ -3316,6 +3392,42 @@ bool isInHomePage() {
|
|||||||
return controller.state.value.selected == 0;
|
return controller.state.value.selected == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildPresetPasswordWarning() {
|
||||||
|
if (bind.mainGetBuildinOption(key: kOptionRemovePresetPasswordWarning) !=
|
||||||
|
'N') {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return Container(
|
||||||
|
color: Colors.yellow,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
child: Text(
|
||||||
|
translate("Security Alert"),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.red,
|
||||||
|
fontSize:
|
||||||
|
18, // https://github.com/rustdesk/rustdesk-server-pro/issues/261
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
)).paddingOnly(bottom: 8),
|
||||||
|
Text(
|
||||||
|
translate("preset_password_warning"),
|
||||||
|
style: TextStyle(color: Colors.red),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
).paddingAll(8),
|
||||||
|
); // Show a warning message if the Future completed with true
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildPresetPasswordWarningMobile() {
|
||||||
|
if (bind.isPresetPasswordMobileOnly()) {
|
||||||
|
return _buildPresetPasswordWarning();
|
||||||
|
} else {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildPresetPasswordWarning() {
|
Widget buildPresetPasswordWarning() {
|
||||||
return FutureBuilder<bool>(
|
return FutureBuilder<bool>(
|
||||||
future: bind.isPresetPassword(),
|
future: bind.isPresetPassword(),
|
||||||
@@ -3326,32 +3438,7 @@ Widget buildPresetPasswordWarning() {
|
|||||||
return Text(
|
return Text(
|
||||||
'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error
|
'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error
|
||||||
} else if (snapshot.hasData && snapshot.data == true) {
|
} else if (snapshot.hasData && snapshot.data == true) {
|
||||||
if (bind.mainGetBuildinOption(
|
return _buildPresetPasswordWarning();
|
||||||
key: kOptionRemovePresetPasswordWarning) !=
|
|
||||||
'N') {
|
|
||||||
return SizedBox.shrink();
|
|
||||||
}
|
|
||||||
return Container(
|
|
||||||
color: Colors.yellow,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Align(
|
|
||||||
child: Text(
|
|
||||||
translate("Security Alert"),
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.red,
|
|
||||||
fontSize:
|
|
||||||
18, // https://github.com/rustdesk/rustdesk-server-pro/issues/261
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
)).paddingOnly(bottom: 8),
|
|
||||||
Text(
|
|
||||||
translate("preset_password_warning"),
|
|
||||||
style: TextStyle(color: Colors.red),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
).paddingAll(8),
|
|
||||||
); // Show a warning message if the Future completed with true
|
|
||||||
} else {
|
} else {
|
||||||
return SizedBox
|
return SizedBox
|
||||||
.shrink(); // Show nothing if the Future completed with false or null
|
.shrink(); // Show nothing if the Future completed with false or null
|
||||||
@@ -3405,7 +3492,8 @@ Widget buildVirtualWindowFrame(BuildContext context, Widget child) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get windowEdgeSize => isLinux && !_linuxWindowResizable ? 0.0 : kWindowEdgeSize;
|
get windowResizeEdgeSize =>
|
||||||
|
isLinux && !_linuxWindowResizable ? 0.0 : kWindowResizeEdgeSize;
|
||||||
|
|
||||||
// `windowManager.setResizable(false)` will reset the window size to the default size on Linux and then set unresizable.
|
// `windowManager.setResizable(false)` will reset the window size to the default size on Linux and then set unresizable.
|
||||||
// See _linuxWindowResizable for more details.
|
// See _linuxWindowResizable for more details.
|
||||||
@@ -3423,7 +3511,12 @@ setResizable(bool resizable) {
|
|||||||
|
|
||||||
isOptionFixed(String key) => bind.mainIsOptionFixed(key: key);
|
isOptionFixed(String key) => bind.mainIsOptionFixed(key: key);
|
||||||
|
|
||||||
final isCustomClient = bind.isCustomClient();
|
bool? _isCustomClient;
|
||||||
|
bool get isCustomClient {
|
||||||
|
_isCustomClient ??= bind.isCustomClient();
|
||||||
|
return _isCustomClient!;
|
||||||
|
}
|
||||||
|
|
||||||
get defaultOptionLang => isCustomClient ? 'default' : '';
|
get defaultOptionLang => isCustomClient ? 'default' : '';
|
||||||
get defaultOptionTheme => isCustomClient ? 'system' : '';
|
get defaultOptionTheme => isCustomClient ? 'system' : '';
|
||||||
get defaultOptionYes => isCustomClient ? 'Y' : '';
|
get defaultOptionYes => isCustomClient ? 'Y' : '';
|
||||||
@@ -3461,3 +3554,36 @@ disableWindowMovable(int? windowId) {
|
|||||||
WindowController.fromWindowId(windowId).setMovable(false);
|
WindowController.fromWindowId(windowId).setMovable(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget netWorkErrorWidget() {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(translate("network_error_tip")),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: gFFI.userModel.refreshCurrentUser,
|
||||||
|
child: Text(translate("Retry")))
|
||||||
|
.marginSymmetric(vertical: 16),
|
||||||
|
SelectableText(gFFI.userModel.networkError.value,
|
||||||
|
style: TextStyle(fontSize: 11, color: Colors.red)),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ResizeEdge>? get windowManagerEnableResizeEdges => isWindows
|
||||||
|
? [
|
||||||
|
ResizeEdge.topLeft,
|
||||||
|
ResizeEdge.top,
|
||||||
|
ResizeEdge.topRight,
|
||||||
|
]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
List<SubWindowResizeEdge>? get subWindowManagerEnableResizeEdges => isWindows
|
||||||
|
? [
|
||||||
|
SubWindowResizeEdge.topLeft,
|
||||||
|
SubWindowResizeEdge.top,
|
||||||
|
SubWindowResizeEdge.topRight,
|
||||||
|
]
|
||||||
|
: null;
|
||||||
|
|||||||
@@ -35,17 +35,14 @@ class AddressBook extends StatefulWidget {
|
|||||||
class _AddressBookState extends State<AddressBook> {
|
class _AddressBookState extends State<AddressBook> {
|
||||||
var menuPos = RelativeRect.fill;
|
var menuPos = RelativeRect.fill;
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Obx(() {
|
Widget build(BuildContext context) => Obx(() {
|
||||||
if (!gFFI.userModel.isLogin) {
|
if (!gFFI.userModel.isLogin) {
|
||||||
return Center(
|
return Center(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: loginDialog, child: Text(translate("Login"))));
|
onPressed: loginDialog, child: Text(translate("Login"))));
|
||||||
|
} else if (gFFI.userModel.networkError.isNotEmpty) {
|
||||||
|
return netWorkErrorWidget();
|
||||||
} else {
|
} else {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
@@ -425,7 +422,8 @@ class _AddressBookState extends State<AddressBook> {
|
|||||||
if (canWrite) getEntry(translate("Add ID"), addIdToCurrentAb),
|
if (canWrite) getEntry(translate("Add ID"), addIdToCurrentAb),
|
||||||
if (canWrite) getEntry(translate("Add Tag"), abAddTag),
|
if (canWrite) getEntry(translate("Add Tag"), abAddTag),
|
||||||
getEntry(translate("Unselect all tags"), gFFI.abModel.unsetSelectedTags),
|
getEntry(translate("Unselect all tags"), gFFI.abModel.unsetSelectedTags),
|
||||||
sortMenuItem(),
|
if (gFFI.abModel.legacyMode.value)
|
||||||
|
sortMenuItem(), // It's already sorted after pulling down
|
||||||
if (canWrite) syncMenuItem(),
|
if (canWrite) syncMenuItem(),
|
||||||
filterMenuItem(),
|
filterMenuItem(),
|
||||||
if (!gFFI.abModel.legacyMode.value && canWrite)
|
if (!gFFI.abModel.legacyMode.value && canWrite)
|
||||||
|
|||||||
@@ -679,6 +679,7 @@ class PasswordWidget extends StatefulWidget {
|
|||||||
this.reRequestFocus = false,
|
this.reRequestFocus = false,
|
||||||
this.hintText,
|
this.hintText,
|
||||||
this.errorText,
|
this.errorText,
|
||||||
|
this.title,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
@@ -686,6 +687,7 @@ class PasswordWidget extends StatefulWidget {
|
|||||||
final bool reRequestFocus;
|
final bool reRequestFocus;
|
||||||
final String? hintText;
|
final String? hintText;
|
||||||
final String? errorText;
|
final String? errorText;
|
||||||
|
final String? title;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PasswordWidget> createState() => _PasswordWidgetState();
|
State<PasswordWidget> createState() => _PasswordWidgetState();
|
||||||
@@ -729,7 +731,7 @@ class _PasswordWidgetState extends State<PasswordWidget> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DialogTextField(
|
return DialogTextField(
|
||||||
title: translate(DialogTextField.kPasswordTitle),
|
title: translate(widget.title ?? DialogTextField.kPasswordTitle),
|
||||||
hintText: translate(widget.hintText ?? 'Enter your password'),
|
hintText: translate(widget.hintText ?? 'Enter your password'),
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
prefixIcon: DialogTextField.kPasswordIcon,
|
prefixIcon: DialogTextField.kPasswordIcon,
|
||||||
@@ -1829,6 +1831,7 @@ void changeBot({Function()? callback}) async {
|
|||||||
void change2fa({Function()? callback}) async {
|
void change2fa({Function()? callback}) async {
|
||||||
if (bind.mainHasValid2FaSync()) {
|
if (bind.mainHasValid2FaSync()) {
|
||||||
await bind.mainSetOption(key: "2fa", value: "");
|
await bind.mainSetOption(key: "2fa", value: "");
|
||||||
|
await bind.mainClearTrustedDevices();
|
||||||
callback?.call();
|
callback?.call();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1896,6 +1899,7 @@ void enter2FaDialog(
|
|||||||
SessionID sessionId, OverlayDialogManager dialogManager) async {
|
SessionID sessionId, OverlayDialogManager dialogManager) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
final RxBool submitReady = false.obs;
|
final RxBool submitReady = false.obs;
|
||||||
|
final RxBool trustThisDevice = false.obs;
|
||||||
|
|
||||||
dialogManager.dismissAll();
|
dialogManager.dismissAll();
|
||||||
dialogManager.show((setState, close, context) {
|
dialogManager.show((setState, close, context) {
|
||||||
@@ -1905,7 +1909,7 @@ void enter2FaDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
gFFI.send2FA(sessionId, controller.text.trim());
|
gFFI.send2FA(sessionId, controller.text.trim(), trustThisDevice.value);
|
||||||
close();
|
close();
|
||||||
dialogManager.showLoading(translate('Logging in...'),
|
dialogManager.showLoading(translate('Logging in...'),
|
||||||
onCancel: closeConnection);
|
onCancel: closeConnection);
|
||||||
@@ -1919,9 +1923,27 @@ void enter2FaDialog(
|
|||||||
onChanged: () => submitReady.value = codeField.isReady,
|
onChanged: () => submitReady.value = codeField.isReady,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final trustField = Obx(() => CheckboxListTile(
|
||||||
|
contentPadding: const EdgeInsets.all(0),
|
||||||
|
dense: true,
|
||||||
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
|
title: Text(translate("Trust this device")),
|
||||||
|
value: trustThisDevice.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value == null) return;
|
||||||
|
trustThisDevice.value = value;
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
title: Text(translate('enter-2fa-title')),
|
title: Text(translate('enter-2fa-title')),
|
||||||
content: codeField,
|
content: Column(
|
||||||
|
children: [
|
||||||
|
codeField,
|
||||||
|
if (bind.sessionGetEnableTrustedDevices(sessionId: sessionId))
|
||||||
|
trustField,
|
||||||
|
],
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
dialogButton('Cancel',
|
dialogButton('Cancel',
|
||||||
onPressed: cancel,
|
onPressed: cancel,
|
||||||
@@ -2216,3 +2238,252 @@ void CommonConfirmDialog(OverlayDialogManager dialogManager, String content,
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void changeUnlockPinDialog(String oldPin, Function() callback) {
|
||||||
|
final pinController = TextEditingController(text: oldPin);
|
||||||
|
final confirmController = TextEditingController(text: oldPin);
|
||||||
|
String? pinErrorText;
|
||||||
|
String? confirmationErrorText;
|
||||||
|
gFFI.dialogManager.show((setState, close, context) {
|
||||||
|
submit() async {
|
||||||
|
pinErrorText = null;
|
||||||
|
confirmationErrorText = null;
|
||||||
|
final pin = pinController.text.trim();
|
||||||
|
final confirm = confirmController.text.trim();
|
||||||
|
if (pin != confirm) {
|
||||||
|
setState(() {
|
||||||
|
confirmationErrorText =
|
||||||
|
translate('The confirmation is not identical.');
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final errorMsg = bind.mainSetUnlockPin(pin: pin);
|
||||||
|
if (errorMsg != '') {
|
||||||
|
setState(() {
|
||||||
|
pinErrorText = translate(errorMsg);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback.call();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: Text(translate("Set PIN")),
|
||||||
|
content: Column(
|
||||||
|
children: [
|
||||||
|
DialogTextField(
|
||||||
|
title: 'PIN',
|
||||||
|
controller: pinController,
|
||||||
|
obscureText: true,
|
||||||
|
errorText: pinErrorText,
|
||||||
|
),
|
||||||
|
DialogTextField(
|
||||||
|
title: translate('Confirmation'),
|
||||||
|
controller: confirmController,
|
||||||
|
obscureText: true,
|
||||||
|
errorText: confirmationErrorText,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
).marginOnly(bottom: 12),
|
||||||
|
actions: [
|
||||||
|
dialogButton(translate("Cancel"), onPressed: close, isOutline: true),
|
||||||
|
dialogButton(translate("OK"), onPressed: submit),
|
||||||
|
],
|
||||||
|
onSubmit: submit,
|
||||||
|
onCancel: close,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkUnlockPinDialog(String correctPin, Function() passCallback) {
|
||||||
|
final controller = TextEditingController();
|
||||||
|
String? errorText;
|
||||||
|
gFFI.dialogManager.show((setState, close, context) {
|
||||||
|
submit() async {
|
||||||
|
final pin = controller.text.trim();
|
||||||
|
if (correctPin != pin) {
|
||||||
|
setState(() {
|
||||||
|
errorText = translate('Wrong PIN');
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
passCallback.call();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomAlertDialog(
|
||||||
|
content: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: PasswordWidget(
|
||||||
|
title: 'PIN',
|
||||||
|
controller: controller,
|
||||||
|
errorText: errorText,
|
||||||
|
hintText: '',
|
||||||
|
))
|
||||||
|
],
|
||||||
|
).marginOnly(bottom: 12),
|
||||||
|
actions: [
|
||||||
|
dialogButton(translate("Cancel"), onPressed: close, isOutline: true),
|
||||||
|
dialogButton(translate("OK"), onPressed: submit),
|
||||||
|
],
|
||||||
|
onSubmit: submit,
|
||||||
|
onCancel: close,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void confrimDeleteTrustedDevicesDialog(
|
||||||
|
RxList<TrustedDevice> trustedDevices, RxList<Uint8List> selectedDevices) {
|
||||||
|
CommonConfirmDialog(gFFI.dialogManager, '${translate('Confirm Delete')}?',
|
||||||
|
() async {
|
||||||
|
if (selectedDevices.isEmpty) return;
|
||||||
|
if (selectedDevices.length == trustedDevices.length) {
|
||||||
|
await bind.mainClearTrustedDevices();
|
||||||
|
trustedDevices.clear();
|
||||||
|
selectedDevices.clear();
|
||||||
|
} else {
|
||||||
|
final json = jsonEncode(selectedDevices.map((e) => e.toList()).toList());
|
||||||
|
await bind.mainRemoveTrustedDevices(json: json);
|
||||||
|
trustedDevices.removeWhere((element) {
|
||||||
|
return selectedDevices.contains(element.hwid);
|
||||||
|
});
|
||||||
|
selectedDevices.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void manageTrustedDeviceDialog() async {
|
||||||
|
RxList<TrustedDevice> trustedDevices = (await TrustedDevice.get()).obs;
|
||||||
|
RxList<Uint8List> selectedDevices = RxList.empty();
|
||||||
|
gFFI.dialogManager.show((setState, close, context) {
|
||||||
|
return CustomAlertDialog(
|
||||||
|
title: Text(translate("Manage trusted devices")),
|
||||||
|
content: trustedDevicesTable(trustedDevices, selectedDevices),
|
||||||
|
actions: [
|
||||||
|
Obx(() => dialogButton(translate("Delete"),
|
||||||
|
onPressed: selectedDevices.isEmpty
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
confrimDeleteTrustedDevicesDialog(
|
||||||
|
trustedDevices,
|
||||||
|
selectedDevices,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isOutline: false)
|
||||||
|
.marginOnly(top: 12)),
|
||||||
|
dialogButton(translate("Close"), onPressed: close, isOutline: true)
|
||||||
|
.marginOnly(top: 12),
|
||||||
|
],
|
||||||
|
onCancel: close,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class TrustedDevice {
|
||||||
|
late final Uint8List hwid;
|
||||||
|
late final int time;
|
||||||
|
late final String id;
|
||||||
|
late final String name;
|
||||||
|
late final String platform;
|
||||||
|
|
||||||
|
TrustedDevice.fromJson(Map<String, dynamic> json) {
|
||||||
|
final hwidList = json['hwid'] as List<dynamic>;
|
||||||
|
hwid = Uint8List.fromList(hwidList.cast<int>());
|
||||||
|
time = json['time'];
|
||||||
|
id = json['id'];
|
||||||
|
name = json['name'];
|
||||||
|
platform = json['platform'];
|
||||||
|
}
|
||||||
|
|
||||||
|
String daysRemaining() {
|
||||||
|
final expiry = time + 90 * 24 * 60 * 60 * 1000;
|
||||||
|
final remaining = expiry - DateTime.now().millisecondsSinceEpoch;
|
||||||
|
if (remaining < 0) {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
return (remaining / (24 * 60 * 60 * 1000)).toStringAsFixed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<List<TrustedDevice>> get() async {
|
||||||
|
final List<TrustedDevice> devices = List.empty(growable: true);
|
||||||
|
try {
|
||||||
|
final devicesJson = await bind.mainGetTrustedDevices();
|
||||||
|
if (devicesJson.isNotEmpty) {
|
||||||
|
final devicesList = json.decode(devicesJson);
|
||||||
|
if (devicesList is List) {
|
||||||
|
for (var device in devicesList) {
|
||||||
|
devices.add(TrustedDevice.fromJson(device));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
|
devices.sort((a, b) => b.time.compareTo(a.time));
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget trustedDevicesTable(
|
||||||
|
RxList<TrustedDevice> devices, RxList<Uint8List> selectedDevices) {
|
||||||
|
RxBool selectAll = false.obs;
|
||||||
|
setSelectAll() {
|
||||||
|
if (selectedDevices.isNotEmpty &&
|
||||||
|
selectedDevices.length == devices.length) {
|
||||||
|
selectAll.value = true;
|
||||||
|
} else {
|
||||||
|
selectAll.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
devices.listen((_) {
|
||||||
|
setSelectAll();
|
||||||
|
});
|
||||||
|
selectedDevices.listen((_) {
|
||||||
|
setSelectAll();
|
||||||
|
});
|
||||||
|
return FittedBox(
|
||||||
|
child: Obx(() => DataTable(
|
||||||
|
columns: [
|
||||||
|
DataColumn(
|
||||||
|
label: Checkbox(
|
||||||
|
value: selectAll.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value == true) {
|
||||||
|
selectedDevices.clear();
|
||||||
|
selectedDevices.addAll(devices.map((e) => e.hwid));
|
||||||
|
} else {
|
||||||
|
selectedDevices.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
DataColumn(label: Text(translate('Platform'))),
|
||||||
|
DataColumn(label: Text(translate('ID'))),
|
||||||
|
DataColumn(label: Text(translate('Username'))),
|
||||||
|
DataColumn(label: Text(translate('Days remaining'))),
|
||||||
|
],
|
||||||
|
rows: devices.map((device) {
|
||||||
|
return DataRow(cells: [
|
||||||
|
DataCell(Checkbox(
|
||||||
|
value: selectedDevices.contains(device.hwid),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value == null) return;
|
||||||
|
if (value) {
|
||||||
|
selectedDevices.remove(device.hwid);
|
||||||
|
selectedDevices.add(device.hwid);
|
||||||
|
} else {
|
||||||
|
selectedDevices.remove(device.hwid);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
DataCell(Text(device.platform)),
|
||||||
|
DataCell(Text(device.id)),
|
||||||
|
DataCell(Text(device.name)),
|
||||||
|
DataCell(Text(device.daysRemaining())),
|
||||||
|
]);
|
||||||
|
}).toList(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -142,11 +142,6 @@ class _WidgetOPState extends State<WidgetOP> {
|
|||||||
String _failedMsg = '';
|
String _failedMsg = '';
|
||||||
String _url = '';
|
String _url = '';
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|||||||
@@ -23,11 +23,6 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
RxString get searchUserText => gFFI.groupModel.searchUserText;
|
RxString get searchUserText => gFFI.groupModel.searchUserText;
|
||||||
static TextEditingController searchUserController = TextEditingController();
|
static TextEditingController searchUserController = TextEditingController();
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
@@ -35,6 +30,8 @@ class _MyGroupState extends State<MyGroup> {
|
|||||||
return Center(
|
return Center(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: loginDialog, child: Text(translate("Login"))));
|
onPressed: loginDialog, child: Text(translate("Login"))));
|
||||||
|
} else if (gFFI.userModel.networkError.isNotEmpty) {
|
||||||
|
return netWorkErrorWidget();
|
||||||
} else if (gFFI.groupModel.groupLoading.value && gFFI.groupModel.emtpy) {
|
} else if (gFFI.groupModel.groupLoading.value && gFFI.groupModel.emtpy) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ class Draggable extends StatefulWidget {
|
|||||||
final Widget Function(BuildContext, GestureDragUpdateCallback) builder;
|
final Widget Function(BuildContext, GestureDragUpdateCallback) builder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _DraggableState();
|
State<StatefulWidget> createState() => _DraggableState(chatModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DraggableState extends State<Draggable> {
|
class _DraggableState extends State<Draggable> {
|
||||||
@@ -362,10 +362,8 @@ class _DraggableState extends State<Draggable> {
|
|||||||
double _saveHeight = 0;
|
double _saveHeight = 0;
|
||||||
double _lastBottomHeight = 0;
|
double _lastBottomHeight = 0;
|
||||||
|
|
||||||
@override
|
_DraggableState(ChatModel? chatModel) {
|
||||||
void initState() {
|
_chatModel = chatModel;
|
||||||
super.initState();
|
|
||||||
_chatModel = widget.chatModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get position => widget.position.pos;
|
get position => widget.position.pos;
|
||||||
@@ -467,7 +465,8 @@ class IOSDraggable extends StatefulWidget {
|
|||||||
final Widget Function(BuildContext) builder;
|
final Widget Function(BuildContext) builder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
IOSDraggableState createState() => IOSDraggableState();
|
IOSDraggableState createState() =>
|
||||||
|
IOSDraggableState(chatModel, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
class IOSDraggableState extends State<IOSDraggable> {
|
class IOSDraggableState extends State<IOSDraggable> {
|
||||||
@@ -478,12 +477,10 @@ class IOSDraggableState extends State<IOSDraggable> {
|
|||||||
double _saveHeight = 0;
|
double _saveHeight = 0;
|
||||||
double _lastBottomHeight = 0;
|
double _lastBottomHeight = 0;
|
||||||
|
|
||||||
@override
|
IOSDraggableState(ChatModel? chatModel, double w, double h) {
|
||||||
void initState() {
|
_chatModel = chatModel;
|
||||||
super.initState();
|
_width = w;
|
||||||
_chatModel = widget.chatModel;
|
_height = h;
|
||||||
_width = widget.width;
|
|
||||||
_height = widget.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DraggableKeyPosition get position => widget.position;
|
DraggableKeyPosition get position => widget.position;
|
||||||
|
|||||||
@@ -636,8 +636,8 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
|
|
||||||
@protected
|
@protected
|
||||||
Future<bool> _isForceAlwaysRelay(String id) async {
|
Future<bool> _isForceAlwaysRelay(String id) async {
|
||||||
return (await bind.mainGetPeerOption(id: id, key: kOptionForceAlwaysRelay))
|
return option2bool(kOptionForceAlwaysRelay,
|
||||||
.isNotEmpty;
|
(await bind.mainGetPeerOption(id: id, key: kOptionForceAlwaysRelay)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
|
|||||||
@@ -76,8 +76,11 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
|
|
||||||
final isOptVisiableFixed = isOptionFixed(kOptionPeerTabVisible);
|
final isOptVisiableFixed = isOptionFixed(kOptionPeerTabVisible);
|
||||||
|
|
||||||
@override
|
_PeerTabPageState() {
|
||||||
void initState() {
|
_loadLocalOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _loadLocalOptions() {
|
||||||
final uiType = bind.getLocalFlutterOption(k: kOptionPeerCardUiType);
|
final uiType = bind.getLocalFlutterOption(k: kOptionPeerCardUiType);
|
||||||
if (uiType != '') {
|
if (uiType != '') {
|
||||||
peerCardUiType.value = int.parse(uiType) == 0
|
peerCardUiType.value = int.parse(uiType) == 0
|
||||||
@@ -87,8 +90,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
: PeerUiType.list;
|
: PeerUiType.list;
|
||||||
}
|
}
|
||||||
hideAbTagsPanel.value =
|
hideAbTagsPanel.value =
|
||||||
bind.mainGetLocalOption(key: kOptionHideAbTagsPanel).isNotEmpty;
|
bind.mainGetLocalOption(key: kOptionHideAbTagsPanel) == 'Y';
|
||||||
super.initState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleTabSelection(int tabIndex) async {
|
Future<void> handleTabSelection(int tabIndex) async {
|
||||||
@@ -872,16 +874,18 @@ class PeerSortDropdown extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PeerSortDropdownState extends State<PeerSortDropdown> {
|
class _PeerSortDropdownState extends State<PeerSortDropdown> {
|
||||||
@override
|
_PeerSortDropdownState() {
|
||||||
void initState() {
|
|
||||||
if (!PeerSortType.values.contains(peerSort.value)) {
|
if (!PeerSortType.values.contains(peerSort.value)) {
|
||||||
peerSort.value = PeerSortType.remoteId;
|
_loadLocalOptions();
|
||||||
bind.setLocalFlutterOption(
|
|
||||||
k: kOptionPeerSorting,
|
|
||||||
v: peerSort.value,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
super.initState();
|
}
|
||||||
|
|
||||||
|
void _loadLocalOptions() {
|
||||||
|
peerSort.value = PeerSortType.remoteId;
|
||||||
|
bind.setLocalFlutterOption(
|
||||||
|
k: kOptionPeerSorting,
|
||||||
|
v: peerSort.value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -45,10 +45,14 @@ class LoadEvent {
|
|||||||
final peerSearchText = "".obs;
|
final peerSearchText = "".obs;
|
||||||
|
|
||||||
/// for peer sort, global obs value
|
/// for peer sort, global obs value
|
||||||
final peerSort = bind.getLocalFlutterOption(k: kOptionPeerSorting).obs;
|
RxString? _peerSort;
|
||||||
|
RxString get peerSort {
|
||||||
|
_peerSort ??= bind.getLocalFlutterOption(k: kOptionPeerSorting).obs;
|
||||||
|
return _peerSort!;
|
||||||
|
}
|
||||||
|
|
||||||
// list for listener
|
// list for listener
|
||||||
final obslist = [peerSearchText, peerSort].obs;
|
RxList<RxString> get obslist => [peerSearchText, peerSort].obs;
|
||||||
|
|
||||||
final peerSearchTextController =
|
final peerSearchTextController =
|
||||||
TextEditingController(text: peerSearchText.value);
|
TextEditingController(text: peerSearchText.value);
|
||||||
@@ -70,7 +74,8 @@ class _PeersView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// State for the peer widget.
|
/// State for the peer widget.
|
||||||
class _PeersViewState extends State<_PeersView> with WindowListener {
|
class _PeersViewState extends State<_PeersView>
|
||||||
|
with WindowListener, WidgetsBindingObserver {
|
||||||
static const int _maxQueryCount = 3;
|
static const int _maxQueryCount = 3;
|
||||||
final HashMap<String, String> _emptyMessages = HashMap.from({
|
final HashMap<String, String> _emptyMessages = HashMap.from({
|
||||||
LoadEvent.recent: 'empty_recent_tip',
|
LoadEvent.recent: 'empty_recent_tip',
|
||||||
@@ -85,6 +90,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
|||||||
var _lastQueryTime = DateTime.now();
|
var _lastQueryTime = DateTime.now();
|
||||||
var _queryCount = 0;
|
var _queryCount = 0;
|
||||||
var _exit = false;
|
var _exit = false;
|
||||||
|
bool _isActive = true;
|
||||||
|
|
||||||
final _scrollController = ScrollController();
|
final _scrollController = ScrollController();
|
||||||
|
|
||||||
@@ -95,12 +101,14 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
windowManager.addListener(this);
|
windowManager.addListener(this);
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
windowManager.removeListener(this);
|
windowManager.removeListener(this);
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
_exit = true;
|
_exit = true;
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
@@ -115,6 +123,20 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
|||||||
_queryCount = _maxQueryCount;
|
_queryCount = _maxQueryCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is required for mobile.
|
||||||
|
// `onWindowFocus` works fine for desktop.
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
super.didChangeAppLifecycleState(state);
|
||||||
|
if (isDesktop) return;
|
||||||
|
if (state == AppLifecycleState.resumed) {
|
||||||
|
_isActive = true;
|
||||||
|
_queryCount = 0;
|
||||||
|
} else if (state == AppLifecycleState.inactive) {
|
||||||
|
_isActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ChangeNotifierProvider<Peers>(
|
return ChangeNotifierProvider<Peers>(
|
||||||
@@ -268,7 +290,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
|||||||
_queryOnlines(false);
|
_queryOnlines(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_queryCount < _maxQueryCount || !p) {
|
if (_isActive && (_queryCount < _maxQueryCount || !p)) {
|
||||||
if (now.difference(_lastQueryTime) >= _queryInterval) {
|
if (now.difference(_lastQueryTime) >= _queryInterval) {
|
||||||
if (_curPeers.isNotEmpty) {
|
if (_curPeers.isNotEmpty) {
|
||||||
bind.queryOnlines(ids: _curPeers.toList(growable: false));
|
bind.queryOnlines(ids: _curPeers.toList(growable: false));
|
||||||
@@ -286,14 +308,14 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
|||||||
_queryOnlines(bool isLoadEvent) {
|
_queryOnlines(bool isLoadEvent) {
|
||||||
if (_curPeers.isNotEmpty) {
|
if (_curPeers.isNotEmpty) {
|
||||||
bind.queryOnlines(ids: _curPeers.toList(growable: false));
|
bind.queryOnlines(ids: _curPeers.toList(growable: false));
|
||||||
_lastQueryPeers = {..._curPeers};
|
|
||||||
if (isLoadEvent) {
|
|
||||||
_lastChangeTime = DateTime.now();
|
|
||||||
} else {
|
|
||||||
_lastQueryTime = DateTime.now().subtract(_queryInterval);
|
|
||||||
}
|
|
||||||
_queryCount = 0;
|
_queryCount = 0;
|
||||||
}
|
}
|
||||||
|
_lastQueryPeers = {..._curPeers};
|
||||||
|
if (isLoadEvent) {
|
||||||
|
_lastChangeTime = DateTime.now();
|
||||||
|
} else {
|
||||||
|
_lastQueryTime = DateTime.now().subtract(_queryInterval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Peer>>? matchPeers(
|
Future<List<Peer>>? matchPeers(
|
||||||
|
|||||||
@@ -130,12 +130,9 @@ List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// paste
|
// paste
|
||||||
if (isMobile &&
|
if (pi.platform != kPeerPlatformAndroid && perms['keyboard'] != false) {
|
||||||
pi.platform != kPeerPlatformAndroid &&
|
|
||||||
perms['keyboard'] != false &&
|
|
||||||
perms['clipboard'] != false) {
|
|
||||||
v.add(TTextMenu(
|
v.add(TTextMenu(
|
||||||
child: Text(translate('Paste')),
|
child: Text(translate('Send clipboard keystrokes')),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
|
ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
|
||||||
if (data != null && data.text != null) {
|
if (data != null && data.text != null) {
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ const String kOptionAllowRemoveWallpaper = "allow-remove-wallpaper";
|
|||||||
const String kOptionStopService = "stop-service";
|
const String kOptionStopService = "stop-service";
|
||||||
const String kOptionDirectxCapture = "enable-directx-capture";
|
const String kOptionDirectxCapture = "enable-directx-capture";
|
||||||
const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification";
|
const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification";
|
||||||
|
const String kOptionEnableTrustedDevices = "enable-trusted-devices";
|
||||||
|
|
||||||
// buildin opitons
|
// buildin opitons
|
||||||
const String kOptionHideServerSetting = "hide-server-settings";
|
const String kOptionHideServerSetting = "hide-server-settings";
|
||||||
@@ -240,9 +241,9 @@ const kDefaultScrollDuration = Duration(milliseconds: 50);
|
|||||||
const kDefaultMouseWheelThrottleDuration = Duration(milliseconds: 50);
|
const kDefaultMouseWheelThrottleDuration = Duration(milliseconds: 50);
|
||||||
const kFullScreenEdgeSize = 0.0;
|
const kFullScreenEdgeSize = 0.0;
|
||||||
const kMaximizeEdgeSize = 0.0;
|
const kMaximizeEdgeSize = 0.0;
|
||||||
// Do not use kWindowEdgeSize directly. Use `windowEdgeSize` in `common.dart` instead.
|
// Do not use kWindowResizeEdgeSize directly. Use `windowResizeEdgeSize` in `common.dart` instead.
|
||||||
final kWindowEdgeSize = isWindows ? 1.0 : 5.0;
|
const kWindowResizeEdgeSize = 5.0;
|
||||||
final kWindowBorderWidth = 1.0;
|
const kWindowBorderWidth = 1.0;
|
||||||
const kDesktopMenuPadding = EdgeInsets.only(left: 12.0, right: 3.0);
|
const kDesktopMenuPadding = EdgeInsets.only(left: 12.0, right: 3.0);
|
||||||
const kFrameBorderRadius = 12.0;
|
const kFrameBorderRadius = 12.0;
|
||||||
const kFrameClipRRectBorderRadius = 12.0;
|
const kFrameClipRRectBorderRadius = 12.0;
|
||||||
|
|||||||
@@ -169,16 +169,12 @@ class _OnlineStatusWidgetState extends State<OnlineStatusWidget> {
|
|||||||
final status =
|
final status =
|
||||||
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
|
jsonDecode(await bind.mainGetConnectStatus()) as Map<String, dynamic>;
|
||||||
final statusNum = status['status_num'] as int;
|
final statusNum = status['status_num'] as int;
|
||||||
final preStatus = stateGlobal.svcStatus.value;
|
|
||||||
if (statusNum == 0) {
|
if (statusNum == 0) {
|
||||||
stateGlobal.svcStatus.value = SvcStatus.connecting;
|
stateGlobal.svcStatus.value = SvcStatus.connecting;
|
||||||
} else if (statusNum == -1) {
|
} else if (statusNum == -1) {
|
||||||
stateGlobal.svcStatus.value = SvcStatus.notReady;
|
stateGlobal.svcStatus.value = SvcStatus.notReady;
|
||||||
} else if (statusNum == 1) {
|
} else if (statusNum == 1) {
|
||||||
stateGlobal.svcStatus.value = SvcStatus.ready;
|
stateGlobal.svcStatus.value = SvcStatus.ready;
|
||||||
if (preStatus != SvcStatus.ready) {
|
|
||||||
gFFI.userModel.refreshCurrentUser();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
stateGlobal.svcStatus.value = SvcStatus.notReady;
|
stateGlobal.svcStatus.value = SvcStatus.notReady;
|
||||||
}
|
}
|
||||||
@@ -212,14 +208,14 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
if (_idController.text.isEmpty) {
|
if (_idController.text.isEmpty) {
|
||||||
() async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
final lastRemoteId = await bind.mainGetLastRemoteId();
|
final lastRemoteId = await bind.mainGetLastRemoteId();
|
||||||
if (lastRemoteId != _idController.id) {
|
if (lastRemoteId != _idController.id) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_idController.id = lastRemoteId;
|
_idController.id = lastRemoteId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}();
|
});
|
||||||
}
|
}
|
||||||
Get.put<IDTextEditingController>(_idController);
|
Get.put<IDTextEditingController>(_idController);
|
||||||
windowManager.addListener(this);
|
windowManager.addListener(this);
|
||||||
@@ -261,8 +257,9 @@ class _ConnectionPageState extends State<ConnectionPage>
|
|||||||
@override
|
@override
|
||||||
void onWindowLeaveFullScreen() {
|
void onWindowLeaveFullScreen() {
|
||||||
// Restore edge border to default edge size.
|
// Restore edge border to default edge size.
|
||||||
stateGlobal.resizeEdgeSize.value =
|
stateGlobal.resizeEdgeSize.value = stateGlobal.isMaximized.isTrue
|
||||||
stateGlobal.isMaximized.isTrue ? kMaximizeEdgeSize : windowEdgeSize;
|
? kMaximizeEdgeSize
|
||||||
|
: windowResizeEdgeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ class DesktopSettingPage extends StatefulWidget {
|
|||||||
DesktopSettingPage({Key? key, required this.initialTabkey}) : super(key: key);
|
DesktopSettingPage({Key? key, required this.initialTabkey}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DesktopSettingPage> createState() => _DesktopSettingPageState();
|
State<DesktopSettingPage> createState() =>
|
||||||
|
_DesktopSettingPageState(initialTabkey);
|
||||||
|
|
||||||
static void switch2page(SettingsTabKey page) {
|
static void switch2page(SettingsTabKey page) {
|
||||||
try {
|
try {
|
||||||
@@ -111,10 +112,8 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
|
|||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
@override
|
_DesktopSettingPageState(SettingsTabKey initialTabkey) {
|
||||||
void initState() {
|
var initialIndex = DesktopSettingPage.tabKeys.indexOf(initialTabkey);
|
||||||
super.initState();
|
|
||||||
var initialIndex = DesktopSettingPage.tabKeys.indexOf(widget.initialTabkey);
|
|
||||||
if (initialIndex == -1) {
|
if (initialIndex == -1) {
|
||||||
initialIndex = 0;
|
initialIndex = 0;
|
||||||
}
|
}
|
||||||
@@ -516,16 +515,16 @@ class _GeneralState extends State<_General> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder(devices, currentDevice, setDevice) {
|
builder(devices, currentDevice, setDevice) {
|
||||||
return _Card(title: 'Audio Input Device', children: [
|
final child = ComboBox(
|
||||||
...devices.map((device) => _Radio<String>(context,
|
keys: devices,
|
||||||
value: device,
|
values: devices,
|
||||||
groupValue: currentDevice,
|
initialKey: currentDevice,
|
||||||
autoNewLine: false,
|
onChanged: (key) async {
|
||||||
label: device, onChanged: (value) {
|
setDevice(key);
|
||||||
setDevice(value);
|
setState(() {});
|
||||||
setState(() {});
|
},
|
||||||
}))
|
).marginOnly(left: _kContentHMargin);
|
||||||
]);
|
return _Card(title: 'Audio Input Device', children: [child]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AudioInput(builder: builder, isCm: false, isVoiceCall: false);
|
return AudioInput(builder: builder, isCm: false, isVoiceCall: false);
|
||||||
@@ -784,8 +783,33 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
onChangedBot(!hasBot.value);
|
onChangedBot(!hasBot.value);
|
||||||
},
|
},
|
||||||
).marginOnly(left: _kCheckBoxLeftMargin + 30);
|
).marginOnly(left: _kCheckBoxLeftMargin + 30);
|
||||||
|
|
||||||
|
final trust = Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Tooltip(
|
||||||
|
waitDuration: Duration(milliseconds: 300),
|
||||||
|
message: translate("enable-trusted-devices-tip"),
|
||||||
|
child: _OptionCheckBox(context, "Enable trusted devices",
|
||||||
|
kOptionEnableTrustedDevices,
|
||||||
|
enabled: !locked, update: (v) {
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (mainGetBoolOptionSync(kOptionEnableTrustedDevices))
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: locked
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
manageTrustedDeviceDialog();
|
||||||
|
},
|
||||||
|
child: Text(translate('Manage trusted devices')))
|
||||||
|
],
|
||||||
|
).marginOnly(left: 30);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [tfa, bot],
|
children: [tfa, bot, trust],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1019,6 +1043,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
_OptionCheckBox(context, 'allow-only-conn-window-open-tip',
|
_OptionCheckBox(context, 'allow-only-conn-window-open-tip',
|
||||||
'allow-only-conn-window-open',
|
'allow-only-conn-window-open',
|
||||||
reverse: false, enabled: enabled),
|
reverse: false, enabled: enabled),
|
||||||
|
if (bind.mainIsInstalled()) unlockPin()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1266,6 +1291,40 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
}(),
|
}(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget unlockPin() {
|
||||||
|
bool enabled = !locked;
|
||||||
|
RxString unlockPin = bind.mainGetUnlockPin().obs;
|
||||||
|
update() async {
|
||||||
|
unlockPin.value = bind.mainGetUnlockPin();
|
||||||
|
}
|
||||||
|
|
||||||
|
onChanged(bool? checked) async {
|
||||||
|
changeUnlockPinDialog(unlockPin.value, update);
|
||||||
|
}
|
||||||
|
|
||||||
|
final isOptFixed = isOptionFixed(kOptionWhitelist);
|
||||||
|
return GestureDetector(
|
||||||
|
child: Obx(() => Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: unlockPin.isNotEmpty,
|
||||||
|
onChanged: enabled && !isOptFixed ? onChanged : null)
|
||||||
|
.marginOnly(right: 5),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
translate('Unlock with PIN'),
|
||||||
|
style: TextStyle(color: disabledTextColor(context, enabled)),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
onTap: enabled
|
||||||
|
? () {
|
||||||
|
onChanged(!unlockPin.isNotEmpty);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
).marginOnly(left: _kCheckBoxLeftMargin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Network extends StatefulWidget {
|
class _Network extends StatefulWidget {
|
||||||
@@ -2161,9 +2220,14 @@ Widget _lock(
|
|||||||
Text(translate(label)).marginOnly(left: 5),
|
Text(translate(label)).marginOnly(left: 5),
|
||||||
]).marginSymmetric(vertical: 2)),
|
]).marginSymmetric(vertical: 2)),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
bool checked = await callMainCheckSuperUserPermission();
|
final unlockPin = bind.mainGetUnlockPin();
|
||||||
if (checked) {
|
if (unlockPin.isEmpty) {
|
||||||
onUnlock();
|
bool checked = await callMainCheckSuperUserPermission();
|
||||||
|
if (checked) {
|
||||||
|
onUnlock();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
checkUnlockPinDialog(unlockPin, onUnlock);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
).marginSymmetric(horizontal: 2, vertical: 4),
|
).marginSymmetric(horizontal: 2, vertical: 4),
|
||||||
|
|||||||
@@ -44,21 +44,9 @@ class _DesktopTabPageState extends State<DesktopTabPage>
|
|||||||
final RxBool _block = false.obs;
|
final RxBool _block = false.obs;
|
||||||
// bool mouseIn = false;
|
// bool mouseIn = false;
|
||||||
|
|
||||||
@override
|
_DesktopTabPageState() {
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
||||||
super.didChangeAppLifecycleState(state);
|
|
||||||
if (state == AppLifecycleState.resumed) {
|
|
||||||
shouldBeBlocked(_block, canBeBlocked);
|
|
||||||
} else if (state == AppLifecycleState.inactive) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
// HardwareKeyboard.instance.addHandler(_handleKeyEvent);
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
Get.put<DesktopTabController>(tabController);
|
|
||||||
RemoteCountState.init();
|
RemoteCountState.init();
|
||||||
|
Get.put<DesktopTabController>(tabController);
|
||||||
tabController.add(TabInfo(
|
tabController.add(TabInfo(
|
||||||
key: kTabLabelHomePage,
|
key: kTabLabelHomePage,
|
||||||
label: kTabLabelHomePage,
|
label: kTabLabelHomePage,
|
||||||
@@ -81,6 +69,21 @@ class _DesktopTabPageState extends State<DesktopTabPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
super.didChangeAppLifecycleState(state);
|
||||||
|
if (state == AppLifecycleState.resumed) {
|
||||||
|
shouldBeBlocked(_block, canBeBlocked);
|
||||||
|
} else if (state == AppLifecycleState.inactive) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
// HardwareKeyboard.instance.addHandler(_handleKeyEvent);
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bool _handleKeyEvent(KeyEvent event) {
|
bool _handleKeyEvent(KeyEvent event) {
|
||||||
if (!mouseIn && event is KeyDownEvent) {
|
if (!mouseIn && event is KeyDownEvent) {
|
||||||
@@ -123,6 +126,7 @@ class _DesktopTabPageState extends State<DesktopTabPage>
|
|||||||
: Obx(
|
: Obx(
|
||||||
() => DragToResizeArea(
|
() => DragToResizeArea(
|
||||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
|
enableResizeEdges: windowManagerEnableResizeEdges,
|
||||||
child: tabWidget,
|
child: tabWidget,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -98,7 +98,10 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
}
|
}
|
||||||
debugPrint("File manager page init success with id ${widget.id}");
|
debugPrint("File manager page init success with id ${widget.id}");
|
||||||
_ffi.dialogManager.setOverlayState(_overlayKeyState);
|
_ffi.dialogManager.setOverlayState(_overlayKeyState);
|
||||||
widget.tabController.onSelected?.call(widget.id);
|
// Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
widget.tabController.onSelected?.call(widget.id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -259,6 +262,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
Offstage(
|
Offstage(
|
||||||
offstage: item.state != JobState.paused,
|
offstage: item.state != JobState.paused,
|
||||||
child: MenuButton(
|
child: MenuButton(
|
||||||
|
tooltip: translate("Resume"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
jobController.resumeJob(item.id);
|
jobController.resumeJob(item.id);
|
||||||
},
|
},
|
||||||
@@ -271,6 +275,7 @@ class _FileManagerPageState extends State<FileManagerPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
MenuButton(
|
MenuButton(
|
||||||
|
tooltip: translate("Delete"),
|
||||||
padding: EdgeInsets.only(right: 15),
|
padding: EdgeInsets.only(right: 15),
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
"assets/close.svg",
|
"assets/close.svg",
|
||||||
@@ -518,6 +523,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
MenuButton(
|
MenuButton(
|
||||||
|
tooltip: translate('Back'),
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
right: 3,
|
right: 3,
|
||||||
),
|
),
|
||||||
@@ -537,6 +543,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
MenuButton(
|
MenuButton(
|
||||||
|
tooltip: translate('Parent directory'),
|
||||||
child: RotatedBox(
|
child: RotatedBox(
|
||||||
quarterTurns: 3,
|
quarterTurns: 3,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
@@ -601,6 +608,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
switch (_locationStatus.value) {
|
switch (_locationStatus.value) {
|
||||||
case LocationStatus.bread:
|
case LocationStatus.bread:
|
||||||
return MenuButton(
|
return MenuButton(
|
||||||
|
tooltip: translate('Search'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_locationStatus.value = LocationStatus.fileSearchBar;
|
_locationStatus.value = LocationStatus.fileSearchBar;
|
||||||
Future.delayed(
|
Future.delayed(
|
||||||
@@ -627,6 +635,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
);
|
);
|
||||||
case LocationStatus.fileSearchBar:
|
case LocationStatus.fileSearchBar:
|
||||||
return MenuButton(
|
return MenuButton(
|
||||||
|
tooltip: translate('Clear'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
onSearchText("", isLocal);
|
onSearchText("", isLocal);
|
||||||
_locationStatus.value = LocationStatus.bread;
|
_locationStatus.value = LocationStatus.bread;
|
||||||
@@ -642,6 +651,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
MenuButton(
|
MenuButton(
|
||||||
|
tooltip: translate('Refresh File'),
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 3,
|
left: 3,
|
||||||
),
|
),
|
||||||
@@ -667,6 +677,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
isLocal ? MainAxisAlignment.start : MainAxisAlignment.end,
|
isLocal ? MainAxisAlignment.start : MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
MenuButton(
|
MenuButton(
|
||||||
|
tooltip: translate('Home'),
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
right: 3,
|
right: 3,
|
||||||
),
|
),
|
||||||
@@ -682,11 +693,27 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
hoverColor: Theme.of(context).hoverColor,
|
hoverColor: Theme.of(context).hoverColor,
|
||||||
),
|
),
|
||||||
MenuButton(
|
MenuButton(
|
||||||
|
tooltip: translate('Create Folder'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final name = TextEditingController();
|
final name = TextEditingController();
|
||||||
|
String? errorText;
|
||||||
_ffi.dialogManager.show((setState, close, context) {
|
_ffi.dialogManager.show((setState, close, context) {
|
||||||
|
name.addListener(() {
|
||||||
|
if (errorText != null) {
|
||||||
|
setState(() {
|
||||||
|
errorText = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
submit() {
|
submit() {
|
||||||
if (name.value.text.isNotEmpty) {
|
if (name.value.text.isNotEmpty) {
|
||||||
|
if (!PathUtil.validName(name.value.text,
|
||||||
|
controller.options.value.isWindows)) {
|
||||||
|
setState(() {
|
||||||
|
errorText = translate("Invalid folder name");
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
controller.createDir(PathUtil.join(
|
controller.createDir(PathUtil.join(
|
||||||
controller.directory.value.path,
|
controller.directory.value.path,
|
||||||
name.value.text,
|
name.value.text,
|
||||||
@@ -718,6 +745,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
labelText: translate(
|
labelText: translate(
|
||||||
"Please enter the folder name",
|
"Please enter the folder name",
|
||||||
),
|
),
|
||||||
|
errorText: errorText,
|
||||||
),
|
),
|
||||||
controller: name,
|
controller: name,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
@@ -751,6 +779,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
hoverColor: Theme.of(context).hoverColor,
|
hoverColor: Theme.of(context).hoverColor,
|
||||||
),
|
),
|
||||||
Obx(() => MenuButton(
|
Obx(() => MenuButton(
|
||||||
|
tooltip: translate('Delete'),
|
||||||
onPressed: SelectedItems.valid(selectedItems.items)
|
onPressed: SelectedItems.valid(selectedItems.items)
|
||||||
? () async {
|
? () async {
|
||||||
await (controller
|
await (controller
|
||||||
@@ -882,6 +911,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
||||||
},
|
},
|
||||||
child: MenuButton(
|
child: MenuButton(
|
||||||
|
tooltip: translate('More'),
|
||||||
onPressed: () => mod_menu.showMenu(
|
onPressed: () => mod_menu.showMenu(
|
||||||
context: context,
|
context: context,
|
||||||
position: menuPos,
|
position: menuPos,
|
||||||
@@ -971,6 +1001,7 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
final lastModifiedStr = entry.isDrive
|
final lastModifiedStr = entry.isDrive
|
||||||
? " "
|
? " "
|
||||||
: "${entry.lastModified().toString().replaceAll(".000", "")} ";
|
: "${entry.lastModified().toString().replaceAll(".000", "")} ";
|
||||||
|
var secondaryPosition = RelativeRect.fromLTRB(0, 0, 0, 0);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 1),
|
padding: EdgeInsets.symmetric(vertical: 1),
|
||||||
child: Obx(() => Container(
|
child: Obx(() => Container(
|
||||||
@@ -1035,6 +1066,35 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
_onSelectedChanged(
|
_onSelectedChanged(
|
||||||
items, filteredEntries, entry, isLocal);
|
items, filteredEntries, entry, isLocal);
|
||||||
},
|
},
|
||||||
|
onSecondaryTap: () {
|
||||||
|
final items = [
|
||||||
|
if (!entry.isDrive &&
|
||||||
|
versionCmp(_ffi.ffiModel.pi.version,
|
||||||
|
"1.3.0") >=
|
||||||
|
0)
|
||||||
|
mod_menu.PopupMenuItem(
|
||||||
|
child: Text("Rename"),
|
||||||
|
height: CustomPopupMenuTheme.height,
|
||||||
|
onTap: () {
|
||||||
|
controller.renameAction(entry, isLocal);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
];
|
||||||
|
if (items.isNotEmpty) {
|
||||||
|
mod_menu.showMenu(
|
||||||
|
context: context,
|
||||||
|
position: secondaryPosition,
|
||||||
|
items: items,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSecondaryTapDown: (details) {
|
||||||
|
secondaryPosition = RelativeRect.fromLTRB(
|
||||||
|
details.globalPosition.dx,
|
||||||
|
details.globalPosition.dy,
|
||||||
|
details.globalPosition.dx,
|
||||||
|
details.globalPosition.dy);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
WindowController.fromWindowId(windowId())
|
WindowController.fromWindowId(windowId())
|
||||||
.setTitle(getWindowNameWithId(id));
|
.setTitle(getWindowNameWithId(id));
|
||||||
};
|
};
|
||||||
|
tabController.onRemoved = (_, id) => onRemoveId(id);
|
||||||
tabController.add(TabInfo(
|
tabController.add(TabInfo(
|
||||||
key: params['id'],
|
key: params['id'],
|
||||||
label: params['id'],
|
label: params['id'],
|
||||||
@@ -54,8 +55,6 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
tabController.onRemoved = (_, id) => onRemoveId(id);
|
|
||||||
|
|
||||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||||
print(
|
print(
|
||||||
"[FileTransfer] call ${call.method} with args ${call.arguments} from window $fromWindowId to ${windowId()}");
|
"[FileTransfer] call ${call.method} with args ${call.arguments} from window $fromWindowId to ${windowId()}");
|
||||||
@@ -97,6 +96,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
controller: tabController,
|
controller: tabController,
|
||||||
onWindowCloseButton: handleWindowCloseButton,
|
onWindowCloseButton: handleWindowCloseButton,
|
||||||
tail: const AddButton(),
|
tail: const AddButton(),
|
||||||
|
selectedBorderColor: MyTheme.accent,
|
||||||
labelGetter: DesktopTab.tablabelGetter,
|
labelGetter: DesktopTab.tablabelGetter,
|
||||||
));
|
));
|
||||||
final tabWidget = isLinux
|
final tabWidget = isLinux
|
||||||
@@ -111,6 +111,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
: SubWindowDragToResizeArea(
|
: SubWindowDragToResizeArea(
|
||||||
child: tabWidget,
|
child: tabWidget,
|
||||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
|
enableResizeEdges: subWindowManagerEnableResizeEdges,
|
||||||
windowId: stateGlobal.windowId,
|
windowId: stateGlobal.windowId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
@@ -19,9 +21,7 @@ class InstallPage extends StatefulWidget {
|
|||||||
class _InstallPageState extends State<InstallPage> {
|
class _InstallPageState extends State<InstallPage> {
|
||||||
final tabController = DesktopTabController(tabType: DesktopTabType.main);
|
final tabController = DesktopTabController(tabType: DesktopTabType.main);
|
||||||
|
|
||||||
@override
|
_InstallPageState() {
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
Get.put<DesktopTabController>(tabController);
|
Get.put<DesktopTabController>(tabController);
|
||||||
const label = "install";
|
const label = "install";
|
||||||
tabController.add(TabInfo(
|
tabController.add(TabInfo(
|
||||||
@@ -43,6 +43,7 @@ class _InstallPageState extends State<InstallPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DragToResizeArea(
|
return DragToResizeArea(
|
||||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
|
enableResizeEdges: windowManagerEnableResizeEdges,
|
||||||
child: Container(
|
child: Container(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Theme.of(context).colorScheme.background,
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
@@ -73,10 +74,16 @@ class _InstallPageBodyState extends State<_InstallPageBody>
|
|||||||
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 12),
|
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 12),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_InstallPageBodyState() {
|
||||||
|
controller = TextEditingController(text: bind.installInstallPath());
|
||||||
|
final installOptions = jsonDecode(bind.installInstallOptions());
|
||||||
|
startmenu.value = installOptions['STARTMENUSHORTCUTS'] != '0';
|
||||||
|
desktopicon.value = installOptions['DESKTOPSHORTCUTS'] != '0';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
windowManager.addListener(this);
|
windowManager.addListener(this);
|
||||||
controller = TextEditingController(text: bind.installInstallPath());
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,6 +255,7 @@ class _InstallPageBodyState extends State<_InstallPageBody>
|
|||||||
if (desktopicon.value) args += ' desktopicon';
|
if (desktopicon.value) args += ' desktopicon';
|
||||||
bind.installInstallMe(options: args, path: controller.text);
|
bind.installInstallMe(options: args, path: controller.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
do_install();
|
do_install();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ class _PortForwardPageState extends State<PortForwardPage>
|
|||||||
isRdp: widget.isRDP);
|
isRdp: widget.isRDP);
|
||||||
Get.put<FFI>(_ffi, tag: 'pf_${widget.id}');
|
Get.put<FFI>(_ffi, tag: 'pf_${widget.id}');
|
||||||
debugPrint("Port forward page init success with id ${widget.id}");
|
debugPrint("Port forward page init success with id ${widget.id}");
|
||||||
widget.tabController.onSelected?.call(widget.id);
|
// Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
widget.tabController.onSelected?.call(widget.id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
WindowController.fromWindowId(windowId())
|
WindowController.fromWindowId(windowId())
|
||||||
.setTitle(getWindowNameWithId(id));
|
.setTitle(getWindowNameWithId(id));
|
||||||
};
|
};
|
||||||
|
tabController.onRemoved = (_, id) => onRemoveId(id);
|
||||||
tabController.add(TabInfo(
|
tabController.add(TabInfo(
|
||||||
key: params['id'],
|
key: params['id'],
|
||||||
label: params['id'],
|
label: params['id'],
|
||||||
@@ -54,8 +55,6 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
tabController.onRemoved = (_, id) => onRemoveId(id);
|
|
||||||
|
|
||||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||||
debugPrint(
|
debugPrint(
|
||||||
"[Port Forward] call ${call.method} with args ${call.arguments} from window $fromWindowId");
|
"[Port Forward] call ${call.method} with args ${call.arguments} from window $fromWindowId");
|
||||||
@@ -106,6 +105,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
tail: AddButton(),
|
tail: AddButton(),
|
||||||
|
selectedBorderColor: MyTheme.accent,
|
||||||
labelGetter: DesktopTab.tablabelGetter,
|
labelGetter: DesktopTab.tablabelGetter,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -127,6 +127,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
() => SubWindowDragToResizeArea(
|
() => SubWindowDragToResizeArea(
|
||||||
child: tabWidget,
|
child: tabWidget,
|
||||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
|
enableResizeEdges: subWindowManagerEnableResizeEdges,
|
||||||
windowId: stateGlobal.windowId,
|
windowId: stateGlobal.windowId,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ class RemotePage extends StatefulWidget {
|
|||||||
this.switchUuid,
|
this.switchUuid,
|
||||||
this.forceRelay,
|
this.forceRelay,
|
||||||
this.isSharedPassword,
|
this.isSharedPassword,
|
||||||
}) : super(key: key);
|
}) : super(key: key) {
|
||||||
|
initSharedStates(id);
|
||||||
|
}
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
final SessionID? sessionId;
|
final SessionID? sessionId;
|
||||||
@@ -64,7 +66,7 @@ class RemotePage extends StatefulWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
State<RemotePage> createState() {
|
State<RemotePage> createState() {
|
||||||
final state = _RemotePageState();
|
final state = _RemotePageState(id);
|
||||||
_lastState.value = state;
|
_lastState.value = state;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -94,8 +96,11 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
|
|
||||||
SessionID get sessionId => _ffi.sessionId;
|
SessionID get sessionId => _ffi.sessionId;
|
||||||
|
|
||||||
|
_RemotePageState(String id) {
|
||||||
|
_initStates(id);
|
||||||
|
}
|
||||||
|
|
||||||
void _initStates(String id) {
|
void _initStates(String id) {
|
||||||
initSharedStates(id);
|
|
||||||
_zoomCursor = PeerBoolOption.find(id, kOptionZoomCursor);
|
_zoomCursor = PeerBoolOption.find(id, kOptionZoomCursor);
|
||||||
_showRemoteCursor = ShowRemoteCursorState.find(id);
|
_showRemoteCursor = ShowRemoteCursorState.find(id);
|
||||||
_keyboardEnabled = KeyboardEnabledState.find(id);
|
_keyboardEnabled = KeyboardEnabledState.find(id);
|
||||||
@@ -105,7 +110,6 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_initStates(widget.id);
|
|
||||||
_ffi = FFI(widget.sessionId);
|
_ffi = FFI(widget.sessionId);
|
||||||
Get.put<FFI>(_ffi, tag: widget.id);
|
Get.put<FFI>(_ffi, tag: widget.id);
|
||||||
_ffi.imageModel.addCallbackOnFirstImage((String peerId) {
|
_ffi.imageModel.addCallbackOnFirstImage((String peerId) {
|
||||||
@@ -135,11 +139,13 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
if (!isWeb) bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote);
|
if (!isWeb) bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote);
|
||||||
_ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId);
|
_ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId);
|
||||||
_ffi.dialogManager.loadMobileActionsOverlayVisible();
|
_ffi.dialogManager.loadMobileActionsOverlayVisible();
|
||||||
// Session option should be set after models.dart/FFI.start
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_showRemoteCursor.value = bind.sessionGetToggleOptionSync(
|
// Session option should be set after models.dart/FFI.start
|
||||||
sessionId: sessionId, arg: 'show-remote-cursor');
|
_showRemoteCursor.value = bind.sessionGetToggleOptionSync(
|
||||||
_zoomCursor.value = bind.sessionGetToggleOptionSync(
|
sessionId: sessionId, arg: 'show-remote-cursor');
|
||||||
sessionId: sessionId, arg: kOptionZoomCursor);
|
_zoomCursor.value = bind.sessionGetToggleOptionSync(
|
||||||
|
sessionId: sessionId, arg: kOptionZoomCursor);
|
||||||
|
});
|
||||||
DesktopMultiWindow.addListener(this);
|
DesktopMultiWindow.addListener(this);
|
||||||
// if (!_isCustomCursorInited) {
|
// if (!_isCustomCursorInited) {
|
||||||
// customCursorController.registerNeedUpdateCursorCallback(
|
// customCursorController.registerNeedUpdateCursorCallback(
|
||||||
@@ -154,7 +160,10 @@ class _RemotePageState extends State<RemotePage>
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
_blockableOverlayState.applyFfi(_ffi);
|
_blockableOverlayState.applyFfi(_ffi);
|
||||||
widget.tabController?.onSelected?.call(widget.id);
|
// Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
widget.tabController?.onSelected?.call(widget.id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -565,11 +574,6 @@ class _ImagePaintState extends State<ImagePaint> {
|
|||||||
RxBool get remoteCursorMoved => widget.remoteCursorMoved;
|
RxBool get remoteCursorMoved => widget.remoteCursorMoved;
|
||||||
Widget Function(Widget)? get listenerBuilder => widget.listenerBuilder;
|
Widget Function(Widget)? get listenerBuilder => widget.listenerBuilder;
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final m = Provider.of<ImageModel>(context);
|
final m = Provider.of<ImageModel>(context);
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
final ffi = remotePage.ffi;
|
final ffi = remotePage.ffi;
|
||||||
bind.setCurSessionId(sessionId: ffi.sessionId);
|
bind.setCurSessionId(sessionId: ffi.sessionId);
|
||||||
}
|
}
|
||||||
WindowController.fromWindowId(windowId())
|
WindowController.fromWindowId(params['windowId'])
|
||||||
.setTitle(getWindowNameWithId(id));
|
.setTitle(getWindowNameWithId(id));
|
||||||
UnreadChatCountState.find(id).value = 0;
|
UnreadChatCountState.find(id).value = 0;
|
||||||
};
|
};
|
||||||
@@ -98,15 +98,14 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
));
|
));
|
||||||
_update_remote_count();
|
_update_remote_count();
|
||||||
}
|
}
|
||||||
|
tabController.onRemoved = (_, id) => onRemoveId(id);
|
||||||
|
rustDeskWinManager.setMethodHandler(_remoteMethodHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
tabController.onRemoved = (_, id) => onRemoveId(id);
|
|
||||||
|
|
||||||
rustDeskWinManager.setMethodHandler(_remoteMethodHandler);
|
|
||||||
if (!_isScreenRectSet) {
|
if (!_isScreenRectSet) {
|
||||||
Future.delayed(Duration.zero, () {
|
Future.delayed(Duration.zero, () {
|
||||||
restoreWindowPosition(
|
restoreWindowPosition(
|
||||||
@@ -121,11 +120,6 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final child = Scaffold(
|
final child = Scaffold(
|
||||||
@@ -134,6 +128,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
controller: tabController,
|
controller: tabController,
|
||||||
onWindowCloseButton: handleWindowCloseButton,
|
onWindowCloseButton: handleWindowCloseButton,
|
||||||
tail: const AddButton(),
|
tail: const AddButton(),
|
||||||
|
selectedBorderColor: MyTheme.accent,
|
||||||
pageViewBuilder: (pageView) => pageView,
|
pageViewBuilder: (pageView) => pageView,
|
||||||
labelGetter: DesktopTab.tablabelGetter,
|
labelGetter: DesktopTab.tablabelGetter,
|
||||||
tabBuilder: (key, icon, label, themeConf) => Obx(() {
|
tabBuilder: (key, icon, label, themeConf) => Obx(() {
|
||||||
@@ -233,6 +228,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
// Specially configured for a better resize area and remote control.
|
// Specially configured for a better resize area and remote control.
|
||||||
childPadding: kDragToResizeAreaPadding,
|
childPadding: kDragToResizeAreaPadding,
|
||||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
|
enableResizeEdges: subWindowManagerEnableResizeEdges,
|
||||||
windowId: stateGlobal.windowId,
|
windowId: stateGlobal.windowId,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -413,12 +409,14 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
final display = args['display'];
|
final display = args['display'];
|
||||||
final displays = args['displays'];
|
final displays = args['displays'];
|
||||||
final screenRect = parseParamScreenRect(args);
|
final screenRect = parseParamScreenRect(args);
|
||||||
|
final prePeerCount = tabController.length;
|
||||||
Future.delayed(Duration.zero, () async {
|
Future.delayed(Duration.zero, () async {
|
||||||
if (stateGlobal.fullscreen.isTrue) {
|
if (stateGlobal.fullscreen.isTrue) {
|
||||||
await WindowController.fromWindowId(windowId()).setFullscreen(false);
|
await WindowController.fromWindowId(windowId()).setFullscreen(false);
|
||||||
stateGlobal.setFullscreen(false, procWnd: false);
|
stateGlobal.setFullscreen(false, procWnd: false);
|
||||||
}
|
}
|
||||||
await setNewConnectWindowFrame(windowId(), id!, display, screenRect);
|
await setNewConnectWindowFrame(
|
||||||
|
windowId(), id!, prePeerCount, display, screenRect);
|
||||||
Future.delayed(Duration(milliseconds: isWindows ? 100 : 0), () async {
|
Future.delayed(Duration(milliseconds: isWindows ? 100 : 0), () async {
|
||||||
await windowOnTop(windowId());
|
await windowOnTop(windowId());
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,14 +32,18 @@ class DesktopServerPage extends StatefulWidget {
|
|||||||
class _DesktopServerPageState extends State<DesktopServerPage>
|
class _DesktopServerPageState extends State<DesktopServerPage>
|
||||||
with WindowListener, AutomaticKeepAliveClientMixin {
|
with WindowListener, AutomaticKeepAliveClientMixin {
|
||||||
final tabController = gFFI.serverModel.tabController;
|
final tabController = gFFI.serverModel.tabController;
|
||||||
@override
|
|
||||||
void initState() {
|
_DesktopServerPageState() {
|
||||||
gFFI.ffiModel.updateEventListener(gFFI.sessionId, "");
|
gFFI.ffiModel.updateEventListener(gFFI.sessionId, "");
|
||||||
windowManager.addListener(this);
|
|
||||||
Get.put<DesktopTabController>(tabController);
|
Get.put<DesktopTabController>(tabController);
|
||||||
tabController.onRemoved = (_, id) {
|
tabController.onRemoved = (_, id) {
|
||||||
onRemoveId(id);
|
onRemoveId(id);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
windowManager.addListener(this);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +83,7 @@ class _DesktopServerPageState extends State<DesktopServerPage>
|
|||||||
child: Consumer<ServerModel>(
|
child: Consumer<ServerModel>(
|
||||||
builder: (context, serverModel, child) {
|
builder: (context, serverModel, child) {
|
||||||
final body = Scaffold(
|
final body = Scaffold(
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
body: ConnectionManager(),
|
body: ConnectionManager(),
|
||||||
);
|
);
|
||||||
return isLinux
|
return isLinux
|
||||||
@@ -108,6 +112,28 @@ class ConnectionManagerState extends State<ConnectionManager>
|
|||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
final RxBool _block = false.obs;
|
final RxBool _block = false.obs;
|
||||||
|
|
||||||
|
ConnectionManagerState() {
|
||||||
|
gFFI.serverModel.tabController.onSelected = (client_id_str) {
|
||||||
|
final client_id = int.tryParse(client_id_str);
|
||||||
|
if (client_id != null) {
|
||||||
|
final client =
|
||||||
|
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == client_id);
|
||||||
|
if (client != null) {
|
||||||
|
gFFI.chatModel.changeCurrentKey(MessageKey(client.peerId, client.id));
|
||||||
|
if (client.unreadChatMessageCount.value > 0) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
client.unreadChatMessageCount.value = 0;
|
||||||
|
gFFI.chatModel.showChatPage(MessageKey(client.peerId, client.id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
windowManager.setTitle(getWindowNameWithId(client.peerId));
|
||||||
|
gFFI.cmFileModel.updateCurrentClientId(client.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
gFFI.chatModel.isConnManager = true;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
super.didChangeAppLifecycleState(state);
|
super.didChangeAppLifecycleState(state);
|
||||||
@@ -121,25 +147,6 @@ class ConnectionManagerState extends State<ConnectionManager>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
gFFI.serverModel.updateClientState();
|
gFFI.serverModel.updateClientState();
|
||||||
gFFI.serverModel.tabController.onSelected = (client_id_str) {
|
|
||||||
final client_id = int.tryParse(client_id_str);
|
|
||||||
if (client_id != null) {
|
|
||||||
final client =
|
|
||||||
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == client_id);
|
|
||||||
if (client != null) {
|
|
||||||
gFFI.chatModel.changeCurrentKey(MessageKey(client.peerId, client.id));
|
|
||||||
if (client.unreadChatMessageCount.value > 0) {
|
|
||||||
Future.delayed(Duration.zero, () {
|
|
||||||
client.unreadChatMessageCount.value = 0;
|
|
||||||
gFFI.chatModel.showChatPage(MessageKey(client.peerId, client.id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
windowManager.setTitle(getWindowNameWithId(client.peerId));
|
|
||||||
gFFI.cmFileModel.updateCurrentClientId(client.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
gFFI.chatModel.isConnManager = true;
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
@@ -186,8 +193,6 @@ class ConnectionManagerState extends State<ConnectionManager>
|
|||||||
maxLabelWidth: 100,
|
maxLabelWidth: 100,
|
||||||
tail: null, //buildScrollJumper(),
|
tail: null, //buildScrollJumper(),
|
||||||
blockTab: allowRemoteCMModification() ? null : _block,
|
blockTab: allowRemoteCMModification() ? null : _block,
|
||||||
selectedTabBackgroundColor:
|
|
||||||
Theme.of(context).hintColor.withOpacity(0),
|
|
||||||
tabBuilder: (key, icon, label, themeConf) {
|
tabBuilder: (key, icon, label, themeConf) {
|
||||||
final client = serverModel.clients
|
final client = serverModel.clients
|
||||||
.firstWhereOrNull((client) => client.id.toString() == key);
|
.firstWhereOrNull((client) => client.id.toString() == key);
|
||||||
@@ -222,7 +227,7 @@ class ConnectionManagerState extends State<ConnectionManager>
|
|||||||
borderWidth;
|
borderWidth;
|
||||||
final realChatPageWidth =
|
final realChatPageWidth =
|
||||||
constrains.maxWidth - realClosedWidth;
|
constrains.maxWidth - realClosedWidth;
|
||||||
return Row(children: [
|
final row = Row(children: [
|
||||||
if (constrains.maxWidth >
|
if (constrains.maxWidth >
|
||||||
kConnectionManagerWindowSizeClosedChat.width)
|
kConnectionManagerWindowSizeClosedChat.width)
|
||||||
Consumer<ChatModel>(
|
Consumer<ChatModel>(
|
||||||
@@ -240,6 +245,10 @@ class ConnectionManagerState extends State<ConnectionManager>
|
|||||||
child:
|
child:
|
||||||
SizedBox(width: realClosedWidth, child: pageView)),
|
SizedBox(width: realClosedWidth, child: pageView)),
|
||||||
]);
|
]);
|
||||||
|
return Container(
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
child: row,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -399,7 +408,10 @@ class _CmHeaderState extends State<_CmHeader>
|
|||||||
_time.value = _time.value + 1;
|
_time.value = _time.value + 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
gFFI.serverModel.tabController.onSelected?.call(client.id.toString());
|
// Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
gFFI.serverModel.tabController.onSelected?.call(client.id.toString());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -732,7 +744,8 @@ class _CmControlPanel extends StatelessWidget {
|
|||||||
child: buildButton(context,
|
child: buildButton(context,
|
||||||
color: MyTheme.accent,
|
color: MyTheme.accent,
|
||||||
onClick: null, onTapDown: (details) async {
|
onClick: null, onTapDown: (details) async {
|
||||||
final devicesInfo = await AudioInput.getDevicesInfo(true, true);
|
final devicesInfo =
|
||||||
|
await AudioInput.getDevicesInfo(true, true);
|
||||||
List<String> devices = devicesInfo['devices'] as List<String>;
|
List<String> devices = devicesInfo['devices'] as List<String>;
|
||||||
if (devices.isEmpty) {
|
if (devices.isEmpty) {
|
||||||
msgBox(
|
msgBox(
|
||||||
@@ -764,7 +777,8 @@ class _CmControlPanel extends StatelessWidget {
|
|||||||
value: d,
|
value: d,
|
||||||
groupValue: currentDevice,
|
groupValue: currentDevice,
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
if (v != null) AudioInput.setDevice(v, true, true);
|
if (v != null)
|
||||||
|
AudioInput.setDevice(v, true, true);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -1143,6 +1157,16 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
|
|||||||
Text(translate('Create Folder'))
|
Text(translate('Create Folder'))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
case CmFileAction.rename:
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.drive_file_move_outlined,
|
||||||
|
color: Theme.of(context).tabBarTheme.labelColor,
|
||||||
|
),
|
||||||
|
Text(translate('Rename'))
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class _MenuButtonState extends State<MenuButton> {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: widget.padding,
|
padding: widget.padding,
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
|
waitDuration: Duration(milliseconds: 300),
|
||||||
message: widget.tooltip,
|
message: widget.tooltip,
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
|
|||||||
@@ -38,18 +38,16 @@ class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
MyPopupMenuItemState<T, PopupMenuChildrenItem<T>> createState() =>
|
MyPopupMenuItemState<T, PopupMenuChildrenItem<T>> createState() =>
|
||||||
MyPopupMenuItemState<T, PopupMenuChildrenItem<T>>();
|
MyPopupMenuItemState<T, PopupMenuChildrenItem<T>>(enabled?.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
|
class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
|
||||||
extends State<W> {
|
extends State<W> {
|
||||||
RxBool enabled = true.obs;
|
RxBool enabled = true.obs;
|
||||||
|
|
||||||
@override
|
MyPopupMenuItemState(bool? e) {
|
||||||
void initState() {
|
if (e != null) {
|
||||||
super.initState();
|
enabled.value = e;
|
||||||
if (widget.enabled != null) {
|
|
||||||
enabled.value = widget.enabled!.value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
|
|||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
Future.delayed(Duration.zero, () async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
_fractionX.value = double.tryParse(await bind.sessionGetOption(
|
_fractionX.value = double.tryParse(await bind.sessionGetOption(
|
||||||
sessionId: widget.ffi.sessionId,
|
sessionId: widget.ffi.sessionId,
|
||||||
arg: 'remote-menubar-drag-x') ??
|
arg: 'remote-menubar-drag-x') ??
|
||||||
@@ -1032,11 +1032,6 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
FFI get ffi => widget.ffi;
|
FFI get ffi => widget.ffi;
|
||||||
String get id => widget.id;
|
String get id => widget.id;
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_screenAdjustor.updateScreen();
|
_screenAdjustor.updateScreen();
|
||||||
@@ -1278,7 +1273,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_getLocalResolutionWayland();
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_getLocalResolutionWayland();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect? scaledRect() {
|
Rect? scaledRect() {
|
||||||
|
|||||||
@@ -227,8 +227,7 @@ typedef TabMenuBuilder = Widget Function(String key);
|
|||||||
typedef LabelGetter = Rx<String> Function(String key);
|
typedef LabelGetter = Rx<String> Function(String key);
|
||||||
|
|
||||||
/// [_lastClickTime], help to handle double click
|
/// [_lastClickTime], help to handle double click
|
||||||
int _lastClickTime =
|
int _lastClickTime = 0;
|
||||||
DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000;
|
|
||||||
|
|
||||||
class DesktopTab extends StatefulWidget {
|
class DesktopTab extends StatefulWidget {
|
||||||
final bool showLogo;
|
final bool showLogo;
|
||||||
@@ -506,17 +505,20 @@ class _DesktopTabState extends State<DesktopTab>
|
|||||||
Obx(() {
|
Obx(() {
|
||||||
if (stateGlobal.showTabBar.isTrue &&
|
if (stateGlobal.showTabBar.isTrue &&
|
||||||
!(kUseCompatibleUiMode && isHideSingleItem())) {
|
!(kUseCompatibleUiMode && isHideSingleItem())) {
|
||||||
|
final showBottomDivider = _showTabBarBottomDivider(tabType);
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: _kTabBarHeight,
|
height: _kTabBarHeight,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: _kTabBarHeight - 1,
|
height:
|
||||||
|
showBottomDivider ? _kTabBarHeight - 1 : _kTabBarHeight,
|
||||||
child: _buildBar(),
|
child: _buildBar(),
|
||||||
),
|
),
|
||||||
const Divider(
|
if (showBottomDivider)
|
||||||
height: 1,
|
const Divider(
|
||||||
),
|
height: 1,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -727,16 +729,6 @@ class WindowActionPanel extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WindowActionPanelState extends State<WindowActionPanel> {
|
class WindowActionPanelState extends State<WindowActionPanel> {
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool showTabDowndown() {
|
bool showTabDowndown() {
|
||||||
return widget.tabController.state.value.tabs.length > 1 &&
|
return widget.tabController.state.value.tabs.length > 1 &&
|
||||||
(widget.tabController.tabType == DesktopTabType.remoteScreen ||
|
(widget.tabController.tabType == DesktopTabType.remoteScreen ||
|
||||||
@@ -1172,7 +1164,10 @@ class _TabState extends State<_Tab> with RestorationMixin {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: _kTabBarHeight,
|
// _kTabBarHeight also displays normally
|
||||||
|
height: _showTabBarBottomDivider(widget.tabType)
|
||||||
|
? _kTabBarHeight - 1
|
||||||
|
: _kTabBarHeight,
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@@ -1271,13 +1266,7 @@ class ActionIcon extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ActionIconState extends State<ActionIcon> {
|
class _ActionIconState extends State<ActionIcon> {
|
||||||
var hover = false.obs;
|
final hover = false.obs;
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
hover.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -1431,6 +1420,10 @@ class _TabDropDownButtonState extends State<_TabDropDownButton> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _showTabBarBottomDivider(DesktopTabType tabType) {
|
||||||
|
return tabType == DesktopTabType.main || tabType == DesktopTabType.install;
|
||||||
|
}
|
||||||
|
|
||||||
class TabbarTheme extends ThemeExtension<TabbarTheme> {
|
class TabbarTheme extends ThemeExtension<TabbarTheme> {
|
||||||
final Color? selectedTabIconColor;
|
final Color? selectedTabIconColor;
|
||||||
final Color? unSelectedTabIconColor;
|
final Color? unSelectedTabIconColor;
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ showCmWindow({bool isStartup = false}) async {
|
|||||||
WindowOptions windowOptions = getHiddenTitleBarWindowOptions(
|
WindowOptions windowOptions = getHiddenTitleBarWindowOptions(
|
||||||
size: kConnectionManagerWindowSizeClosedChat, alwaysOnTop: true);
|
size: kConnectionManagerWindowSizeClosedChat, alwaysOnTop: true);
|
||||||
await windowManager.waitUntilReadyToShow(windowOptions, null);
|
await windowManager.waitUntilReadyToShow(windowOptions, null);
|
||||||
bind.mainHideDocker();
|
bind.mainHideDock();
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
windowManager.show(),
|
windowManager.show(),
|
||||||
windowManager.focus(),
|
windowManager.focus(),
|
||||||
@@ -288,14 +288,14 @@ hideCmWindow({bool isStartup = false}) async {
|
|||||||
size: kConnectionManagerWindowSizeClosedChat);
|
size: kConnectionManagerWindowSizeClosedChat);
|
||||||
windowManager.setOpacity(0);
|
windowManager.setOpacity(0);
|
||||||
await windowManager.waitUntilReadyToShow(windowOptions, null);
|
await windowManager.waitUntilReadyToShow(windowOptions, null);
|
||||||
bind.mainHideDocker();
|
bind.mainHideDock();
|
||||||
await windowManager.minimize();
|
await windowManager.minimize();
|
||||||
await windowManager.hide();
|
await windowManager.hide();
|
||||||
_isCmReadyToShow = true;
|
_isCmReadyToShow = true;
|
||||||
} else if (_isCmReadyToShow) {
|
} else if (_isCmReadyToShow) {
|
||||||
if (await windowManager.getOpacity() != 0) {
|
if (await windowManager.getOpacity() != 0) {
|
||||||
await windowManager.setOpacity(0);
|
await windowManager.setOpacity(0);
|
||||||
bind.mainHideDocker();
|
bind.mainHideDock();
|
||||||
await windowManager.minimize();
|
await windowManager.minimize();
|
||||||
await windowManager.hide();
|
await windowManager.hide();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,19 +50,26 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
bool isPeersLoaded = false;
|
bool isPeersLoaded = false;
|
||||||
StreamSubscription? _uniLinksSubscription;
|
StreamSubscription? _uniLinksSubscription;
|
||||||
|
|
||||||
|
_ConnectionPageState() {
|
||||||
|
if (!isWeb) _uniLinksSubscription = listenUniLinks();
|
||||||
|
_idController.addListener(() {
|
||||||
|
_idEmpty.value = _idController.text.isEmpty;
|
||||||
|
});
|
||||||
|
Get.put<IDTextEditingController>(_idController);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
if (!isWeb) _uniLinksSubscription = listenUniLinks();
|
|
||||||
if (_idController.text.isEmpty) {
|
if (_idController.text.isEmpty) {
|
||||||
() async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
final lastRemoteId = await bind.mainGetLastRemoteId();
|
final lastRemoteId = await bind.mainGetLastRemoteId();
|
||||||
if (lastRemoteId != _idController.id) {
|
if (lastRemoteId != _idController.id) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_idController.id = lastRemoteId;
|
_idController.id = lastRemoteId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}();
|
});
|
||||||
}
|
}
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
if (!bind.isCustomClient()) {
|
if (!bind.isCustomClient()) {
|
||||||
@@ -72,11 +79,6 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_idController.addListener(() {
|
|
||||||
_idEmpty.value = _idController.text.isEmpty;
|
|
||||||
});
|
|
||||||
Get.put<IDTextEditingController>(_idController);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -204,36 +204,54 @@ class _FileManagerPageState extends State<FileManagerPage> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
} else if (v == "folder") {
|
} else if (v == "folder") {
|
||||||
final name = TextEditingController();
|
final name = TextEditingController();
|
||||||
gFFI.dialogManager
|
String? errorText;
|
||||||
.show((setState, close, context) => CustomAlertDialog(
|
gFFI.dialogManager.show((setState, close, context) {
|
||||||
title: Text(translate("Create Folder")),
|
name.addListener(() {
|
||||||
content: Column(
|
if (errorText != null) {
|
||||||
mainAxisSize: MainAxisSize.min,
|
setState(() {
|
||||||
children: [
|
errorText = null;
|
||||||
TextFormField(
|
});
|
||||||
decoration: InputDecoration(
|
}
|
||||||
labelText: translate(
|
});
|
||||||
"Please enter the folder name"),
|
return CustomAlertDialog(
|
||||||
),
|
title: Text(translate("Create Folder")),
|
||||||
controller: name,
|
content: Column(
|
||||||
),
|
mainAxisSize: MainAxisSize.min,
|
||||||
],
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText:
|
||||||
|
translate("Please enter the folder name"),
|
||||||
|
errorText: errorText,
|
||||||
),
|
),
|
||||||
actions: [
|
controller: name,
|
||||||
dialogButton("Cancel",
|
),
|
||||||
onPressed: () => close(false),
|
],
|
||||||
isOutline: true),
|
),
|
||||||
dialogButton("OK", onPressed: () {
|
actions: [
|
||||||
if (name.value.text.isNotEmpty) {
|
dialogButton("Cancel",
|
||||||
currentFileController.createDir(
|
onPressed: () => close(false), isOutline: true),
|
||||||
PathUtil.join(
|
dialogButton("OK", onPressed: () {
|
||||||
currentDir.path,
|
if (name.value.text.isNotEmpty) {
|
||||||
name.value.text,
|
if (!PathUtil.validName(
|
||||||
currentOptions.isWindows));
|
name.value.text,
|
||||||
close();
|
currentFileController
|
||||||
}
|
.options.value.isWindows)) {
|
||||||
})
|
setState(() {
|
||||||
]));
|
errorText =
|
||||||
|
translate("Invalid folder name");
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentFileController.createDir(PathUtil.join(
|
||||||
|
currentDir.path,
|
||||||
|
name.value.text,
|
||||||
|
currentOptions.isWindows));
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
});
|
||||||
} else if (v == "hidden") {
|
} else if (v == "hidden") {
|
||||||
currentFileController.toggleShowHidden();
|
currentFileController.toggleShowHidden();
|
||||||
}
|
}
|
||||||
@@ -497,7 +515,15 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
child: Text(translate("Properties")),
|
child: Text(translate("Properties")),
|
||||||
value: "properties",
|
value: "properties",
|
||||||
enabled: false,
|
enabled: false,
|
||||||
)
|
),
|
||||||
|
if (!entries[index].isDrive &&
|
||||||
|
versionCmp(gFFI.ffiModel.pi.version,
|
||||||
|
"1.3.0") >=
|
||||||
|
0)
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Text(translate("Rename")),
|
||||||
|
value: "rename",
|
||||||
|
)
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
onSelected: (v) {
|
onSelected: (v) {
|
||||||
@@ -509,6 +535,9 @@ class _FileManagerViewState extends State<FileManagerView> {
|
|||||||
_selectedItems.clear();
|
_selectedItems.clear();
|
||||||
widget.selectMode.toggle(isLocal);
|
widget.selectMode.toggle(isLocal);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
} else if (v == "rename") {
|
||||||
|
controller.renameAction(
|
||||||
|
entries[index], isLocal);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class RemotePage extends StatefulWidget {
|
|||||||
final bool? isSharedPassword;
|
final bool? isSharedPassword;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RemotePage> createState() => _RemotePageState();
|
State<RemotePage> createState() => _RemotePageState(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RemotePageState extends State<RemotePage> {
|
class _RemotePageState extends State<RemotePage> {
|
||||||
@@ -58,6 +58,12 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
final TextEditingController _textController =
|
final TextEditingController _textController =
|
||||||
TextEditingController(text: initText);
|
TextEditingController(text: initText);
|
||||||
|
|
||||||
|
_RemotePageState(String id) {
|
||||||
|
initSharedStates(id);
|
||||||
|
gFFI.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted;
|
||||||
|
gFFI.dialogManager.loadMobileActionsOverlayVisible();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -80,12 +86,9 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
gFFI.qualityMonitorModel.checkShowQualityMonitor(sessionId);
|
gFFI.qualityMonitorModel.checkShowQualityMonitor(sessionId);
|
||||||
keyboardSubscription =
|
keyboardSubscription =
|
||||||
keyboardVisibilityController.onChange.listen(onSoftKeyboardChanged);
|
keyboardVisibilityController.onChange.listen(onSoftKeyboardChanged);
|
||||||
initSharedStates(widget.id);
|
|
||||||
gFFI.chatModel
|
gFFI.chatModel
|
||||||
.changeCurrentKey(MessageKey(widget.id, ChatModel.clientModeID));
|
.changeCurrentKey(MessageKey(widget.id, ChatModel.clientModeID));
|
||||||
gFFI.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted;
|
|
||||||
_blockableOverlayState.applyFfi(gFFI);
|
_blockableOverlayState.applyFfi(gFFI);
|
||||||
gFFI.dialogManager.loadMobileActionsOverlayVisible();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -776,11 +779,6 @@ class _KeyHelpToolsState extends State<KeyHelpTools> {
|
|||||||
onPressed: onPressed);
|
onPressed: onPressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateRect() {
|
_updateRect() {
|
||||||
RenderObject? renderObject = _key.currentContext?.findRenderObject();
|
RenderObject? renderObject = _key.currentContext?.findRenderObject();
|
||||||
if (renderObject == null) {
|
if (renderObject == null) {
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ class _ServerPageState extends State<ServerPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
buildPresetPasswordWarning(),
|
buildPresetPasswordWarningMobile(),
|
||||||
gFFI.serverModel.isStart
|
gFFI.serverModel.isStart
|
||||||
? ServerInfo()
|
? ServerInfo()
|
||||||
: ServiceNotRunningNotification(),
|
: ServiceNotRunningNotification(),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
|
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
|
||||||
@@ -87,12 +88,9 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
var _hideServer = false;
|
var _hideServer = false;
|
||||||
var _hideProxy = false;
|
var _hideProxy = false;
|
||||||
var _hideNetwork = false;
|
var _hideNetwork = false;
|
||||||
|
var _enableTrustedDevices = false;
|
||||||
|
|
||||||
@override
|
_SettingsState() {
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
|
|
||||||
_enableAbr = option2bool(
|
_enableAbr = option2bool(
|
||||||
kOptionEnableAbr, bind.mainGetOptionSync(key: kOptionEnableAbr));
|
kOptionEnableAbr, bind.mainGetOptionSync(key: kOptionEnableAbr));
|
||||||
_denyLANDiscovery = !option2bool(kOptionEnableLanDiscovery,
|
_denyLANDiscovery = !option2bool(kOptionEnableLanDiscovery,
|
||||||
@@ -117,8 +115,15 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
_hideProxy = bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
|
_hideProxy = bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
|
||||||
_hideNetwork =
|
_hideNetwork =
|
||||||
bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y';
|
bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y';
|
||||||
|
_enableTrustedDevices = mainGetBoolOptionSync(kOptionEnableTrustedDevices);
|
||||||
|
}
|
||||||
|
|
||||||
() async {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
var update = false;
|
var update = false;
|
||||||
|
|
||||||
if (_hasIgnoreBattery) {
|
if (_hasIgnoreBattery) {
|
||||||
@@ -177,7 +182,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
if (update) {
|
if (update) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
}();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -241,18 +246,76 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
],
|
],
|
||||||
));
|
));
|
||||||
final List<AbstractSettingsTile> enhancementsTiles = [];
|
final List<AbstractSettingsTile> enhancementsTiles = [];
|
||||||
final List<AbstractSettingsTile> shareScreenTiles = [
|
final enable2fa = bind.mainHasValid2FaSync();
|
||||||
|
final List<AbstractSettingsTile> tfaTiles = [
|
||||||
SettingsTile.switchTile(
|
SettingsTile.switchTile(
|
||||||
title: Text(translate('enable-2fa-title')),
|
title: Text(translate('enable-2fa-title')),
|
||||||
initialValue: bind.mainHasValid2FaSync(),
|
initialValue: enable2fa,
|
||||||
onToggle: (_) async {
|
onToggle: (v) async {
|
||||||
update() async {
|
update() async {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
change2fa(callback: update);
|
if (v == false) {
|
||||||
|
CommonConfirmDialog(
|
||||||
|
gFFI.dialogManager, translate('cancel-2fa-confirm-tip'), () {
|
||||||
|
change2fa(callback: update);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
change2fa(callback: update);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (enable2fa)
|
||||||
|
SettingsTile.switchTile(
|
||||||
|
title: Text(translate('Telegram bot')),
|
||||||
|
initialValue: bind.mainHasValidBotSync(),
|
||||||
|
onToggle: (v) async {
|
||||||
|
update() async {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v == false) {
|
||||||
|
CommonConfirmDialog(
|
||||||
|
gFFI.dialogManager, translate('cancel-bot-confirm-tip'), () {
|
||||||
|
changeBot(callback: update);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
changeBot(callback: update);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (enable2fa)
|
||||||
|
SettingsTile.switchTile(
|
||||||
|
title: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(translate('Enable trusted devices')),
|
||||||
|
Text('* ${translate('enable-trusted-devices-tip')}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
initialValue: _enableTrustedDevices,
|
||||||
|
onToggle: isOptionFixed(kOptionEnableTrustedDevices)
|
||||||
|
? null
|
||||||
|
: (v) async {
|
||||||
|
mainSetBoolOption(kOptionEnableTrustedDevices, v);
|
||||||
|
setState(() {
|
||||||
|
_enableTrustedDevices = v;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (enable2fa && _enableTrustedDevices)
|
||||||
|
SettingsTile(
|
||||||
|
title: Text(translate('Manage trusted devices')),
|
||||||
|
trailing: Icon(Icons.arrow_forward_ios),
|
||||||
|
onPressed: (context) {
|
||||||
|
Navigator.push(context, MaterialPageRoute(builder: (context) {
|
||||||
|
return _ManageTrustedDevices();
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
];
|
||||||
|
final List<AbstractSettingsTile> shareScreenTiles = [
|
||||||
SettingsTile.switchTile(
|
SettingsTile.switchTile(
|
||||||
title: Text(translate('Deny LAN discovery')),
|
title: Text(translate('Deny LAN discovery')),
|
||||||
initialValue: _denyLANDiscovery,
|
initialValue: _denyLANDiscovery,
|
||||||
@@ -640,6 +703,11 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
if (isAndroid &&
|
||||||
|
!disabledSettings &&
|
||||||
|
!outgoingOnly &&
|
||||||
|
!hideSecuritySettings)
|
||||||
|
SettingsSection(title: Text('2FA'), tiles: tfaTiles),
|
||||||
if (isAndroid &&
|
if (isAndroid &&
|
||||||
!disabledSettings &&
|
!disabledSettings &&
|
||||||
!outgoingOnly &&
|
!outgoingOnly &&
|
||||||
@@ -961,6 +1029,51 @@ class __DisplayPageState extends State<_DisplayPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ManageTrustedDevices extends StatefulWidget {
|
||||||
|
const _ManageTrustedDevices();
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_ManageTrustedDevices> createState() => __ManageTrustedDevicesState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class __ManageTrustedDevicesState extends State<_ManageTrustedDevices> {
|
||||||
|
RxList<TrustedDevice> trustedDevices = RxList.empty(growable: true);
|
||||||
|
RxList<Uint8List> selectedDevices = RxList.empty();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(translate('Manage trusted devices')),
|
||||||
|
centerTitle: true,
|
||||||
|
actions: [
|
||||||
|
Obx(() => IconButton(
|
||||||
|
icon: Icon(Icons.delete, color: Colors.white),
|
||||||
|
onPressed: selectedDevices.isEmpty
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
confrimDeleteTrustedDevicesDialog(
|
||||||
|
trustedDevices, selectedDevices);
|
||||||
|
}))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: FutureBuilder(
|
||||||
|
future: TrustedDevice.get(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return Center(child: Text('Error: ${snapshot.error}'));
|
||||||
|
}
|
||||||
|
final devices = snapshot.data as List<TrustedDevice>;
|
||||||
|
trustedDevices = devices.obs;
|
||||||
|
return trustedDevicesTable(trustedDevices, selectedDevices);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _RadioEntry {
|
class _RadioEntry {
|
||||||
final String label;
|
final String label;
|
||||||
final String value;
|
final String value;
|
||||||
|
|||||||
@@ -41,18 +41,16 @@ class GestureHelp extends StatefulWidget {
|
|||||||
final OnTouchModeChange onTouchModeChange;
|
final OnTouchModeChange onTouchModeChange;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _GestureHelpState();
|
State<StatefulWidget> createState() => _GestureHelpState(touchMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GestureHelpState extends State<GestureHelp> {
|
class _GestureHelpState extends State<GestureHelp> {
|
||||||
var _selectedIndex;
|
late int _selectedIndex;
|
||||||
var _touchMode;
|
late bool _touchMode;
|
||||||
|
|
||||||
@override
|
_GestureHelpState(bool touchMode) {
|
||||||
void initState() {
|
_touchMode = touchMode;
|
||||||
_touchMode = widget.touchMode;
|
|
||||||
_selectedIndex = _touchMode ? 1 : 0;
|
_selectedIndex = _touchMode ? 1 : 0;
|
||||||
super.initState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ import '../common.dart';
|
|||||||
|
|
||||||
final syncAbOption = 'sync-ab-with-recent-sessions';
|
final syncAbOption = 'sync-ab-with-recent-sessions';
|
||||||
bool shouldSyncAb() {
|
bool shouldSyncAb() {
|
||||||
return bind.mainGetLocalOption(key: syncAbOption).isNotEmpty;
|
return bind.mainGetLocalOption(key: syncAbOption) == 'Y';
|
||||||
}
|
}
|
||||||
|
|
||||||
final sortAbTagsOption = 'sync-ab-tags';
|
final sortAbTagsOption = 'sync-ab-tags';
|
||||||
bool shouldSortTags() {
|
bool shouldSortTags() {
|
||||||
return bind.mainGetLocalOption(key: sortAbTagsOption).isNotEmpty;
|
return bind.mainGetLocalOption(key: sortAbTagsOption) == 'Y';
|
||||||
}
|
}
|
||||||
|
|
||||||
final filterAbTagOption = 'filter-ab-by-intersection';
|
final filterAbTagOption = 'filter-ab-by-intersection';
|
||||||
bool filterAbTagByIntersection() {
|
bool filterAbTagByIntersection() {
|
||||||
return bind.mainGetLocalOption(key: filterAbTagOption).isNotEmpty;
|
return bind.mainGetLocalOption(key: filterAbTagOption) == 'Y';
|
||||||
}
|
}
|
||||||
|
|
||||||
const _personalAddressBookName = "My address book";
|
const _personalAddressBookName = "My address book";
|
||||||
@@ -111,9 +111,10 @@ class AbModel {
|
|||||||
Future<void> _pullAb(
|
Future<void> _pullAb(
|
||||||
{required ForcePullAb? force, required bool quiet}) async {
|
{required ForcePullAb? force, required bool quiet}) async {
|
||||||
if (bind.isDisableAb()) return;
|
if (bind.isDisableAb()) return;
|
||||||
debugPrint("pullAb, force: $force, quiet: $quiet");
|
|
||||||
if (!gFFI.userModel.isLogin) return;
|
if (!gFFI.userModel.isLogin) return;
|
||||||
|
if (gFFI.userModel.networkError.isNotEmpty) return;
|
||||||
if (force == null && listInitialized && current.initialized) return;
|
if (force == null && listInitialized && current.initialized) return;
|
||||||
|
debugPrint("pullAb, force: $force, quiet: $quiet");
|
||||||
if (!listInitialized || force == ForcePullAb.listAndCurrent) {
|
if (!listInitialized || force == ForcePullAb.listAndCurrent) {
|
||||||
try {
|
try {
|
||||||
// Read personal guid every time to avoid upgrading the server without closing the main window
|
// Read personal guid every time to avoid upgrading the server without closing the main window
|
||||||
@@ -815,8 +816,6 @@ abstract class BaseAb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LegacyAb extends BaseAb {
|
class LegacyAb extends BaseAb {
|
||||||
final sortTags = shouldSortTags().obs;
|
|
||||||
final filterByIntersection = filterAbTagByIntersection().obs;
|
|
||||||
bool get emtpy => peers.isEmpty && tags.isEmpty;
|
bool get emtpy => peers.isEmpty && tags.isEmpty;
|
||||||
// licensedDevices is obtained from personal ab, shared ab restrict it in server
|
// licensedDevices is obtained from personal ab, shared ab restrict it in server
|
||||||
var licensedDevices = 0;
|
var licensedDevices = 0;
|
||||||
@@ -1209,8 +1208,6 @@ class LegacyAb extends BaseAb {
|
|||||||
class Ab extends BaseAb {
|
class Ab extends BaseAb {
|
||||||
AbProfile profile;
|
AbProfile profile;
|
||||||
late final bool personal;
|
late final bool personal;
|
||||||
final sortTags = shouldSortTags().obs;
|
|
||||||
final filterByIntersection = filterAbTagByIntersection().obs;
|
|
||||||
bool get emtpy => peers.isEmpty && tags.isEmpty;
|
bool get emtpy => peers.isEmpty && tags.isEmpty;
|
||||||
|
|
||||||
Ab(this.profile, this.personal);
|
Ab(this.profile, this.personal);
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ class CmFileModel {
|
|||||||
_onFileRemove(evt['remove']);
|
_onFileRemove(evt['remove']);
|
||||||
} else if (evt['create_dir'] != null) {
|
} else if (evt['create_dir'] != null) {
|
||||||
_onDirCreate(evt['create_dir']);
|
_onDirCreate(evt['create_dir']);
|
||||||
|
} else if (evt['rename'] != null) {
|
||||||
|
_onRename(evt['rename']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,8 +61,6 @@ class CmFileModel {
|
|||||||
|
|
||||||
_dealOneJob(dynamic l, bool calcSpeed) {
|
_dealOneJob(dynamic l, bool calcSpeed) {
|
||||||
final data = TransferJobSerdeData.fromJson(l);
|
final data = TransferJobSerdeData.fromJson(l);
|
||||||
Client? client =
|
|
||||||
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId);
|
|
||||||
var jobTable = _jobTables[data.connId];
|
var jobTable = _jobTables[data.connId];
|
||||||
if (jobTable == null) {
|
if (jobTable == null) {
|
||||||
debugPrint("jobTable should not be null");
|
debugPrint("jobTable should not be null");
|
||||||
@@ -70,12 +70,7 @@ class CmFileModel {
|
|||||||
if (job == null) {
|
if (job == null) {
|
||||||
job = CmFileLog();
|
job = CmFileLog();
|
||||||
jobTable.add(job);
|
jobTable.add(job);
|
||||||
final currentSelectedTab =
|
_addUnread(data.connId);
|
||||||
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
|
||||||
if (!(gFFI.chatModel.isShowCMSidePage &&
|
|
||||||
currentSelectedTab.key == data.connId.toString())) {
|
|
||||||
client?.unreadChatMessageCount.value += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
job.id = data.id;
|
job.id = data.id;
|
||||||
job.action =
|
job.action =
|
||||||
@@ -167,8 +162,6 @@ class CmFileModel {
|
|||||||
try {
|
try {
|
||||||
dynamic d = jsonDecode(log);
|
dynamic d = jsonDecode(log);
|
||||||
FileActionLog data = FileActionLog.fromJson(d);
|
FileActionLog data = FileActionLog.fromJson(d);
|
||||||
Client? client =
|
|
||||||
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId);
|
|
||||||
var jobTable = _jobTables[data.connId];
|
var jobTable = _jobTables[data.connId];
|
||||||
if (jobTable == null) {
|
if (jobTable == null) {
|
||||||
debugPrint("jobTable should not be null");
|
debugPrint("jobTable should not be null");
|
||||||
@@ -179,17 +172,45 @@ class CmFileModel {
|
|||||||
..fileName = data.path
|
..fileName = data.path
|
||||||
..action = CmFileAction.createDir
|
..action = CmFileAction.createDir
|
||||||
..state = JobState.done);
|
..state = JobState.done);
|
||||||
final currentSelectedTab =
|
_addUnread(data.connId);
|
||||||
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
|
||||||
if (!(gFFI.chatModel.isShowCMSidePage &&
|
|
||||||
currentSelectedTab.key == data.connId.toString())) {
|
|
||||||
client?.unreadChatMessageCount.value += 1;
|
|
||||||
}
|
|
||||||
jobTable.refresh();
|
jobTable.refresh();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('$e');
|
debugPrint('$e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onRename(dynamic log) {
|
||||||
|
try {
|
||||||
|
dynamic d = jsonDecode(log);
|
||||||
|
FileRenamenLog data = FileRenamenLog.fromJson(d);
|
||||||
|
var jobTable = _jobTables[data.connId];
|
||||||
|
if (jobTable == null) {
|
||||||
|
debugPrint("jobTable should not be null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final fileName = '${data.path} -> ${data.newName}';
|
||||||
|
jobTable.add(CmFileLog()
|
||||||
|
..id = 0
|
||||||
|
..fileName = fileName
|
||||||
|
..action = CmFileAction.rename
|
||||||
|
..state = JobState.done);
|
||||||
|
_addUnread(data.connId);
|
||||||
|
jobTable.refresh();
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('$e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_addUnread(int connId) {
|
||||||
|
Client? client =
|
||||||
|
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == connId);
|
||||||
|
final currentSelectedTab =
|
||||||
|
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
||||||
|
if (!(gFFI.chatModel.isShowCMSidePage &&
|
||||||
|
currentSelectedTab.key == connId.toString())) {
|
||||||
|
client?.unreadChatMessageCount.value += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CmFileAction {
|
enum CmFileAction {
|
||||||
@@ -198,6 +219,7 @@ enum CmFileAction {
|
|||||||
localToRemote,
|
localToRemote,
|
||||||
remove,
|
remove,
|
||||||
createDir,
|
createDir,
|
||||||
|
rename,
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmFileLog {
|
class CmFileLog {
|
||||||
@@ -285,3 +307,22 @@ class FileActionLog {
|
|||||||
dir: d['dir'] ?? false,
|
dir: d['dir'] ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FileRenamenLog {
|
||||||
|
int connId = 0;
|
||||||
|
String path = '';
|
||||||
|
String newName = '';
|
||||||
|
|
||||||
|
FileRenamenLog({
|
||||||
|
required this.connId,
|
||||||
|
required this.path,
|
||||||
|
required this.newName,
|
||||||
|
});
|
||||||
|
|
||||||
|
FileRenamenLog.fromJson(dynamic d)
|
||||||
|
: this(
|
||||||
|
connId: d['connId'] ?? 0,
|
||||||
|
path: d['path'] ?? '',
|
||||||
|
newName: d['newName'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
|
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||||
import 'package:flutter_hbb/utils/event_loop.dart';
|
import 'package:flutter_hbb/utils/event_loop.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
@@ -642,6 +643,77 @@ class FileController {
|
|||||||
path: path,
|
path: path,
|
||||||
isRemote: !isLocal);
|
isRemote: !isLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> renameAction(Entry item, bool isLocal) async {
|
||||||
|
final textEditingController = TextEditingController(text: item.name);
|
||||||
|
String? errorText;
|
||||||
|
dialogManager?.show((setState, close, context) {
|
||||||
|
textEditingController.addListener(() {
|
||||||
|
if (errorText != null) {
|
||||||
|
setState(() {
|
||||||
|
errorText = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
submit() async {
|
||||||
|
final newName = textEditingController.text;
|
||||||
|
if (newName.isEmpty || newName == item.name) {
|
||||||
|
close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (directory.value.entries.any((e) => e.name == newName)) {
|
||||||
|
setState(() {
|
||||||
|
errorText = translate("Already exists");
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!PathUtil.validName(newName, options.value.isWindows)) {
|
||||||
|
setState(() {
|
||||||
|
if (item.isDirectory) {
|
||||||
|
errorText = translate("Invalid folder name");
|
||||||
|
} else {
|
||||||
|
errorText = translate("Invalid file name");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await bind.sessionRenameFile(
|
||||||
|
sessionId: sessionId,
|
||||||
|
actId: JobController.jobID.next(),
|
||||||
|
path: item.path,
|
||||||
|
newName: newName,
|
||||||
|
isRemote: !isLocal);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomAlertDialog(
|
||||||
|
content: Column(
|
||||||
|
children: [
|
||||||
|
DialogTextField(
|
||||||
|
title: '${translate('Rename')} ${item.name}',
|
||||||
|
controller: textEditingController,
|
||||||
|
errorText: errorText,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
dialogButton(
|
||||||
|
"Cancel",
|
||||||
|
icon: Icon(Icons.close_rounded),
|
||||||
|
onPressed: close,
|
||||||
|
isOutline: true,
|
||||||
|
),
|
||||||
|
dialogButton(
|
||||||
|
"OK",
|
||||||
|
icon: Icon(Icons.done_rounded),
|
||||||
|
onPressed: submit,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onSubmit: submit,
|
||||||
|
onCancel: close,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class JobController {
|
class JobController {
|
||||||
@@ -1083,6 +1155,13 @@ class PathUtil {
|
|||||||
final pathUtil = isWindows ? windowsContext : posixContext;
|
final pathUtil = isWindows ? windowsContext : posixContext;
|
||||||
return pathUtil.dirname(path);
|
return pathUtil.dirname(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool validName(String name, bool isWindows) {
|
||||||
|
final unixFileNamePattern = RegExp(r'^[^/\0]+$');
|
||||||
|
final windowsFileNamePattern = RegExp(r'^[^<>:"/\\|?*]+$');
|
||||||
|
final reg = isWindows ? windowsFileNamePattern : unixFileNamePattern;
|
||||||
|
return reg.hasMatch(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DirectoryOptions {
|
class DirectoryOptions {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class GroupModel {
|
|||||||
Future<void> pull({force = true, quiet = false}) async {
|
Future<void> pull({force = true, quiet = false}) async {
|
||||||
if (bind.isDisableGroupPanel()) return;
|
if (bind.isDisableGroupPanel()) return;
|
||||||
if (!gFFI.userModel.isLogin || groupLoading.value) return;
|
if (!gFFI.userModel.isLogin || groupLoading.value) return;
|
||||||
|
if (gFFI.userModel.networkError.isNotEmpty) return;
|
||||||
if (!force && initialized) return;
|
if (!force && initialized) return;
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
groupLoading.value = true;
|
groupLoading.value = true;
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
} else if (name == 'use_texture_render') {
|
} else if (name == 'use_texture_render') {
|
||||||
_handleUseTextureRender(evt, sessionId, peerId);
|
_handleUseTextureRender(evt, sessionId, peerId);
|
||||||
} else {
|
} else {
|
||||||
debugPrint('Unknown event name: $name');
|
debugPrint('Event is not handled in the fixed branch: $name');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -723,6 +723,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
|
|
||||||
/// Handle the peer info event based on [evt].
|
/// Handle the peer info event based on [evt].
|
||||||
handlePeerInfo(Map<String, dynamic> evt, String peerId, bool isCache) async {
|
handlePeerInfo(Map<String, dynamic> evt, String peerId, bool isCache) async {
|
||||||
|
parent.target?.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted;
|
||||||
|
|
||||||
// This call is to ensuer the keyboard mode is updated depending on the peer version.
|
// This call is to ensuer the keyboard mode is updated depending on the peer version.
|
||||||
parent.target?.inputModel.updateKeyboardMode();
|
parent.target?.inputModel.updateKeyboardMode();
|
||||||
|
|
||||||
@@ -1001,14 +1003,15 @@ class FfiModel with ChangeNotifier {
|
|||||||
// Notify to switch display
|
// Notify to switch display
|
||||||
msgBox(sessionId, 'custom-nook-nocancel-hasclose-info', 'Prompt',
|
msgBox(sessionId, 'custom-nook-nocancel-hasclose-info', 'Prompt',
|
||||||
'display_is_plugged_out_msg', '', parent.target!.dialogManager);
|
'display_is_plugged_out_msg', '', parent.target!.dialogManager);
|
||||||
final newDisplay = pi.primaryDisplay == kInvalidDisplayIndex
|
final isPeerPrimaryDisplayValid =
|
||||||
? 0
|
pi.primaryDisplay == kInvalidDisplayIndex ||
|
||||||
: pi.primaryDisplay;
|
pi.primaryDisplay >= pi.displays.length;
|
||||||
final displays = newDisplay;
|
final newDisplay =
|
||||||
|
isPeerPrimaryDisplayValid ? 0 : pi.primaryDisplay;
|
||||||
bind.sessionSwitchDisplay(
|
bind.sessionSwitchDisplay(
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
value: Int32List.fromList([displays]),
|
value: Int32List.fromList([newDisplay]),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_pi.isSupportMultiUiSession) {
|
if (_pi.isSupportMultiUiSession) {
|
||||||
@@ -1173,26 +1176,28 @@ class ImageModel with ChangeNotifier {
|
|||||||
|
|
||||||
addCallbackOnFirstImage(Function(String) cb) => callbacksOnFirstImage.add(cb);
|
addCallbackOnFirstImage(Function(String) cb) => callbacksOnFirstImage.add(cb);
|
||||||
|
|
||||||
onRgba(int display, Uint8List rgba) {
|
clearImage() => _image = null;
|
||||||
|
|
||||||
|
onRgba(int display, Uint8List rgba) async {
|
||||||
|
try {
|
||||||
|
await decodeAndUpdate(display, rgba);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('onRgba error: $e');
|
||||||
|
}
|
||||||
|
platformFFI.nextRgba(sessionId, display);
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeAndUpdate(int display, Uint8List rgba) async {
|
||||||
final pid = parent.target?.id;
|
final pid = parent.target?.id;
|
||||||
final rect = parent.target?.ffiModel.pi.getDisplayRect(display);
|
final rect = parent.target?.ffiModel.pi.getDisplayRect(display);
|
||||||
img.decodeImageFromPixels(
|
final image = await img.decodeImageFromPixels(
|
||||||
rgba,
|
rgba,
|
||||||
rect?.width.toInt() ?? 0,
|
rect?.width.toInt() ?? 0,
|
||||||
rect?.height.toInt() ?? 0,
|
rect?.height.toInt() ?? 0,
|
||||||
isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888,
|
isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888,
|
||||||
onPixelsCopied: () {
|
);
|
||||||
// Unlock the rgba memory from rust codes.
|
if (parent.target?.id != pid) return;
|
||||||
platformFFI.nextRgba(sessionId, display);
|
await update(image);
|
||||||
}).then((image) {
|
|
||||||
if (parent.target?.id != pid) return;
|
|
||||||
try {
|
|
||||||
// my throw exception, because the listener maybe already dispose
|
|
||||||
update(image);
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('update image: $e');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(ui.Image? image) async {
|
update(ui.Image? image) async {
|
||||||
@@ -2547,32 +2552,30 @@ class FFI {
|
|||||||
}
|
}
|
||||||
} else if (message is EventToUI_Rgba) {
|
} else if (message is EventToUI_Rgba) {
|
||||||
final display = message.field0;
|
final display = message.field0;
|
||||||
if (imageModel.useTextureRender || ffiModel.pi.forceTextureRender) {
|
// Fetch the image buffer from rust codes.
|
||||||
//debugPrint("EventToUI_Rgba display:$display");
|
final sz = platformFFI.getRgbaSize(sessionId, display);
|
||||||
textureModel.setTextureType(display: display, gpuTexture: false);
|
if (sz == 0) {
|
||||||
|
platformFFI.nextRgba(sessionId, display);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final rgba = platformFFI.getRgba(sessionId, display, sz);
|
||||||
|
if (rgba != null) {
|
||||||
onEvent2UIRgba();
|
onEvent2UIRgba();
|
||||||
|
await imageModel.onRgba(display, rgba);
|
||||||
} else {
|
} else {
|
||||||
// Fetch the image buffer from rust codes.
|
platformFFI.nextRgba(sessionId, display);
|
||||||
final sz = platformFFI.getRgbaSize(sessionId, display);
|
|
||||||
if (sz == 0) {
|
|
||||||
platformFFI.nextRgba(sessionId, display);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final rgba = platformFFI.getRgba(sessionId, display, sz);
|
|
||||||
if (rgba != null) {
|
|
||||||
onEvent2UIRgba();
|
|
||||||
imageModel.onRgba(display, rgba);
|
|
||||||
} else {
|
|
||||||
platformFFI.nextRgba(sessionId, display);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (message is EventToUI_Texture) {
|
} else if (message is EventToUI_Texture) {
|
||||||
final display = message.field0;
|
final display = message.field0;
|
||||||
debugPrint("EventToUI_Texture display:$display");
|
final gpuTexture = message.field1;
|
||||||
if (hasGpuTextureRender) {
|
debugPrint(
|
||||||
textureModel.setTextureType(display: display, gpuTexture: true);
|
"EventToUI_Texture display:$display, gpuTexture:$gpuTexture");
|
||||||
onEvent2UIRgba();
|
if (gpuTexture && !hasGpuTextureRender) {
|
||||||
|
debugPrint('the gpuTexture is not supported.');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
textureModel.setTextureType(display: display, gpuTexture: gpuTexture);
|
||||||
|
onEvent2UIRgba();
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
});
|
});
|
||||||
@@ -2608,8 +2611,9 @@ class FFI {
|
|||||||
remember: remember);
|
remember: remember);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send2FA(SessionID sessionId, String code) {
|
void send2FA(SessionID sessionId, String code, bool trustThisDevice) {
|
||||||
bind.sessionSend2Fa(sessionId: sessionId, code: code);
|
bind.sessionSend2Fa(
|
||||||
|
sessionId: sessionId, code: code, trustThisDevice: trustThisDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close the remote session.
|
/// Close the remote session.
|
||||||
@@ -2626,7 +2630,7 @@ class FFI {
|
|||||||
canvasModel.scale,
|
canvasModel.scale,
|
||||||
ffiModel.pi.currentDisplay);
|
ffiModel.pi.currentDisplay);
|
||||||
}
|
}
|
||||||
imageModel.update(null);
|
await imageModel.update(null);
|
||||||
cursorModel.clear();
|
cursorModel.clear();
|
||||||
ffiModel.clear();
|
ffiModel.clear();
|
||||||
canvasModel.clear();
|
canvasModel.clear();
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
bind.mainSetOption(key: kOptionEnableAudio, value: "N");
|
bind.mainSetOption(key: kOptionEnableAudio, value: "N");
|
||||||
} else {
|
} else {
|
||||||
final audioOption = await bind.mainGetOption(key: kOptionEnableAudio);
|
final audioOption = await bind.mainGetOption(key: kOptionEnableAudio);
|
||||||
_audioOk = audioOption.isEmpty;
|
_audioOk = audioOption != 'N';
|
||||||
}
|
}
|
||||||
|
|
||||||
// file
|
// file
|
||||||
@@ -206,7 +206,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
} else {
|
} else {
|
||||||
final fileOption =
|
final fileOption =
|
||||||
await bind.mainGetOption(key: kOptionEnableFileTransfer);
|
await bind.mainGetOption(key: kOptionEnableFileTransfer);
|
||||||
_fileOk = fileOption.isEmpty;
|
_fileOk = fileOption != 'N';
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class StateGlobal {
|
|||||||
bool _isMinimized = false;
|
bool _isMinimized = false;
|
||||||
final RxBool isMaximized = false.obs;
|
final RxBool isMaximized = false.obs;
|
||||||
final RxBool _showTabBar = true.obs;
|
final RxBool _showTabBar = true.obs;
|
||||||
final RxDouble _resizeEdgeSize = RxDouble(windowEdgeSize);
|
final RxDouble _resizeEdgeSize = RxDouble(windowResizeEdgeSize);
|
||||||
final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth);
|
final RxDouble _windowBorderWidth = RxDouble(kWindowBorderWidth);
|
||||||
final RxBool showRemoteToolBar = false.obs;
|
final RxBool showRemoteToolBar = false.obs;
|
||||||
final svcStatus = SvcStatus.notReady.obs;
|
final svcStatus = SvcStatus.notReady.obs;
|
||||||
@@ -93,7 +93,7 @@ class StateGlobal {
|
|||||||
? kFullScreenEdgeSize
|
? kFullScreenEdgeSize
|
||||||
: isMaximized.isTrue
|
: isMaximized.isTrue
|
||||||
? kMaximizeEdgeSize
|
? kMaximizeEdgeSize
|
||||||
: windowEdgeSize;
|
: windowResizeEdgeSize;
|
||||||
|
|
||||||
String getInputSource({bool force = false}) {
|
String getInputSource({bool force = false}) {
|
||||||
if (force || _inputSource.isEmpty) {
|
if (force || _inputSource.isEmpty) {
|
||||||
|
|||||||
@@ -17,13 +17,23 @@ bool refreshingUser = false;
|
|||||||
class UserModel {
|
class UserModel {
|
||||||
final RxString userName = ''.obs;
|
final RxString userName = ''.obs;
|
||||||
final RxBool isAdmin = false.obs;
|
final RxBool isAdmin = false.obs;
|
||||||
|
final RxString networkError = ''.obs;
|
||||||
bool get isLogin => userName.isNotEmpty;
|
bool get isLogin => userName.isNotEmpty;
|
||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
|
|
||||||
UserModel(this.parent);
|
UserModel(this.parent) {
|
||||||
|
userName.listen((p0) {
|
||||||
|
// When user name becomes empty, show login button
|
||||||
|
// When user name becomes non-empty:
|
||||||
|
// For _updateLocalUserInfo, network error will be set later
|
||||||
|
// For login success, should clear network error
|
||||||
|
networkError.value = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void refreshCurrentUser() async {
|
void refreshCurrentUser() async {
|
||||||
if (bind.isDisableAccount()) return;
|
if (bind.isDisableAccount()) return;
|
||||||
|
networkError.value = '';
|
||||||
final token = bind.mainGetLocalOption(key: 'access_token');
|
final token = bind.mainGetLocalOption(key: 'access_token');
|
||||||
if (token == '') {
|
if (token == '') {
|
||||||
await updateOtherModels();
|
await updateOtherModels();
|
||||||
@@ -38,12 +48,18 @@ class UserModel {
|
|||||||
if (refreshingUser) return;
|
if (refreshingUser) return;
|
||||||
try {
|
try {
|
||||||
refreshingUser = true;
|
refreshingUser = true;
|
||||||
final response = await http.post(Uri.parse('$url/api/currentUser'),
|
final http.Response response;
|
||||||
headers: {
|
try {
|
||||||
'Content-Type': 'application/json',
|
response = await http.post(Uri.parse('$url/api/currentUser'),
|
||||||
'Authorization': 'Bearer $token'
|
headers: {
|
||||||
},
|
'Content-Type': 'application/json',
|
||||||
body: json.encode(body));
|
'Authorization': 'Bearer $token'
|
||||||
|
},
|
||||||
|
body: json.encode(body));
|
||||||
|
} catch (e) {
|
||||||
|
networkError.value = e.toString();
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
refreshingUser = false;
|
refreshingUser = false;
|
||||||
final status = response.statusCode;
|
final status = response.statusCode;
|
||||||
if (status == 401 || status == 400) {
|
if (status == 401 || status == 400) {
|
||||||
|
|||||||
@@ -13,14 +13,12 @@ Future<ui.Image?> decodeImageFromPixels(
|
|||||||
int? rowBytes,
|
int? rowBytes,
|
||||||
int? targetWidth,
|
int? targetWidth,
|
||||||
int? targetHeight,
|
int? targetHeight,
|
||||||
VoidCallback? onPixelsCopied, // must ensure onPixelsCopied is called no matter this function succeeds
|
|
||||||
bool allowUpscaling = true,
|
bool allowUpscaling = true,
|
||||||
}) async {
|
}) async {
|
||||||
if (targetWidth != null) {
|
if (targetWidth != null) {
|
||||||
assert(allowUpscaling || targetWidth <= width);
|
assert(allowUpscaling || targetWidth <= width);
|
||||||
if (!(allowUpscaling || targetWidth <= width)) {
|
if (!(allowUpscaling || targetWidth <= width)) {
|
||||||
print("not allow upscaling but targetWidth > width");
|
print("not allow upscaling but targetWidth > width");
|
||||||
onPixelsCopied?.call();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +26,6 @@ Future<ui.Image?> decodeImageFromPixels(
|
|||||||
assert(allowUpscaling || targetHeight <= height);
|
assert(allowUpscaling || targetHeight <= height);
|
||||||
if (!(allowUpscaling || targetHeight <= height)) {
|
if (!(allowUpscaling || targetHeight <= height)) {
|
||||||
print("not allow upscaling but targetHeight > height");
|
print("not allow upscaling but targetHeight > height");
|
||||||
onPixelsCopied?.call();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,9 +33,7 @@ Future<ui.Image?> decodeImageFromPixels(
|
|||||||
final ui.ImmutableBuffer buffer;
|
final ui.ImmutableBuffer buffer;
|
||||||
try {
|
try {
|
||||||
buffer = await ui.ImmutableBuffer.fromUint8List(pixels);
|
buffer = await ui.ImmutableBuffer.fromUint8List(pixels);
|
||||||
onPixelsCopied?.call();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
onPixelsCopied?.call();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,10 @@ class RustdeskImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sessionSend2Fa(
|
Future<void> sessionSend2Fa(
|
||||||
{required UuidValue sessionId, required String code, dynamic hint}) {
|
{required UuidValue sessionId,
|
||||||
|
required String code,
|
||||||
|
required bool trustThisDevice,
|
||||||
|
dynamic hint}) {
|
||||||
return Future(() => js.context.callMethod('setByName', ['send_2fa', code]));
|
return Future(() => js.context.callMethod('setByName', ['send_2fa', code]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1412,7 +1415,7 @@ class RustdeskImpl {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mainHideDocker({dynamic hint}) {
|
bool mainHideDock({dynamic hint}) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1622,5 +1625,30 @@ class RustdeskImpl {
|
|||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String mainGetUnlockPin({dynamic hint}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
String mainSetUnlockPin({required String pin, dynamic hint}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sessionGetEnableTrustedDevices(
|
||||||
|
{required UuidValue sessionId, dynamic hint}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> mainGetTrustedDevices({dynamic hint}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> mainRemoveTrustedDevices({required String json, dynamic hint}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> mainClearTrustedDevices({dynamic hint}) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
void dispose() {}
|
void dispose() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,5 +45,7 @@
|
|||||||
<string>Record the sound from microphone for the purpose of the remote desktop.</string>
|
<string>Record the sound from microphone for the purpose of the remote desktop.</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
|
<key>LSUIElement</key>
|
||||||
|
<string>1</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
2
flutter/ndk_x86.sh
Executable file
2
flutter/ndk_x86.sh
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
cargo ndk --platform 21 --target i686-linux-android build --release --features flutter
|
||||||
@@ -335,7 +335,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: HEAD
|
ref: HEAD
|
||||||
resolved-ref: "336308d86ec8b9640504a371b50ba500eb779363"
|
resolved-ref: "80b063b9d4e015f62e17f42a5aa0b3d20a365926"
|
||||||
url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window"
|
url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window"
|
||||||
source: git
|
source: git
|
||||||
version: "0.1.0"
|
version: "0.1.0"
|
||||||
@@ -377,7 +377,7 @@ packages:
|
|||||||
path: "."
|
path: "."
|
||||||
ref: "24cb88413fa5181d949ddacbb30a65d5c459e7d9"
|
ref: "24cb88413fa5181d949ddacbb30a65d5c459e7d9"
|
||||||
resolved-ref: "24cb88413fa5181d949ddacbb30a65d5c459e7d9"
|
resolved-ref: "24cb88413fa5181d949ddacbb30a65d5c459e7d9"
|
||||||
url: "https://github.com/21pages/dynamic_layouts.git"
|
url: "https://github.com/rustdesk-org/dynamic_layouts.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1+1"
|
version: "0.0.1+1"
|
||||||
external_path:
|
external_path:
|
||||||
@@ -511,7 +511,7 @@ packages:
|
|||||||
path: "."
|
path: "."
|
||||||
ref: "38951317afe79d953ab25733667bd96e172a80d3"
|
ref: "38951317afe79d953ab25733667bd96e172a80d3"
|
||||||
resolved-ref: "38951317afe79d953ab25733667bd96e172a80d3"
|
resolved-ref: "38951317afe79d953ab25733667bd96e172a80d3"
|
||||||
url: "https://github.com/21pages/flutter_gpu_texture_renderer"
|
url: "https://github.com/rustdesk-org/flutter_gpu_texture_renderer"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
flutter_improved_scrolling:
|
flutter_improved_scrolling:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers
|
# 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers
|
||||||
version: 1.2.7+46
|
version: 1.3.0+46
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '^3.1.0'
|
sdk: '^3.1.0'
|
||||||
@@ -92,14 +92,14 @@ dependencies:
|
|||||||
dropdown_button2: ^2.0.0
|
dropdown_button2: ^2.0.0
|
||||||
flutter_gpu_texture_renderer:
|
flutter_gpu_texture_renderer:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/21pages/flutter_gpu_texture_renderer
|
url: https://github.com/rustdesk-org/flutter_gpu_texture_renderer
|
||||||
ref: 38951317afe79d953ab25733667bd96e172a80d3
|
ref: 38951317afe79d953ab25733667bd96e172a80d3
|
||||||
uuid: ^3.0.7
|
uuid: ^3.0.7
|
||||||
auto_size_text_field: ^2.2.1
|
auto_size_text_field: ^2.2.1
|
||||||
flex_color_picker: ^3.3.0
|
flex_color_picker: ^3.3.0
|
||||||
dynamic_layouts:
|
dynamic_layouts:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/21pages/dynamic_layouts.git
|
url: https://github.com/rustdesk-org/dynamic_layouts.git
|
||||||
ref: 24cb88413fa5181d949ddacbb30a65d5c459e7d9
|
ref: 24cb88413fa5181d949ddacbb30a65d5c459e7d9
|
||||||
pull_down_button: ^0.9.3
|
pull_down_button: ^0.9.3
|
||||||
device_info_plus: ^9.1.0
|
device_info_plus: ^9.1.0
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 460 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 481 KiB |
@@ -1,3 +1,4 @@
|
|||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
use crate::{CliprdrError, CliprdrServiceContext};
|
use crate::{CliprdrError, CliprdrServiceContext};
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -63,8 +64,10 @@ pub fn create_cliprdr_context(
|
|||||||
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
|
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
struct DummyCliprdrContext {}
|
struct DummyCliprdrContext {}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
impl CliprdrServiceContext for DummyCliprdrContext {
|
impl CliprdrServiceContext for DummyCliprdrContext {
|
||||||
fn set_is_stopped(&mut self) -> Result<(), CliprdrError> {
|
fn set_is_stopped(&mut self) -> Result<(), CliprdrError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ serde = { version = "1.0", optional = true }
|
|||||||
serde_derive = { version = "1.0", optional = true }
|
serde_derive = { version = "1.0", optional = true }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
rdev = { git = "https://github.com/rustdesk-org/rdev" }
|
rdev = { git = "https://github.com/rustdesk-org/rdev" }
|
||||||
tfc = { git = "https://github.com/rustdesk-org/The-Fat-Controller" }
|
tfc = { git = "https://github.com/rustdesk-org/The-Fat-Controller", branch = "history/rebase_upstream_20240722" }
|
||||||
hbb_common = { path = "../hbb_common" }
|
hbb_common = { path = "../hbb_common" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ libc = "0.2"
|
|||||||
dlopen = "0.1"
|
dlopen = "0.1"
|
||||||
toml = "0.7"
|
toml = "0.7"
|
||||||
uuid = { version = "1.3", features = ["v4"] }
|
uuid = { version = "1.3", features = ["v4"] }
|
||||||
# crash, versions >= 0.29.1 are affected by #GuillaumeGomez/sysinfo/1052
|
# new sysinfo issue: https://github.com/rustdesk/rustdesk/pull/6330#issuecomment-2270871442
|
||||||
sysinfo = { git = "https://github.com/rustdesk-org/sysinfo" }
|
sysinfo = { git = "https://github.com/rustdesk-org/sysinfo", branch = "rlim_max" }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
httparse = "1.5"
|
httparse = "1.5"
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
@@ -46,7 +46,7 @@ url = "2.2"
|
|||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||||
mac_address = "1.1"
|
mac_address = "1.1"
|
||||||
machine-uid = { git = "https://github.com/21pages/machine-uid" }
|
machine-uid = { git = "https://github.com/rustdesk-org/machine-uid" }
|
||||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies]
|
[target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies]
|
||||||
tokio-rustls = { version = "0.26", features = ["logging", "tls12", "ring"], default-features = false }
|
tokio-rustls = { version = "0.26", features = ["logging", "tls12", "ring"], default-features = false }
|
||||||
rustls-platform-verifier = "0.3.1"
|
rustls-platform-verifier = "0.3.1"
|
||||||
|
|||||||
@@ -81,10 +81,13 @@ message LoginRequest {
|
|||||||
uint64 session_id = 10;
|
uint64 session_id = 10;
|
||||||
string version = 11;
|
string version = 11;
|
||||||
OSLogin os_login = 12;
|
OSLogin os_login = 12;
|
||||||
|
string my_platform = 13;
|
||||||
|
bytes hwid = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Auth2FA {
|
message Auth2FA {
|
||||||
string code = 1;
|
string code = 1;
|
||||||
|
bytes hwid = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ChatMessage { string text = 1; }
|
message ChatMessage { string text = 1; }
|
||||||
@@ -136,6 +139,7 @@ message LoginResponse {
|
|||||||
string error = 1;
|
string error = 1;
|
||||||
PeerInfo peer_info = 2;
|
PeerInfo peer_info = 2;
|
||||||
}
|
}
|
||||||
|
bool enable_trusted_devices = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TouchScaleUpdate {
|
message TouchScaleUpdate {
|
||||||
@@ -315,13 +319,25 @@ message Hash {
|
|||||||
string challenge = 2;
|
string challenge = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ClipboardFormat {
|
||||||
|
Text = 0;
|
||||||
|
Rtf = 1;
|
||||||
|
Html = 2;
|
||||||
|
ImageRgba = 21;
|
||||||
|
ImagePng = 22;
|
||||||
|
ImageSvg = 23;
|
||||||
|
}
|
||||||
|
|
||||||
message Clipboard {
|
message Clipboard {
|
||||||
bool compress = 1;
|
bool compress = 1;
|
||||||
bytes content = 2;
|
bytes content = 2;
|
||||||
int32 width = 3;
|
int32 width = 3;
|
||||||
int32 height = 4;
|
int32 height = 4;
|
||||||
|
ClipboardFormat format = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message MultiClipboards { repeated Clipboard clipboards = 1; }
|
||||||
|
|
||||||
enum FileType {
|
enum FileType {
|
||||||
Dir = 0;
|
Dir = 0;
|
||||||
DirLink = 2;
|
DirLink = 2;
|
||||||
@@ -355,6 +371,12 @@ message ReadAllFiles {
|
|||||||
bool include_hidden = 3;
|
bool include_hidden = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message FileRename {
|
||||||
|
int32 id = 1;
|
||||||
|
string path = 2;
|
||||||
|
string new_name = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message FileAction {
|
message FileAction {
|
||||||
oneof union {
|
oneof union {
|
||||||
ReadDir read_dir = 1;
|
ReadDir read_dir = 1;
|
||||||
@@ -366,6 +388,7 @@ message FileAction {
|
|||||||
ReadAllFiles all_files = 7;
|
ReadAllFiles all_files = 7;
|
||||||
FileTransferCancel cancel = 8;
|
FileTransferCancel cancel = 8;
|
||||||
FileTransferSendConfirmRequest send_confirm = 9;
|
FileTransferSendConfirmRequest send_confirm = 9;
|
||||||
|
FileRename rename = 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,5 +839,6 @@ message Message {
|
|||||||
PeerInfo peer_info = 25;
|
PeerInfo peer_info = 25;
|
||||||
PointerDeviceEvent pointer_device_event = 26;
|
PointerDeviceEvent pointer_device_event = 26;
|
||||||
Auth2FA auth_2fa = 27;
|
Auth2FA auth_2fa = 27;
|
||||||
|
MultiClipboards multi_clipboards = 28;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use bytes::Bytes;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde as de;
|
use serde as de;
|
||||||
@@ -52,6 +53,7 @@ lazy_static::lazy_static! {
|
|||||||
static ref CONFIG: RwLock<Config> = RwLock::new(Config::load());
|
static ref CONFIG: RwLock<Config> = RwLock::new(Config::load());
|
||||||
static ref CONFIG2: RwLock<Config2> = RwLock::new(Config2::load());
|
static ref CONFIG2: RwLock<Config2> = RwLock::new(Config2::load());
|
||||||
static ref LOCAL_CONFIG: RwLock<LocalConfig> = RwLock::new(LocalConfig::load());
|
static ref LOCAL_CONFIG: RwLock<LocalConfig> = RwLock::new(LocalConfig::load());
|
||||||
|
static ref TRUSTED_DEVICES: RwLock<(Vec<TrustedDevice>, bool)> = Default::default();
|
||||||
static ref ONLINE: Mutex<HashMap<String, i64>> = Default::default();
|
static ref ONLINE: Mutex<HashMap<String, i64>> = Default::default();
|
||||||
pub static ref PROD_RENDEZVOUS_SERVER: RwLock<String> = RwLock::new(match option_env!("RENDEZVOUS_SERVER") {
|
pub static ref PROD_RENDEZVOUS_SERVER: RwLock<String> = RwLock::new(match option_env!("RENDEZVOUS_SERVER") {
|
||||||
Some(key) if !key.is_empty() => key,
|
Some(key) if !key.is_empty() => key,
|
||||||
@@ -69,7 +71,7 @@ lazy_static::lazy_static! {
|
|||||||
pub static ref DEFAULT_LOCAL_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
|
pub static ref DEFAULT_LOCAL_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
|
||||||
pub static ref OVERWRITE_LOCAL_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
|
pub static ref OVERWRITE_LOCAL_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
|
||||||
pub static ref HARD_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
|
pub static ref HARD_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
|
||||||
pub static ref BUILDIN_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
|
pub static ref BUILTIN_SETTINGS: RwLock<HashMap<String, String>> = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@@ -208,6 +210,10 @@ pub struct Config2 {
|
|||||||
nat_type: i32,
|
nat_type: i32,
|
||||||
#[serde(default, deserialize_with = "deserialize_i32")]
|
#[serde(default, deserialize_with = "deserialize_i32")]
|
||||||
serial: i32,
|
serial: i32,
|
||||||
|
#[serde(default, deserialize_with = "deserialize_string")]
|
||||||
|
unlock_pin: String,
|
||||||
|
#[serde(default, deserialize_with = "deserialize_string")]
|
||||||
|
trusted_devices: String,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
socks: Option<Socks5Server>,
|
socks: Option<Socks5Server>,
|
||||||
@@ -427,14 +433,20 @@ fn patch(path: PathBuf) -> PathBuf {
|
|||||||
impl Config2 {
|
impl Config2 {
|
||||||
fn load() -> Config2 {
|
fn load() -> Config2 {
|
||||||
let mut config = Config::load_::<Config2>("2");
|
let mut config = Config::load_::<Config2>("2");
|
||||||
|
let mut store = false;
|
||||||
if let Some(mut socks) = config.socks {
|
if let Some(mut socks) = config.socks {
|
||||||
let (password, _, store) =
|
let (password, _, store2) =
|
||||||
decrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION);
|
decrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION);
|
||||||
socks.password = password;
|
socks.password = password;
|
||||||
config.socks = Some(socks);
|
config.socks = Some(socks);
|
||||||
if store {
|
store |= store2;
|
||||||
config.store();
|
}
|
||||||
}
|
let (unlock_pin, _, store2) =
|
||||||
|
decrypt_str_or_original(&config.unlock_pin, PASSWORD_ENC_VERSION);
|
||||||
|
config.unlock_pin = unlock_pin;
|
||||||
|
store |= store2;
|
||||||
|
if store {
|
||||||
|
config.store();
|
||||||
}
|
}
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
@@ -450,6 +462,8 @@ impl Config2 {
|
|||||||
encrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
|
encrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
|
||||||
config.socks = Some(socks);
|
config.socks = Some(socks);
|
||||||
}
|
}
|
||||||
|
config.unlock_pin =
|
||||||
|
encrypt_str_or_original(&config.unlock_pin, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
|
||||||
Config::store_(&config, "2");
|
Config::store_(&config, "2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,6 +1002,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
config.password = password.into();
|
config.password = password.into();
|
||||||
config.store();
|
config.store();
|
||||||
|
Self::clear_trusted_devices();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_permanent_password() -> String {
|
pub fn get_permanent_password() -> String {
|
||||||
@@ -1081,6 +1096,77 @@ impl Config {
|
|||||||
NetworkType::Direct
|
NetworkType::Direct
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_unlock_pin() -> String {
|
||||||
|
CONFIG2.read().unwrap().unlock_pin.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_unlock_pin(pin: &str) {
|
||||||
|
let mut config = CONFIG2.write().unwrap();
|
||||||
|
if pin == config.unlock_pin {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config.unlock_pin = pin.to_string();
|
||||||
|
config.store();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_trusted_devices_json() -> String {
|
||||||
|
serde_json::to_string(&Self::get_trusted_devices()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_trusted_devices() -> Vec<TrustedDevice> {
|
||||||
|
let (devices, synced) = TRUSTED_DEVICES.read().unwrap().clone();
|
||||||
|
if synced {
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
let devices = CONFIG2.read().unwrap().trusted_devices.clone();
|
||||||
|
let (devices, succ, store) = decrypt_str_or_original(&devices, PASSWORD_ENC_VERSION);
|
||||||
|
if succ {
|
||||||
|
let mut devices: Vec<TrustedDevice> =
|
||||||
|
serde_json::from_str(&devices).unwrap_or_default();
|
||||||
|
let len = devices.len();
|
||||||
|
devices.retain(|d| !d.outdate());
|
||||||
|
if store || devices.len() != len {
|
||||||
|
Self::set_trusted_devices(devices.clone());
|
||||||
|
}
|
||||||
|
*TRUSTED_DEVICES.write().unwrap() = (devices.clone(), true);
|
||||||
|
devices
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_trusted_devices(mut trusted_devices: Vec<TrustedDevice>) {
|
||||||
|
trusted_devices.retain(|d| !d.outdate());
|
||||||
|
let devices = serde_json::to_string(&trusted_devices).unwrap_or_default();
|
||||||
|
let max_len = 1024 * 1024;
|
||||||
|
if devices.bytes().len() > max_len {
|
||||||
|
log::error!("Trusted devices too large: {}", devices.bytes().len());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let devices = encrypt_str_or_original(&devices, PASSWORD_ENC_VERSION, max_len);
|
||||||
|
let mut config = CONFIG2.write().unwrap();
|
||||||
|
config.trusted_devices = devices;
|
||||||
|
config.store();
|
||||||
|
*TRUSTED_DEVICES.write().unwrap() = (trusted_devices, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_trusted_device(device: TrustedDevice) {
|
||||||
|
let mut devices = Self::get_trusted_devices();
|
||||||
|
devices.retain(|d| d.hwid != device.hwid);
|
||||||
|
devices.push(device);
|
||||||
|
Self::set_trusted_devices(devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_trusted_devices(hwids: &Vec<Bytes>) {
|
||||||
|
let mut devices = Self::get_trusted_devices();
|
||||||
|
devices.retain(|d| !hwids.contains(&d.hwid));
|
||||||
|
Self::set_trusted_devices(devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_trusted_devices() {
|
||||||
|
Self::set_trusted_devices(Default::default());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get() -> Config {
|
pub fn get() -> Config {
|
||||||
return CONFIG.read().unwrap().clone();
|
return CONFIG.read().unwrap().clone();
|
||||||
}
|
}
|
||||||
@@ -1911,6 +1997,22 @@ impl Group {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct TrustedDevice {
|
||||||
|
pub hwid: Bytes,
|
||||||
|
pub time: i64,
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub platform: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrustedDevice {
|
||||||
|
pub fn outdate(&self) -> bool {
|
||||||
|
const DAYS_90: i64 = 90 * 24 * 60 * 60 * 1000;
|
||||||
|
self.time + DAYS_90 < crate::get_time()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
deserialize_default!(deserialize_string, String);
|
deserialize_default!(deserialize_string, String);
|
||||||
deserialize_default!(deserialize_bool, bool);
|
deserialize_default!(deserialize_bool, bool);
|
||||||
deserialize_default!(deserialize_i32, i32);
|
deserialize_default!(deserialize_i32, i32);
|
||||||
@@ -2100,6 +2202,7 @@ pub mod keys {
|
|||||||
pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture";
|
pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture";
|
||||||
pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str =
|
pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str =
|
||||||
"enable-android-software-encoding-half-scale";
|
"enable-android-software-encoding-half-scale";
|
||||||
|
pub const OPTION_ENABLE_TRUSTED_DEVICES: &str = "enable-trusted-devices";
|
||||||
|
|
||||||
// buildin options
|
// buildin options
|
||||||
pub const OPTION_DISPLAY_NAME: &str = "display-name";
|
pub const OPTION_DISPLAY_NAME: &str = "display-name";
|
||||||
@@ -2114,6 +2217,7 @@ pub mod keys {
|
|||||||
pub const OPTION_HIDE_USERNAME_ON_CARD: &str = "hide-username-on-card";
|
pub const OPTION_HIDE_USERNAME_ON_CARD: &str = "hide-username-on-card";
|
||||||
pub const OPTION_HIDE_HELP_CARDS: &str = "hide-help-cards";
|
pub const OPTION_HIDE_HELP_CARDS: &str = "hide-help-cards";
|
||||||
pub const OPTION_DEFAULT_CONNECT_PASSWORD: &str = "default-connect-password";
|
pub const OPTION_DEFAULT_CONNECT_PASSWORD: &str = "default-connect-password";
|
||||||
|
pub const OPTION_HIDE_TRAY: &str = "hide-tray";
|
||||||
|
|
||||||
// flutter local options
|
// flutter local options
|
||||||
pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState";
|
pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState";
|
||||||
@@ -2240,6 +2344,7 @@ pub mod keys {
|
|||||||
OPTION_PRESET_ADDRESS_BOOK_TAG,
|
OPTION_PRESET_ADDRESS_BOOK_TAG,
|
||||||
OPTION_ENABLE_DIRECTX_CAPTURE,
|
OPTION_ENABLE_DIRECTX_CAPTURE,
|
||||||
OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE,
|
OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE,
|
||||||
|
OPTION_ENABLE_TRUSTED_DEVICES,
|
||||||
];
|
];
|
||||||
|
|
||||||
// BUILDIN_SETTINGS
|
// BUILDIN_SETTINGS
|
||||||
@@ -2256,6 +2361,7 @@ pub mod keys {
|
|||||||
OPTION_HIDE_USERNAME_ON_CARD,
|
OPTION_HIDE_USERNAME_ON_CARD,
|
||||||
OPTION_HIDE_HELP_CARDS,
|
OPTION_HIDE_HELP_CARDS,
|
||||||
OPTION_DEFAULT_CONNECT_PASSWORD,
|
OPTION_DEFAULT_CONNECT_PASSWORD,
|
||||||
|
OPTION_HIDE_TRAY,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -838,6 +838,21 @@ pub fn create_dir(dir: &str) -> ResultType<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn rename_file(path: &str, new_name: &str) -> ResultType<()> {
|
||||||
|
let path = std::path::Path::new(&path);
|
||||||
|
if path.exists() {
|
||||||
|
let dir = path
|
||||||
|
.parent()
|
||||||
|
.ok_or(anyhow!("Parent directoy of {path:?} not exists"))?;
|
||||||
|
let new_path = dir.join(&new_name);
|
||||||
|
std::fs::rename(&path, &new_path)?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
bail!("{path:?} not exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn transform_windows_path(entries: &mut Vec<FileEntry>) {
|
pub fn transform_windows_path(entries: &mut Vec<FileEntry>) {
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustdesk-portable-packer"
|
name = "rustdesk-portable-packer"
|
||||||
version = "1.2.7"
|
version = "1.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "RustDesk Remote Desktop"
|
description = "RustDesk Remote Desktop"
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ cfg-if = "1.0"
|
|||||||
num_cpus = "1.15"
|
num_cpus = "1.15"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
hbb_common = { path = "../hbb_common" }
|
hbb_common = { path = "../hbb_common" }
|
||||||
webm = { git = "https://github.com/21pages/rust-webm" }
|
webm = { git = "https://github.com/rustdesk-org/rust-webm" }
|
||||||
serde = {version="1.0", features=["derive"]}
|
serde = {version="1.0", features=["derive"]}
|
||||||
|
|
||||||
[dependencies.winapi]
|
[dependencies.winapi]
|
||||||
@@ -59,7 +59,7 @@ gstreamer-app = { version = "0.16", features = ["v1_10"], optional = true }
|
|||||||
gstreamer-video = { version = "0.16", optional = true }
|
gstreamer-video = { version = "0.16", optional = true }
|
||||||
|
|
||||||
[dependencies.hwcodec]
|
[dependencies.hwcodec]
|
||||||
git = "https://github.com/21pages/hwcodec"
|
git = "https://github.com/rustdesk-org/hwcodec"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -188,6 +188,52 @@ fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &s
|
|||||||
generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex);
|
generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If you have problems installing ffmpeg, you can download $VCPKG_ROOT/installed from ci
|
||||||
|
// Linux require link in hwcodec
|
||||||
|
/*
|
||||||
|
fn ffmpeg() {
|
||||||
|
// ffmpeg
|
||||||
|
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||||
|
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||||
|
let static_libs = vec!["avcodec", "avutil", "avformat"];
|
||||||
|
static_libs.iter().for_each(|lib| {
|
||||||
|
find_package(lib);
|
||||||
|
});
|
||||||
|
if target_os == "windows" {
|
||||||
|
println!("cargo:rustc-link-lib=static=libmfx");
|
||||||
|
}
|
||||||
|
|
||||||
|
// os
|
||||||
|
let dyn_libs: Vec<&str> = if target_os == "windows" {
|
||||||
|
["User32", "bcrypt", "ole32", "advapi32"].to_vec()
|
||||||
|
} else if target_os == "linux" {
|
||||||
|
let mut v = ["va", "va-drm", "va-x11", "vdpau", "X11", "stdc++"].to_vec();
|
||||||
|
if target_arch == "x86_64" {
|
||||||
|
v.push("z");
|
||||||
|
}
|
||||||
|
v
|
||||||
|
} else if target_os == "macos" || target_os == "ios" {
|
||||||
|
["c++", "m"].to_vec()
|
||||||
|
} else if target_os == "android" {
|
||||||
|
["z", "m", "android", "atomic"].to_vec()
|
||||||
|
} else {
|
||||||
|
panic!("unsupported os");
|
||||||
|
};
|
||||||
|
dyn_libs
|
||||||
|
.iter()
|
||||||
|
.map(|lib| println!("cargo:rustc-link-lib={}", lib))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
if target_os == "macos" || target_os == "ios" {
|
||||||
|
println!("cargo:rustc-link-lib=framework=CoreFoundation");
|
||||||
|
println!("cargo:rustc-link-lib=framework=CoreVideo");
|
||||||
|
println!("cargo:rustc-link-lib=framework=CoreMedia");
|
||||||
|
println!("cargo:rustc-link-lib=framework=VideoToolbox");
|
||||||
|
println!("cargo:rustc-link-lib=framework=AVFoundation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// note: all link symbol names in x86 (32-bit) are prefixed wth "_".
|
// note: all link symbol names in x86 (32-bit) are prefixed wth "_".
|
||||||
// run "rustup show" to show current default toolchain, if it is stable-x86-pc-windows-msvc,
|
// run "rustup show" to show current default toolchain, if it is stable-x86-pc-windows-msvc,
|
||||||
@@ -204,6 +250,7 @@ fn main() {
|
|||||||
gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*");
|
gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*");
|
||||||
gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*");
|
gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*");
|
||||||
gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*");
|
gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*");
|
||||||
|
// ffmpeg();
|
||||||
|
|
||||||
// there is problem with cfg(target_os) in build.rs, so use our workaround
|
// there is problem with cfg(target_os) in build.rs, so use our workaround
|
||||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||||
|
|||||||
@@ -349,6 +349,10 @@ fn init_ndk_context() -> JniResult<()> {
|
|||||||
jvm.get_java_vm_pointer() as _,
|
jvm.get_java_vm_pointer() as _,
|
||||||
ctx.as_obj().as_raw() as _,
|
ctx.as_obj().as_raw() as _,
|
||||||
);
|
);
|
||||||
|
#[cfg(feature = "hwcodec")]
|
||||||
|
hwcodec::android::ffmpeg_set_java_vm(
|
||||||
|
jvm.get_java_vm_pointer() as _,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
*lock = true;
|
*lock = true;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|||||||
@@ -593,7 +593,7 @@ pub fn request_remote_desktop() -> Result<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(Box::new(DBusError(
|
Err(Box::new(DBusError(
|
||||||
"Failed to obtain screen capture.".into(),
|
"Failed to obtain screen capture. You may need to upgrade the PipeWire library for better compatibility. Please check https://github.com/rustdesk/rustdesk/issues/8600#issuecomment-2254720954 for more details.".into()
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
pkgname=rustdesk
|
pkgname=rustdesk
|
||||||
pkgver=1.2.7
|
pkgver=1.3.0
|
||||||
pkgrel=0
|
pkgrel=0
|
||||||
epoch=
|
epoch=
|
||||||
pkgdesc=""
|
pkgdesc=""
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ def view(
|
|||||||
devices.append(device)
|
devices.append(device)
|
||||||
continue
|
continue
|
||||||
last_online = datetime.strptime(
|
last_online = datetime.strptime(
|
||||||
device["last_online"], "%Y-%m-%dT%H:%M:%S"
|
device["last_online"].split(".")[0], "%Y-%m-%dT%H:%M:%S"
|
||||||
) # assuming date is in this format
|
) # assuming date is in this format
|
||||||
if (datetime.utcnow() - last_online).days >= offline_days:
|
if (datetime.utcnow() - last_online).days >= offline_days:
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
@@ -129,6 +129,8 @@ def main():
|
|||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
while args.url.endswith("/"): args.url = args.url[:-1]
|
||||||
|
|
||||||
devices = view(
|
devices = view(
|
||||||
args.url,
|
args.url,
|
||||||
args.token,
|
args.token,
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ LExit:
|
|||||||
return WcaFinalize(er);
|
return WcaFinalize(er);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CAUTION: We can't simply remove the install folder here, because silent repair/upgrade will fail.
|
||||||
|
// `RemoveInstallFolder()` is a deferred custom action, it will be executed after the files are copied.
|
||||||
|
// `msiexec /i package.msi /qn`
|
||||||
|
//
|
||||||
|
// So we need to delete the files separately in install folder.
|
||||||
UINT __stdcall RemoveInstallFolder(
|
UINT __stdcall RemoveInstallFolder(
|
||||||
__in MSIHANDLE hInstall)
|
__in MSIHANDLE hInstall)
|
||||||
{
|
{
|
||||||
@@ -41,6 +46,7 @@ UINT __stdcall RemoveInstallFolder(
|
|||||||
LPWSTR installFolder = NULL;
|
LPWSTR installFolder = NULL;
|
||||||
LPWSTR pwz = NULL;
|
LPWSTR pwz = NULL;
|
||||||
LPWSTR pwzData = NULL;
|
LPWSTR pwzData = NULL;
|
||||||
|
WCHAR runtimeBroker[1024] = { 0, };
|
||||||
|
|
||||||
hr = WcaInitialize(hInstall, "RemoveInstallFolder");
|
hr = WcaInitialize(hInstall, "RemoveInstallFolder");
|
||||||
ExitOnFailure(hr, "Failed to initialize");
|
ExitOnFailure(hr, "Failed to initialize");
|
||||||
@@ -52,21 +58,22 @@ UINT __stdcall RemoveInstallFolder(
|
|||||||
hr = WcaReadStringFromCaData(&pwz, &installFolder);
|
hr = WcaReadStringFromCaData(&pwz, &installFolder);
|
||||||
ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz);
|
ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz);
|
||||||
|
|
||||||
|
StringCchPrintfW(runtimeBroker, sizeof(runtimeBroker) / sizeof(runtimeBroker[0]), L"%ls\\RuntimeBroker_rustdesk.exe", installFolder);
|
||||||
|
|
||||||
SHFILEOPSTRUCTW fileOp;
|
SHFILEOPSTRUCTW fileOp;
|
||||||
ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT));
|
ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT));
|
||||||
|
|
||||||
fileOp.wFunc = FO_DELETE;
|
fileOp.wFunc = FO_DELETE;
|
||||||
fileOp.pFrom = installFolder;
|
fileOp.pFrom = runtimeBroker;
|
||||||
fileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT;
|
fileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT;
|
||||||
|
|
||||||
nResult = SHFileOperationW(&fileOp);
|
nResult = SHFileOperationW(&fileOp);
|
||||||
if (nResult == 0)
|
if (nResult == 0)
|
||||||
{
|
{
|
||||||
WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has been deleted.", installFolder);
|
WcaLog(LOGMSG_STANDARD, "The external file \"%ls\" has been deleted.", runtimeBroker);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has not been deleted, error code: 0x%02X. Please refer to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa for the error codes.", installFolder, nResult);
|
WcaLog(LOGMSG_STANDARD, "The external file \"%ls\" has not been deleted, error code: 0x%02X. Please refer to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa for the error codes.", runtimeBroker, nResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
LExit:
|
LExit:
|
||||||
|
|||||||
@@ -3,8 +3,26 @@
|
|||||||
<?include ../Includes.wxi?>
|
<?include ../Includes.wxi?>
|
||||||
|
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
<!-- For compatibility with command line values from previous versions -->
|
||||||
|
<Property Id="INSTALLFOLDER" Secure="yes">
|
||||||
|
<RegistrySearch Id="InstallFolderSearch" Root="HKCR" Key="$(var.RegKeyRoot)" Name="INSTALLFOLDER" Type="raw" />
|
||||||
|
</Property>
|
||||||
|
|
||||||
|
<!-- If a property value has been passed via the command line (which includes when set from the bundle), the registry search will
|
||||||
|
overwrite the command line value, these actions temporarily store the command line value before the registry search
|
||||||
|
is performed so they can be restored after the registry search is complete -->
|
||||||
|
<SetProperty Id="SavedInstallFolderCmdLineValue" Value="[INSTALLFOLDER]" Before="AppSearch" Sequence="first" Condition="INSTALLFOLDER" />
|
||||||
|
|
||||||
|
<!-- If a command line value was stored, restore it after the registry search has been performed -->
|
||||||
|
<SetProperty Action="RestoreSavedInstallFolderValue" Id="INSTALLFOLDER" Value="[SavedInstallFolderCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedInstallFolderCmdLineValue" />
|
||||||
|
|
||||||
|
<!-- If a command line value or registry value was set, update the main properties with the value -->
|
||||||
|
<SetProperty Id="INSTALLFOLDER_INNER" Value="[INSTALLFOLDER]" After="RestoreSavedInstallFolderValue" Sequence="first" Condition="INSTALLFOLDER" />
|
||||||
|
|
||||||
|
<!-- INSTALLFOLDER_INNER is defined for compatibility with previous versions of the installer. -->
|
||||||
|
<!-- Because we need to use INSTALLFOLDER as the command line argument. -->
|
||||||
<StandardDirectory Id="ProgramFiles6432Folder">
|
<StandardDirectory Id="ProgramFiles6432Folder">
|
||||||
<Directory Id="INSTALLFOLDER" Name="$(var.Product)" />
|
<Directory Id="INSTALLFOLDER_INNER" Name="$(var.Product)" />
|
||||||
</StandardDirectory>
|
</StandardDirectory>
|
||||||
|
|
||||||
<StandardDirectory Id="CommonAppDataFolder">
|
<StandardDirectory Id="CommonAppDataFolder">
|
||||||
|
|||||||
@@ -5,16 +5,16 @@
|
|||||||
<Fragment>
|
<Fragment>
|
||||||
<!-- Regs for shortcuts are defined in "Fragments/ShortcutProperties.wxs" -->
|
<!-- Regs for shortcuts are defined in "Fragments/ShortcutProperties.wxs" -->
|
||||||
<!-- Component that persists the property values to the registry so they are available during an upgrade/modify -->
|
<!-- Component that persists the property values to the registry so they are available during an upgrade/modify -->
|
||||||
<DirectoryRef Id="INSTALLFOLDER">
|
<DirectoryRef Id="INSTALLFOLDER_INNER">
|
||||||
<Component Id="Product.Registry.InstallDir" Guid="3196EDA7-9AEF-4705-A0C8-E3F3ECCCB153">
|
<Component Id="Product.Registry.InstallFolder" Guid="3196EDA7-9AEF-4705-A0C8-E3F3ECCCB153">
|
||||||
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
||||||
<RegistryValue Type="string" Name="INSTALLFOLDER" Value="[INSTALLFOLDER]" />
|
<RegistryValue Type="string" Name="INSTALLFOLDER" Value="[INSTALLFOLDER_INNER]" />
|
||||||
</RegistryKey>
|
</RegistryKey>
|
||||||
</Component>
|
</Component>
|
||||||
|
|
||||||
<Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49">
|
<Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49">
|
||||||
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\DefaultIcon">
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\DefaultIcon">
|
||||||
<RegistryValue Type="string" Value='"[INSTALLFOLDER]$(var.Product).exe",0' />
|
<RegistryValue Type="string" Value='"[INSTALLFOLDER_INNER]$(var.Product).exe",0' />
|
||||||
</RegistryKey>
|
</RegistryKey>
|
||||||
</Component>
|
</Component>
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell" />
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell" />
|
||||||
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open" />
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open" />
|
||||||
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open\command">
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)\shell\open\command">
|
||||||
<RegistryValue Type="string" Value='"[INSTALLFOLDER]$(var.Product).exe" --play "%1"' />
|
<RegistryValue Type="string" Value='"[INSTALLFOLDER_INNER]$(var.Product).exe" --play "%1"' />
|
||||||
</RegistryKey>
|
</RegistryKey>
|
||||||
</Component>
|
</Component>
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell" />
|
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell" />
|
||||||
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open" />
|
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open" />
|
||||||
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open\command">
|
<RegistryKey Root="HKCR" Key="$(var.ProductLower)\shell\open\command">
|
||||||
<RegistryValue Type="string" Value='"[INSTALLFOLDER]$(var.Product).exe" "%1"' />
|
<RegistryValue Type="string" Value='"[INSTALLFOLDER_INNER]$(var.Product).exe" "%1"' />
|
||||||
</RegistryKey>
|
</RegistryKey>
|
||||||
</Component>
|
</Component>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<?include ../Includes.wxi?>
|
<?include ../Includes.wxi?>
|
||||||
|
|
||||||
<DirectoryRef Id="INSTALLFOLDER" FileSource="$(var.BuildDir)">
|
<DirectoryRef Id="INSTALLFOLDER_INNER" FileSource="$(var.BuildDir)">
|
||||||
<Component Id="App.exe" Guid="620F0F69-4C17-4320-A619-495E329712A4">
|
<Component Id="App.exe" Guid="620F0F69-4C17-4320-A619-495E329712A4">
|
||||||
<File Id="App.exe" Name="$(var.Product).exe" KeyPath="yes" Checksum="yes">
|
<File Id="App.exe" Name="$(var.Product).exe" KeyPath="yes" Checksum="yes">
|
||||||
<!--<fire:FirewallException Id="AppEx" Name="$(var.Product) Service" Scope="any" IgnoreFailure="yes" />-->
|
<!--<fire:FirewallException Id="AppEx" Name="$(var.Product) Service" Scope="any" IgnoreFailure="yes" />-->
|
||||||
@@ -12,10 +12,10 @@
|
|||||||
</Component>
|
</Component>
|
||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
|
|
||||||
<CustomAction Id="RemoveInstallFolder.SetParam" Return="check" Property="RemoveInstallFolder" Value="[INSTALLFOLDER]" />
|
<CustomAction Id="RemoveInstallFolder.SetParam" Return="check" Property="RemoveInstallFolder" Value="[INSTALLFOLDER_INNER]" />
|
||||||
<CustomAction Id="AddFirewallRules.SetParam" Return="check" Property="AddFirewallRules" Value="1[INSTALLFOLDER]$(var.Product).exe" />
|
<CustomAction Id="AddFirewallRules.SetParam" Return="check" Property="AddFirewallRules" Value="1[INSTALLFOLDER_INNER]$(var.Product).exe" />
|
||||||
<CustomAction Id="RemoveFirewallRules.SetParam" Return="check" Property="RemoveFirewallRules" Value="0[INSTALLFOLDER]$(var.Product).exe" />
|
<CustomAction Id="RemoveFirewallRules.SetParam" Return="check" Property="RemoveFirewallRules" Value="0[INSTALLFOLDER_INNER]$(var.Product).exe" />
|
||||||
<CustomAction Id="CreateStartService.SetParam" Return="check" Property="CreateStartService" Value="$(var.Product);"[INSTALLFOLDER]$(var.Product).exe" --service" />
|
<CustomAction Id="CreateStartService.SetParam" Return="check" Property="CreateStartService" Value="$(var.Product);"[INSTALLFOLDER_INNER]$(var.Product).exe" --service" />
|
||||||
<CustomAction Id="TryStopDeleteService.SetParam" Return="check" Property="TryStopDeleteService" Value="$(var.Product)" />
|
<CustomAction Id="TryStopDeleteService.SetParam" Return="check" Property="TryStopDeleteService" Value="$(var.Product)" />
|
||||||
|
|
||||||
<CustomAction Id="LaunchApp" ExeCommand="" Return="asyncNoWait" FileRef="App.exe" />
|
<CustomAction Id="LaunchApp" ExeCommand="" Return="asyncNoWait" FileRef="App.exe" />
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<CustomAction Id="SetPropertyServiceStop.SetParam.ConfigKey" Return="check" Property="ConfigKey" Value="stop-service" />
|
<CustomAction Id="SetPropertyServiceStop.SetParam.ConfigKey" Return="check" Property="ConfigKey" Value="stop-service" />
|
||||||
<CustomAction Id="SetPropertyServiceStop.SetParam.PropertyName" Return="check" Property="PropertyName" Value="STOP_SERVICE" />
|
<CustomAction Id="SetPropertyServiceStop.SetParam.PropertyName" Return="check" Property="PropertyName" Value="STOP_SERVICE" />
|
||||||
<CustomAction Id="TryDeleteStartupShortcut.SetParam" Return="check" Property="ShortcutName" Value="$(var.Product) Tray" />
|
<CustomAction Id="TryDeleteStartupShortcut.SetParam" Return="check" Property="ShortcutName" Value="$(var.Product) Tray" />
|
||||||
<CustomAction Id="RemoveAmyuniIdd.SetParam" Return="check" Property="RemoveAmyuniIdd" Value="[INSTALLFOLDER]" />
|
<CustomAction Id="RemoveAmyuniIdd.SetParam" Return="check" Property="RemoveAmyuniIdd" Value="[INSTALLFOLDER_INNER]" />
|
||||||
<InstallExecuteSequence>
|
<InstallExecuteSequence>
|
||||||
|
|
||||||
<Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" />
|
<Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" />
|
||||||
@@ -43,8 +43,8 @@
|
|||||||
|
|
||||||
<!-- Do not call CreateStartService if is uninstalling. -->
|
<!-- Do not call CreateStartService if is uninstalling. -->
|
||||||
<!-- (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE) means uninstalling. -->
|
<!-- (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE) means uninstalling. -->
|
||||||
<Custom Action="CreateStartService" Before="InstallFinalize" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE="'Y'")" />
|
<Custom Action="CreateStartService" Before="InstallFinalize" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE="'Y'") AND (NOT CC_CONNECTION_TYPE="outgoing")" />
|
||||||
<Custom Action="CreateStartService.SetParam" Before="CreateStartService" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE="'Y'")" />
|
<Custom Action="CreateStartService.SetParam" Before="CreateStartService" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE="'Y'") AND (NOT CC_CONNECTION_TYPE="outgoing")" />
|
||||||
|
|
||||||
<Custom Action="CustomActionHello" Before="InstallFinalize" />
|
<Custom Action="CustomActionHello" Before="InstallFinalize" />
|
||||||
|
|
||||||
@@ -54,14 +54,14 @@
|
|||||||
|
|
||||||
<!-- Launch ClientLauncher if installing or already installed and not uninstalling -->
|
<!-- Launch ClientLauncher if installing or already installed and not uninstalling -->
|
||||||
<Custom Action="LaunchApp" After="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
|
<Custom Action="LaunchApp" After="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
|
||||||
<Custom Action="LaunchAppTray" After="InstallFinalize" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE="'Y'")"/>
|
<Custom Action="LaunchAppTray" After="InstallFinalize" Condition="(NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)) AND (NOT STOP_SERVICE="'Y'") AND (NOT CC_CONNECTION_TYPE="outgoing")"/>
|
||||||
|
|
||||||
<!--Workaround of "fire:FirewallException". If Outbound="Yes" or Outbound="true", the following error occurs.-->
|
<!--Workaround of "fire:FirewallException". If Outbound="Yes" or Outbound="true", the following error occurs.-->
|
||||||
<!--ExecFirewallExceptions: Error 0x80070057: failed to add app to the authorized apps list-->
|
<!--ExecFirewallExceptions: Error 0x80070057: failed to add app to the authorized apps list-->
|
||||||
<Custom Action="AddFirewallRules" Before="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
|
<Custom Action="AddFirewallRules" Before="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
|
||||||
<Custom Action="AddFirewallRules.SetParam" Before="AddFirewallRules" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
|
<Custom Action="AddFirewallRules.SetParam" Before="AddFirewallRules" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
|
||||||
|
|
||||||
<Custom Action="AddRegSoftwareSASGeneration" Before="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE)"/>
|
<Custom Action="AddRegSoftwareSASGeneration" Before="InstallFinalize" Condition="NOT (Installed AND REMOVE AND NOT UPGRADINGPRODUCTCODE) AND (NOT CC_CONNECTION_TYPE="outgoing")"/>
|
||||||
|
|
||||||
<Custom Action="RemoveInstallFolder" Before="RemoveFiles"/>
|
<Custom Action="RemoveInstallFolder" Before="RemoveFiles"/>
|
||||||
<Custom Action="RemoveInstallFolder.SetParam" Before="RemoveInstallFolder"/>
|
<Custom Action="RemoveInstallFolder.SetParam" Before="RemoveInstallFolder"/>
|
||||||
@@ -88,8 +88,8 @@
|
|||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
|
|
||||||
<DirectoryRef Id="App.StartMenu">
|
<DirectoryRef Id="App.StartMenu">
|
||||||
<Component Id="App.StartMenu.Shortcut" Guid="43ABCAC7-E47D-42D8-A408-25EC70DBB993" Condition="STARTMENUSHORTCUTS = 1">
|
<Component Id="App.StartMenu.Shortcut" Guid="43ABCAC7-E47D-42D8-A408-25EC70DBB993" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = "Y" OR STARTMENUSHORTCUTS = "y"">
|
||||||
<Shortcut Id="App.StartMenu.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
|
<Shortcut Id="App.StartMenu.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
|
||||||
<!--
|
<!--
|
||||||
Fix ICE 38 by adding a dummy registry key that is the key for this shortcut.
|
Fix ICE 38 by adding a dummy registry key that is the key for this shortcut.
|
||||||
https://learn.microsoft.com/en-us/windows/win32/msi/ice38
|
https://learn.microsoft.com/en-us/windows/win32/msi/ice38
|
||||||
@@ -97,31 +97,31 @@
|
|||||||
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.Shortcut" Type="string" Value="1" KeyPath="yes" />
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.Shortcut" Type="string" Value="1" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
|
|
||||||
<Component Id="App.StartMenu.ShortcutUninstall" Guid="E100D7F8-D607-4513-28DA-2C95E5EA698E" Condition="STARTMENUSHORTCUTS = 1">
|
<Component Id="App.StartMenu.ShortcutUninstall" Guid="E100D7F8-D607-4513-28DA-2C95E5EA698E" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = "Y" OR STARTMENUSHORTCUTS = "y"">
|
||||||
<Shortcut Id="App.StartMenu.ShortcutUninstall" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
|
<Shortcut Id="App.StartMenu.ShortcutUninstall" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
|
||||||
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.ShortcutUninstall" Type="string" Value="1" KeyPath="yes" />
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartMenu.ShortcutUninstall" Type="string" Value="1" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
<StandardDirectory Id="DesktopFolder">
|
<StandardDirectory Id="DesktopFolder">
|
||||||
<Component Id="App.Desktop.Shortcut" Guid="CA8FB7AA-17F7-4E36-A58A-5A016A303709" Condition="DESKTOPSHORTCUTS = 1">
|
<Component Id="App.Desktop.Shortcut" Guid="CA8FB7AA-17F7-4E36-A58A-5A016A303709" Condition="DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = "Y" OR DESKTOPSHORTCUTS = "y"">
|
||||||
<Shortcut Id="App.Desktop.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
|
<Shortcut Id="App.Desktop.Shortcut" Name="!(loc.SC_Client)" Description="!(loc.SC_Client_Desc)" Target="[!App.exe]" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
|
||||||
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.Desktop.Shortcut" Type="string" Value="1" KeyPath="yes" />
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.Desktop.Shortcut" Type="string" Value="1" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
</StandardDirectory>
|
</StandardDirectory>
|
||||||
<StandardDirectory Id="StartupFolder">
|
<StandardDirectory Id="StartupFolder">
|
||||||
<Component Id="App.StartupFolder.ShortcutTray" Guid="B1D1E2BB-E53E-E159-DB7C-744D5C726A8C" Condition="STARTUPSHORTCUTS = 1">
|
<Component Id="App.StartupFolder.ShortcutTray" Guid="B1D1E2BB-E53E-E159-DB7C-744D5C726A8C" Condition="STARTUPSHORTCUTS = 1 AND (NOT CC_CONNECTION_TYPE="outgoing")">
|
||||||
<Shortcut Id="App.StartupFolder.ShortcutTray" Name="!(loc.SC_Client_Tray)" Description="!(loc.SC_Client_Tray_Desc)" Target="[!App.exe]" Arguments="--tray" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER" />
|
<Shortcut Id="App.StartupFolder.ShortcutTray" Name="!(loc.SC_Client_Tray)" Description="!(loc.SC_Client_Tray_Desc)" Target="[!App.exe]" Arguments="--tray" Icon="AppIcon" WorkingDirectory="INSTALLFOLDER_INNER" />
|
||||||
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartupFolder.ShortcutTray" Type="string" Value="1" KeyPath="yes" />
|
<RegistryValue Root="HKCU" Key="Software\$(var.Product)" Name="App.StartupFolder.ShortcutTray" Type="string" Value="1" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
</StandardDirectory>
|
</StandardDirectory>
|
||||||
|
|
||||||
<!--<DirectoryRef Id="INSTALLFOLDER">
|
<!--<DirectoryRef Id="INSTALLFOLDER_INNER">
|
||||||
<Component Id="App.UninstallShortcut" Guid="FB0F2AC7-2AE5-4C54-B860-5E472620B6B1">
|
<Component Id="App.UninstallShortcut" Guid="FB0F2AC7-2AE5-4C54-B860-5E472620B6B1">
|
||||||
<Shortcut Id="App.UninstallShortcut" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
|
<Shortcut Id="App.UninstallShortcut" Name="!(loc.SC_Uninstall)" Description="!(loc.SC_Uninstall_Desc)" Target="[System6432Folder]msiexec.exe" Arguments="/x [ProductCode]" Icon="AppIcon" />
|
||||||
</Component>
|
</Component>
|
||||||
</DirectoryRef>-->
|
</DirectoryRef>-->
|
||||||
|
|
||||||
<ComponentGroup Id="Components" Directory="INSTALLFOLDER">
|
<ComponentGroup Id="Components" Directory="INSTALLFOLDER_INNER">
|
||||||
<ComponentRef Id="App.exe" />
|
<ComponentRef Id="App.exe" />
|
||||||
<ComponentRef Id="App.Desktop.Shortcut" />
|
<ComponentRef Id="App.Desktop.Shortcut" />
|
||||||
<!--<ComponentRef Id="App.UninstallShortcut" />-->
|
<!--<ComponentRef Id="App.UninstallShortcut" />-->
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
<!--$ArpStart$-->
|
<!--$ArpStart$-->
|
||||||
<!--$ArpEnd$-->
|
<!--$ArpEnd$-->
|
||||||
|
|
||||||
|
<!--$CustomClientPropsStart$-->
|
||||||
|
<!--$CustomClientPropsEnd$-->
|
||||||
|
|
||||||
<Property Id="APP_WINDOWS_INSTALLER">
|
<Property Id="APP_WINDOWS_INSTALLER">
|
||||||
<RegistrySearch Id="AppWindowsInstallerFolderSearch" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.Product)" Name="WindowsInstaller" Type="raw" />
|
<RegistrySearch Id="AppWindowsInstallerFolderSearch" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\$(var.Product)" Name="WindowsInstaller" Type="raw" />
|
||||||
</Property>
|
</Property>
|
||||||
|
|||||||
@@ -25,11 +25,25 @@
|
|||||||
</Property>
|
</Property>
|
||||||
|
|
||||||
<!-- Component that persists the property values to the registry so they are available during an upgrade/modify -->
|
<!-- Component that persists the property values to the registry so they are available during an upgrade/modify -->
|
||||||
<DirectoryRef Id="INSTALLFOLDER">
|
<DirectoryRef Id="INSTALLFOLDER_INNER">
|
||||||
<Component Id="Product.Registry.PersistedShortcutProperties" Guid="1BBAD054-6EC2-4362-BF1B-E8BDE988B597">
|
<Component Id="Product.Registry.PersistedStartMenuShortcutProperties1" Guid="62F79BCF-3367-4ACF-950F-F8BCABACDDC0" Condition="STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = "Y" OR STARTMENUSHORTCUTS = "y"">
|
||||||
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
||||||
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="[STARTMENUSHORTCUTS]" KeyPath="yes" />
|
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="1" KeyPath="yes" />
|
||||||
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="[DESKTOPSHORTCUTS]" />
|
</RegistryKey>
|
||||||
|
</Component>
|
||||||
|
<Component Id="Product.Registry.PersistedStartMenuShortcutProperties0" Guid="8EA2D5A8-6E5D-4BDD-9019-2099297FF519" Condition="NOT (STARTMENUSHORTCUTS = 1 OR STARTMENUSHORTCUTS = "Y" OR STARTMENUSHORTCUTS = "y")">
|
||||||
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
||||||
|
<RegistryValue Type="string" Name="STARTMENUSHORTCUTS" Value="0" KeyPath="yes" />
|
||||||
|
</RegistryKey>
|
||||||
|
</Component>
|
||||||
|
<Component Id="Product.Registry.PersistedDesktopShortcutProperties1" Guid="1BBAD054-6EC2-4362-BF1B-E8BDE988B597" Condition="DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = "Y" OR DESKTOPSHORTCUTS = "y"">
|
||||||
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
||||||
|
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="1" />
|
||||||
|
</RegistryKey>
|
||||||
|
</Component>
|
||||||
|
<Component Id="Product.Registry.PersistedDesktopShortcutProperties0" Guid="FA992614-D2E1-4795-9696-D45A5EF1B9C8" Condition="NOT (DESKTOPSHORTCUTS = 1 OR DESKTOPSHORTCUTS = "Y" OR DESKTOPSHORTCUTS = "y")">
|
||||||
|
<RegistryKey Root="HKCR" Key="$(var.RegKeyRoot)">
|
||||||
|
<RegistryValue Type="string" Name="DESKTOPSHORTCUTS" Value="0" />
|
||||||
</RegistryKey>
|
</RegistryKey>
|
||||||
</Component>
|
</Component>
|
||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
@@ -45,8 +59,8 @@
|
|||||||
<SetProperty Action="RestoreSavedDesktopShortcutsValue" Id="CREATEDESKTOPSHORTCUTS" Value="[SavedDesktopShortcutsCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedDesktopShortcutsCmdLineValue" />
|
<SetProperty Action="RestoreSavedDesktopShortcutsValue" Id="CREATEDESKTOPSHORTCUTS" Value="[SavedDesktopShortcutsCmdLineValue]" After="AppSearch" Sequence="first" Condition="SavedDesktopShortcutsCmdLineValue" />
|
||||||
|
|
||||||
<!-- If a command line value or registry value was set, update the main properties with the value -->
|
<!-- If a command line value or registry value was set, update the main properties with the value -->
|
||||||
<SetProperty Id="STARTMENUSHORTCUTS" Value="[CREATESTARTMENUSHORTCUTS]" After="RestoreSavedStartMenuShortcutsValue" Sequence="first" Condition="CREATESTARTMENUSHORTCUTS" />
|
<SetProperty Id="STARTMENUSHORTCUTS" Value="" After="RestoreSavedStartMenuShortcutsValue" Sequence="first" Condition="CREATESTARTMENUSHORTCUTS AND NOT (CREATESTARTMENUSHORTCUTS = 1 OR CREATESTARTMENUSHORTCUTS = "Y" OR CREATESTARTMENUSHORTCUTS = "y")" />
|
||||||
<SetProperty Id="DESKTOPSHORTCUTS" Value="[CREATEDESKTOPSHORTCUTS]" After="RestoreSavedDesktopShortcutsValue" Sequence="first" Condition="CREATEDESKTOPSHORTCUTS" />
|
<SetProperty Id="DESKTOPSHORTCUTS" Value="" After="RestoreSavedDesktopShortcutsValue" Sequence="first" Condition="CREATEDESKTOPSHORTCUTS AND NOT (CREATEDESKTOPSHORTCUTS = 1 OR CREATEDESKTOPSHORTCUTS = "Y" OR CREATEDESKTOPSHORTCUTS = "y")" />
|
||||||
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
</Wix>
|
</Wix>
|
||||||
|
|||||||
@@ -49,4 +49,7 @@ This file contains the declaration of all the localizable strings.
|
|||||||
<String Id="AnotherAppDialogTitle" Value="Cancel installation."/>
|
<String Id="AnotherAppDialogTitle" Value="Cancel installation."/>
|
||||||
<String Id="AnotherAppDialogDescription" Value="The application is installed by self-installation method, please uninstall it first."/>
|
<String Id="AnotherAppDialogDescription" Value="The application is installed by self-installation method, please uninstall it first."/>
|
||||||
|
|
||||||
|
<String Id="MyInstallDirDlgDesktopShortcuts" Value="Create desktop icon" />
|
||||||
|
<String Id="MyInstallDirDlgStartMenuShortcuts" Value="Create start menu shortcuts" />
|
||||||
|
|
||||||
</WixLocalization>
|
</WixLocalization>
|
||||||
|
|||||||
@@ -18,11 +18,11 @@
|
|||||||
<!-- User Interface -->
|
<!-- User Interface -->
|
||||||
<WixVariable Id="WixUILicenseRtf" Value="License.rtf" />
|
<WixVariable Id="WixUILicenseRtf" Value="License.rtf" />
|
||||||
|
|
||||||
<ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER" />
|
<ui:WixUI Id="UI_MyInstallDialog" InstallDirectory="INSTALLFOLDER_INNER" />
|
||||||
<UIRef Id="WixUI_ErrorProgressText" />
|
<UIRef Id="WixUI_ErrorProgressText" />
|
||||||
|
|
||||||
<InstallUISequence>
|
<InstallUISequence>
|
||||||
<Show Dialog="AnotherAppDialog" Before="WelcomeDlg" Condition="Not installed AND APP_WINDOWS_INSTALLER="#0""/>
|
<Show Dialog="UI_AnotherAppDialog" Before="WelcomeDlg" Condition="Not installed AND APP_WINDOWS_INSTALLER="#0""/>
|
||||||
</InstallUISequence>
|
</InstallUISequence>
|
||||||
|
|
||||||
<InstallExecuteSequence>
|
<InstallExecuteSequence>
|
||||||
@@ -40,14 +40,17 @@
|
|||||||
<Feature Id="App" Level="1" AllowAdvertise="no" Display="expand" Title="!(loc.F_App)" Description="!(loc.F_App_Desc)" AllowAbsent="no">
|
<Feature Id="App" Level="1" AllowAdvertise="no" Display="expand" Title="!(loc.F_App)" Description="!(loc.F_App_Desc)" AllowAbsent="no">
|
||||||
<ComponentGroupRef Id="Components" />
|
<ComponentGroupRef Id="Components" />
|
||||||
|
|
||||||
<ComponentRef Id="Product.Registry.InstallDir" />
|
<ComponentRef Id="Product.Registry.InstallFolder" />
|
||||||
<ComponentRef Id="Product.Registry.DefaultIcon" />
|
<ComponentRef Id="Product.Registry.DefaultIcon" />
|
||||||
<ComponentRef Id="Product.Registry.CommandPlay" />
|
<ComponentRef Id="Product.Registry.CommandPlay" />
|
||||||
<ComponentRef Id="Product.Registry.URLProtocol" />
|
<ComponentRef Id="Product.Registry.URLProtocol" />
|
||||||
<ComponentRef Id="Product.Registry.Command" />
|
<ComponentRef Id="Product.Registry.Command" />
|
||||||
<ComponentRef Id="Product.Registry.UninstallApp" />
|
<ComponentRef Id="Product.Registry.UninstallApp" />
|
||||||
<ComponentRef Id="App.StartMenu" />
|
<ComponentRef Id="App.StartMenu" />
|
||||||
<ComponentRef Id="Product.Registry.PersistedShortcutProperties" />
|
<ComponentRef Id="Product.Registry.PersistedStartMenuShortcutProperties1" />
|
||||||
|
<ComponentRef Id="Product.Registry.PersistedStartMenuShortcutProperties0" />
|
||||||
|
<ComponentRef Id="Product.Registry.PersistedDesktopShortcutProperties1" />
|
||||||
|
<ComponentRef Id="Product.Registry.PersistedDesktopShortcutProperties0" />
|
||||||
</Feature>
|
</Feature>
|
||||||
|
|
||||||
<!--https://wixtoolset.org/docs/tools/wixext/wixui/#customizing-a-dialog-set-->
|
<!--https://wixtoolset.org/docs/tools/wixext/wixui/#customizing-a-dialog-set-->
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<UI>
|
<UI>
|
||||||
<Dialog Id="AnotherAppDialog" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
|
<Dialog Id="UI_AnotherAppDialog" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
|
||||||
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUICancel)">
|
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUICancel)">
|
||||||
<Publish Event="EndDialog" Value="ErrorAbort" />
|
<Publish Event="EndDialog" Value="ErrorAbort" />
|
||||||
</Control>
|
</Control>
|
||||||
|
|||||||
31
res/msi/Package/UI/MyInstallDirDlg.wxs
Normal file
31
res/msi/Package/UI/MyInstallDirDlg.wxs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<!-- https://github.com/wixtoolset/wix/blob/ce73352b1fa1d4f9cded10a0ee410f2e786bd326/src/ext/UI/wixlib/InstallDirDlg.wxs -->
|
||||||
|
|
||||||
|
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
|
||||||
|
|
||||||
|
|
||||||
|
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||||
|
<Fragment>
|
||||||
|
<UI>
|
||||||
|
<Dialog Id="MyInstallDirDlg" Width="370" Height="270" Title="!(loc.InstallDirDlg_Title)">
|
||||||
|
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)" />
|
||||||
|
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" />
|
||||||
|
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
|
||||||
|
<Publish Event="SpawnDialog" Value="CancelDlg" />
|
||||||
|
</Control>
|
||||||
|
|
||||||
|
<Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgDescription)" />
|
||||||
|
<Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgTitle)" />
|
||||||
|
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.InstallDirDlgBannerBitmap)" />
|
||||||
|
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
|
||||||
|
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
|
||||||
|
|
||||||
|
<Control Id="FolderLabel" Type="Text" X="20" Y="60" Width="290" Height="18" NoPrefix="yes" Text="!(loc.InstallDirDlgFolderLabel)" />
|
||||||
|
<Control Id="Folder" Type="PathEdit" X="20" Y="80" Width="320" Height="18" Property="WIXUI_INSTALLDIR" Indirect="yes" />
|
||||||
|
<Control Id="ChangeFolder" Type="PushButton" X="20" Y="100" Width="56" Height="17" Text="!(loc.InstallDirDlgChange)" />
|
||||||
|
|
||||||
|
<Control Id="ChkBoxStartMenuShortcuts" Type="CheckBox" X="20" Y="140" Width="290" Height="17" Property="STARTMENUSHORTCUTS" CheckBoxValue="1" Text="!(loc.MyInstallDirDlgStartMenuShortcuts)" />
|
||||||
|
<Control Id="ChkBoxDesktopShortcuts" Type="CheckBox" X="20" Y="160" Width="290" Height="17" Property="DESKTOPSHORTCUTS" CheckBoxValue="1" Text="!(loc.MyInstallDirDlgDesktopShortcuts)" />
|
||||||
|
</Dialog>
|
||||||
|
</UI>
|
||||||
|
</Fragment>
|
||||||
|
</Wix>
|
||||||
87
res/msi/Package/UI/MyInstallDlg.wxs
Normal file
87
res/msi/Package/UI/MyInstallDlg.wxs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<!-- From https://github.com/wixtoolset/wix/blob/ce73352b1fa1d4f9cded10a0ee410f2e786bd326/src/ext/UI/wixlib/WixUI_InstallDir.wxs -->
|
||||||
|
|
||||||
|
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
First-time install dialog sequence:
|
||||||
|
- WixUI_WelcomeDlg
|
||||||
|
- WixUI_LicenseAgreementDlg
|
||||||
|
- WixUI_InstallDirDlg
|
||||||
|
- WixUI_VerifyReadyDlg
|
||||||
|
- WixUI_DiskCostDlg
|
||||||
|
|
||||||
|
Maintenance dialog sequence:
|
||||||
|
- WixUI_MaintenanceWelcomeDlg
|
||||||
|
- WixUI_MaintenanceTypeDlg
|
||||||
|
- WixUI_InstallDirDlg
|
||||||
|
- WixUI_VerifyReadyDlg
|
||||||
|
|
||||||
|
Patch dialog sequence:
|
||||||
|
- WixUI_WelcomeDlg
|
||||||
|
- WixUI_VerifyReadyDlg
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
||||||
|
<?foreach WIXUIARCH in X86;X64;A64 ?>
|
||||||
|
<Fragment>
|
||||||
|
<UI Id="UI_MyInstallDialog_$(WIXUIARCH)">
|
||||||
|
<Publish Dialog="LicenseAgreementDlg" Control="Print" Event="DoAction" Value="WixUIPrintEula_$(WIXUIARCH)" />
|
||||||
|
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH" />
|
||||||
|
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="2" Condition="NOT WIXUI_DONTVALIDATEPATH" />
|
||||||
|
</UI>
|
||||||
|
|
||||||
|
<UIRef Id="UI_MyInstallDialog" />
|
||||||
|
</Fragment>
|
||||||
|
<?endforeach?>
|
||||||
|
|
||||||
|
<Fragment>
|
||||||
|
<UI Id="file UI_MyInstallDialog">
|
||||||
|
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
|
||||||
|
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
|
||||||
|
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
|
||||||
|
|
||||||
|
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
|
||||||
|
|
||||||
|
<DialogRef Id="BrowseDlg" />
|
||||||
|
<DialogRef Id="DiskCostDlg" />
|
||||||
|
<DialogRef Id="ErrorDlg" />
|
||||||
|
<DialogRef Id="FatalError" />
|
||||||
|
<DialogRef Id="FilesInUse" />
|
||||||
|
<DialogRef Id="MsiRMFilesInUse" />
|
||||||
|
<DialogRef Id="PrepareDlg" />
|
||||||
|
<DialogRef Id="ProgressDlg" />
|
||||||
|
<DialogRef Id="ResumeDlg" />
|
||||||
|
<DialogRef Id="UserExit" />
|
||||||
|
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"" />
|
||||||
|
|
||||||
|
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />
|
||||||
|
|
||||||
|
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg" Condition="NOT Installed" />
|
||||||
|
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="Installed AND PATCH" />
|
||||||
|
|
||||||
|
<Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" />
|
||||||
|
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="MyInstallDirDlg" Condition="LicenseAccepted = "1"" />
|
||||||
|
|
||||||
|
<Publish Dialog="MyInstallDirDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg" />
|
||||||
|
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />
|
||||||
|
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"" />
|
||||||
|
<Publish Dialog="MyInstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4" Condition="WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"" />
|
||||||
|
<Publish Dialog="MyInstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
|
||||||
|
<Publish Dialog="MyInstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2" />
|
||||||
|
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MyInstallDirDlg" Order="1" Condition="NOT Installed" />
|
||||||
|
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2" Condition="Installed AND NOT PATCH" />
|
||||||
|
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="Installed AND PATCH" />
|
||||||
|
|
||||||
|
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />
|
||||||
|
|
||||||
|
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||||
|
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||||
|
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />
|
||||||
|
|
||||||
|
<Property Id="ARPNOMODIFY" Value="1" />
|
||||||
|
</UI>
|
||||||
|
|
||||||
|
<UIRef Id="WixUI_Common" />
|
||||||
|
</Fragment>
|
||||||
|
</Wix>
|
||||||
@@ -64,6 +64,12 @@ def make_parser():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-c", "--custom", action="store_true", help="Is custom client", default=False
|
"-c", "--custom", action="store_true", help="Is custom client", default=False
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--conn-type",
|
||||||
|
type=str,
|
||||||
|
default="",
|
||||||
|
help='Connection type, e.g. "incoming", "outgoing". Default is empty, means incoming-outgoing',
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--app-name", type=str, default="RustDesk", help="The app name."
|
"--app-name", type=str, default="RustDesk", help="The app name."
|
||||||
)
|
)
|
||||||
@@ -84,7 +90,7 @@ def make_parser():
|
|||||||
|
|
||||||
|
|
||||||
def read_lines_and_start_index(file_path, tag_start, tag_end):
|
def read_lines_and_start_index(file_path, tag_start, tag_end):
|
||||||
with open(file_path, "r") as f:
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
index_start = -1
|
index_start = -1
|
||||||
index_end = -1
|
index_end = -1
|
||||||
@@ -174,11 +180,11 @@ def gen_pre_vars(args, dist_dir):
|
|||||||
def replace_app_name_in_langs(app_name):
|
def replace_app_name_in_langs(app_name):
|
||||||
langs_dir = Path(sys.argv[0]).parent.joinpath("Package/Language")
|
langs_dir = Path(sys.argv[0]).parent.joinpath("Package/Language")
|
||||||
for file_path in langs_dir.glob("*.wxl"):
|
for file_path in langs_dir.glob("*.wxl"):
|
||||||
with open(file_path, "r") as f:
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
lines[i] = line.replace("RustDesk", app_name)
|
lines[i] = line.replace("RustDesk", app_name)
|
||||||
with open(file_path, "w") as f:
|
with open(file_path, "w", encoding="utf-8") as f:
|
||||||
f.writelines(lines)
|
f.writelines(lines)
|
||||||
|
|
||||||
|
|
||||||
@@ -295,7 +301,7 @@ def gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir):
|
|||||||
f'{indent}<RegistryValue Type="string" Name="DisplayName" Value="{args.app_name}" />\n'
|
f'{indent}<RegistryValue Type="string" Name="DisplayName" Value="{args.app_name}" />\n'
|
||||||
)
|
)
|
||||||
lines_new.append(
|
lines_new.append(
|
||||||
f'{indent}<RegistryValue Type="string" Name="DisplayIcon" Value="[INSTALLFOLDER]{args.app_name}.exe" />\n'
|
f'{indent}<RegistryValue Type="string" Name="DisplayIcon" Value="[INSTALLFOLDER_INNER]{args.app_name}.exe" />\n'
|
||||||
)
|
)
|
||||||
lines_new.append(
|
lines_new.append(
|
||||||
f'{indent}<RegistryValue Type="string" Name="DisplayVersion" Value="{g_version}" />\n'
|
f'{indent}<RegistryValue Type="string" Name="DisplayVersion" Value="{g_version}" />\n'
|
||||||
@@ -308,7 +314,7 @@ def gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir):
|
|||||||
f'{indent}<RegistryValue Type="string" Name="InstallDate" Value="{installDate}" />\n'
|
f'{indent}<RegistryValue Type="string" Name="InstallDate" Value="{installDate}" />\n'
|
||||||
)
|
)
|
||||||
lines_new.append(
|
lines_new.append(
|
||||||
f'{indent}<RegistryValue Type="string" Name="InstallLocation" Value="[INSTALLFOLDER]" />\n'
|
f'{indent}<RegistryValue Type="string" Name="InstallLocation" Value="[INSTALLFOLDER_INNER]" />\n'
|
||||||
)
|
)
|
||||||
lines_new.append(
|
lines_new.append(
|
||||||
f'{indent}<RegistryValue Type="string" Name="InstallSource" Value="[InstallSource]" />\n'
|
f'{indent}<RegistryValue Type="string" Name="InstallSource" Value="[InstallSource]" />\n'
|
||||||
@@ -385,6 +391,26 @@ def gen_custom_ARPSYSTEMCOMPONENT(args, dist_dir):
|
|||||||
else:
|
else:
|
||||||
return gen_custom_ARPSYSTEMCOMPONENT_False(args)
|
return gen_custom_ARPSYSTEMCOMPONENT_False(args)
|
||||||
|
|
||||||
|
def gen_conn_type(args):
|
||||||
|
def func(lines, index_start):
|
||||||
|
indent = g_indent_unit * 3
|
||||||
|
|
||||||
|
lines_new = []
|
||||||
|
if args.conn_type != "":
|
||||||
|
lines_new.append(
|
||||||
|
f"""{indent}<Property Id="CC_CONNECTION_TYPE" Value="{args.conn_type}" />\n"""
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, line in enumerate(lines_new):
|
||||||
|
lines.insert(index_start + i + 1, line)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
return gen_content_between_tags(
|
||||||
|
"Package/Fragments/AddRemoveProperties.wxs",
|
||||||
|
"<!--$CustomClientPropsStart$-->",
|
||||||
|
"<!--$CustomClientPropsEnd$-->",
|
||||||
|
func,
|
||||||
|
)
|
||||||
|
|
||||||
def gen_content_between_tags(filename, tag_start, tag_end, func):
|
def gen_content_between_tags(filename, tag_start, tag_end, func):
|
||||||
target_file = Path(sys.argv[0]).parent.joinpath(filename)
|
target_file = Path(sys.argv[0]).parent.joinpath(filename)
|
||||||
@@ -394,7 +420,7 @@ def gen_content_between_tags(filename, tag_start, tag_end, func):
|
|||||||
|
|
||||||
func(lines, index_start)
|
func(lines, index_start)
|
||||||
|
|
||||||
with open(target_file, "w") as f:
|
with open(target_file, "w", encoding="utf-8") as f:
|
||||||
f.writelines(lines)
|
f.writelines(lines)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -454,19 +480,19 @@ def update_license_file(app_name):
|
|||||||
if app_name == "RustDesk":
|
if app_name == "RustDesk":
|
||||||
return
|
return
|
||||||
license_file = Path(sys.argv[0]).parent.joinpath("Package/License.rtf")
|
license_file = Path(sys.argv[0]).parent.joinpath("Package/License.rtf")
|
||||||
with open(license_file, "r") as f:
|
with open(license_file, "r", encoding="utf-8") as f:
|
||||||
license_content = f.read()
|
license_content = f.read()
|
||||||
license_content = license_content.replace("website rustdesk.com and other ", "")
|
license_content = license_content.replace("website rustdesk.com and other ", "")
|
||||||
license_content = license_content.replace("RustDesk", app_name)
|
license_content = license_content.replace("RustDesk", app_name)
|
||||||
license_content = re.sub("Purslane Ltd", app_name, license_content, flags=re.IGNORECASE)
|
license_content = re.sub("Purslane Ltd", app_name, license_content, flags=re.IGNORECASE)
|
||||||
with open(license_file, "w") as f:
|
with open(license_file, "w", encoding="utf-8") as f:
|
||||||
f.write(license_content)
|
f.write(license_content)
|
||||||
|
|
||||||
|
|
||||||
def replace_component_guids_in_wxs():
|
def replace_component_guids_in_wxs():
|
||||||
langs_dir = Path(sys.argv[0]).parent.joinpath("Package")
|
langs_dir = Path(sys.argv[0]).parent.joinpath("Package")
|
||||||
for file_path in langs_dir.glob("**/*.wxs"):
|
for file_path in langs_dir.glob("**/*.wxs"):
|
||||||
with open(file_path, "r") as f:
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
|
|
||||||
# <Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49">
|
# <Component Id="Product.Registry.DefaultIcon" Guid="6DBF2690-0955-4C6A-940F-634DDA503F49">
|
||||||
@@ -475,7 +501,7 @@ def replace_component_guids_in_wxs():
|
|||||||
if match:
|
if match:
|
||||||
lines[i] = re.sub(r'Guid="[^"]+"', f'Guid="{uuid.uuid4()}"', line)
|
lines[i] = re.sub(r'Guid="[^"]+"', f'Guid="{uuid.uuid4()}"', line)
|
||||||
|
|
||||||
with open(file_path, "w") as f:
|
with open(file_path, "w", encoding="utf-8") as f:
|
||||||
f.writelines(lines)
|
f.writelines(lines)
|
||||||
|
|
||||||
|
|
||||||
@@ -506,6 +532,9 @@ if __name__ == "__main__":
|
|||||||
if not gen_custom_ARPSYSTEMCOMPONENT(args, dist_dir):
|
if not gen_custom_ARPSYSTEMCOMPONENT(args, dist_dir):
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
if not gen_conn_type(args):
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
if not gen_auto_component(app_name, dist_dir):
|
if not gen_auto_component(app_name, dist_dir):
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name: rustdesk
|
Name: rustdesk
|
||||||
Version: 1.2.7
|
Version: 1.3.0
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: RPM package
|
Summary: RPM package
|
||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name: rustdesk
|
Name: rustdesk
|
||||||
Version: 1.2.7
|
Version: 1.3.0
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: RPM package
|
Summary: RPM package
|
||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name: rustdesk
|
Name: rustdesk
|
||||||
Version: 1.2.7
|
Version: 1.3.0
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: RPM package
|
Summary: RPM package
|
||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
|
|||||||
123
res/users.py
Executable file
123
res/users.py
Executable file
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import argparse
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
|
||||||
|
def view(
|
||||||
|
url,
|
||||||
|
token,
|
||||||
|
name=None,
|
||||||
|
group_name=None,
|
||||||
|
):
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
pageSize = 30
|
||||||
|
params = {
|
||||||
|
"name": name,
|
||||||
|
"group_name": group_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
params = {
|
||||||
|
k: "%" + v + "%" if (v != "-" and "%" not in v) else v
|
||||||
|
for k, v in params.items()
|
||||||
|
if v is not None
|
||||||
|
}
|
||||||
|
params["pageSize"] = pageSize
|
||||||
|
|
||||||
|
users = []
|
||||||
|
|
||||||
|
current = 1
|
||||||
|
|
||||||
|
while True:
|
||||||
|
params["current"] = current
|
||||||
|
response = requests.get(f"{url}/api/users", headers=headers, params=params)
|
||||||
|
response_json = response.json()
|
||||||
|
|
||||||
|
data = response_json.get("data", [])
|
||||||
|
users.extend(data)
|
||||||
|
|
||||||
|
total = response_json.get("total", 0)
|
||||||
|
current += pageSize
|
||||||
|
if len(data) < pageSize or current > total:
|
||||||
|
break
|
||||||
|
|
||||||
|
return users
|
||||||
|
|
||||||
|
|
||||||
|
def check(response):
|
||||||
|
if response.status_code == 200:
|
||||||
|
try:
|
||||||
|
response_json = response.json()
|
||||||
|
return response_json
|
||||||
|
except ValueError:
|
||||||
|
return response.text or "Success"
|
||||||
|
else:
|
||||||
|
return "Failed", response.status_code, response.text
|
||||||
|
|
||||||
|
|
||||||
|
def disable(url, token, guid, name):
|
||||||
|
print("Disable", name)
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.post(f"{url}/api/users/{guid}/disable", headers=headers)
|
||||||
|
return check(response)
|
||||||
|
|
||||||
|
|
||||||
|
def enable(url, token, guid, name):
|
||||||
|
print("Enable", name)
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.post(f"{url}/api/users/{guid}/enable", headers=headers)
|
||||||
|
return check(response)
|
||||||
|
|
||||||
|
|
||||||
|
def delete(url, token, guid, name):
|
||||||
|
print("Delete", name)
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.delete(f"{url}/api/users/{guid}", headers=headers)
|
||||||
|
return check(response)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="User manager")
|
||||||
|
parser.add_argument(
|
||||||
|
"command",
|
||||||
|
choices=["view", "disable", "enable", "delete"],
|
||||||
|
help="Command to execute",
|
||||||
|
)
|
||||||
|
parser.add_argument("--url", required=True, help="URL of the API")
|
||||||
|
parser.add_argument(
|
||||||
|
"--token", required=True, help="Bearer token for authentication"
|
||||||
|
)
|
||||||
|
parser.add_argument("--name", help="User name")
|
||||||
|
parser.add_argument("--group_name", help="Group name")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
while args.url.endswith("/"): args.url = args.url[:-1]
|
||||||
|
|
||||||
|
users = view(
|
||||||
|
args.url,
|
||||||
|
args.token,
|
||||||
|
args.name,
|
||||||
|
args.group_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
if args.command == "view":
|
||||||
|
for user in users:
|
||||||
|
print(user)
|
||||||
|
elif args.command == "disable":
|
||||||
|
for user in users:
|
||||||
|
response = disable(args.url, args.token, user["guid"], user["name"])
|
||||||
|
print(response)
|
||||||
|
elif args.command == "enable":
|
||||||
|
for user in users:
|
||||||
|
response = enable(args.url, args.token, user["guid"], user["name"])
|
||||||
|
print(response)
|
||||||
|
elif args.command == "delete":
|
||||||
|
for user in users:
|
||||||
|
response = delete(args.url, args.token, user["guid"], user["name"])
|
||||||
|
print(response)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
60
res/vcpkg/aom/aom-avx2.diff
Normal file
60
res/vcpkg/aom/aom-avx2.diff
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
diff --git a/build/cmake/cpu.cmake b/build/cmake/cpu.cmake
|
||||||
|
index acebe20..8c67d89 100644
|
||||||
|
--- a/build/cmake/cpu.cmake
|
||||||
|
+++ b/build/cmake/cpu.cmake
|
||||||
|
@@ -120,6 +120,19 @@ elseif("${AOM_TARGET_CPU}" MATCHES "^x86")
|
||||||
|
set(RTCD_ARCH_X86_64 "yes")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
+ # AVX2 requires __m256i definition starting v3.9.0
|
||||||
|
+
|
||||||
|
+ if(ENABLE_AVX2)
|
||||||
|
+ aom_check_source_compiles("x86_64_avx2_m256i_available" "
|
||||||
|
+#include <emmintrin.h>
|
||||||
|
+#ifndef __m256i
|
||||||
|
+#error 1
|
||||||
|
+#endif" HAVE_AVX2_M256I)
|
||||||
|
+ if(HAVE_AVX2_M256I EQUAL 0)
|
||||||
|
+ set(ENABLE_AVX2 0)
|
||||||
|
+ endif()
|
||||||
|
+ endif()
|
||||||
|
+
|
||||||
|
set(X86_FLAVORS "MMX;SSE;SSE2;SSE3;SSSE3;SSE4_1;SSE4_2;AVX;AVX2")
|
||||||
|
foreach(flavor ${X86_FLAVORS})
|
||||||
|
if(ENABLE_${flavor} AND NOT disable_remaining_flavors)
|
||||||
|
diff --git a/aom_dsp/x86/synonyms.h b/aom_dsp/x86/synonyms.h
|
||||||
|
index 0d51cdf..6744ec5 100644
|
||||||
|
--- a/aom_dsp/x86/synonyms.h
|
||||||
|
+++ b/aom_dsp/x86/synonyms.h
|
||||||
|
@@ -46,13 +46,6 @@ static INLINE __m128i xx_loadu_128(const void *a) {
|
||||||
|
return _mm_loadu_si128((const __m128i *)a);
|
||||||
|
}
|
||||||
|
|
||||||
|
-// Load 64 bits from each of hi and low, and pack into an SSE register
|
||||||
|
-// Since directly loading as `int64_t`s and using _mm_set_epi64 may violate
|
||||||
|
-// the strict aliasing rule, this takes a different approach
|
||||||
|
-static INLINE __m128i xx_loadu_2x64(const void *hi, const void *lo) {
|
||||||
|
- return _mm_unpacklo_epi64(_mm_loadu_si64(lo), _mm_loadu_si64(hi));
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static INLINE void xx_storel_32(void *const a, const __m128i v) {
|
||||||
|
const int val = _mm_cvtsi128_si32(v);
|
||||||
|
memcpy(a, &val, sizeof(val));
|
||||||
|
diff --git a/aom_dsp/x86/synonyms_avx2.h b/aom_dsp/x86/synonyms_avx2.h
|
||||||
|
index d4e8f69..45be17e 100644
|
||||||
|
--- a/aom_dsp/x86/synonyms_avx2.h
|
||||||
|
+++ b/aom_dsp/x86/synonyms_avx2.h
|
||||||
|
@@ -25,6 +25,13 @@
|
||||||
|
* Intrinsics prefixed with yy_ operate on or return 256bit YMM registers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
+// Load 64 bits from each of hi and low, and pack into an SSE register
|
||||||
|
+// Since directly loading as `int64_t`s and using _mm_set_epi64 may violate
|
||||||
|
+// the strict aliasing rule, this takes a different approach
|
||||||
|
+static INLINE __m128i xx_loadu_2x64(const void *hi, const void *lo) {
|
||||||
|
+ return _mm_unpacklo_epi64(_mm_loadu_si64(lo), _mm_loadu_si64(hi));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
// Loads and stores to do away with the tedium of casting the address
|
||||||
|
// to the right type.
|
||||||
|
static INLINE __m256i yy_load_256(const void *a) {
|
||||||
@@ -14,6 +14,7 @@ vcpkg_from_git(
|
|||||||
REF 8ad484f8a18ed1853c094e7d3a4e023b2a92df28 # 3.9.1
|
REF 8ad484f8a18ed1853c094e7d3a4e023b2a92df28 # 3.9.1
|
||||||
PATCHES
|
PATCHES
|
||||||
aom-uninitialized-pointer.diff
|
aom-uninitialized-pointer.diff
|
||||||
|
aom-avx2.diff
|
||||||
# Can be dropped when https://bugs.chromium.org/p/aomedia/issues/detail?id=3029 is merged into the upstream
|
# Can be dropped when https://bugs.chromium.org/p/aomedia/issues/detail?id=3029 is merged into the upstream
|
||||||
aom-install.diff
|
aom-install.diff
|
||||||
)
|
)
|
||||||
|
|||||||
11
res/vcpkg/ffmpeg/0002-fix-msvc-link.patch
Normal file
11
res/vcpkg/ffmpeg/0002-fix-msvc-link.patch
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
diff --git a/configure b/configure
|
||||||
|
--- a/configure
|
||||||
|
+++ b/configure
|
||||||
|
@@ -6162,6 +6162,7 @@ EOF
|
||||||
|
test -n "$extern_prefix" && append X86ASMFLAGS "-DPREFIX"
|
||||||
|
case "$objformat" in
|
||||||
|
elf*) enabled debug && append X86ASMFLAGS $x86asm_debug ;;
|
||||||
|
+ win*) enabled debug && append X86ASMFLAGS "-g" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
enabled avx512 && check_x86asm avx512_external "vmovdqa32 [eax]{k1}{z}, zmm0"
|
||||||
13
res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch
Normal file
13
res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c
|
||||||
|
--- a/fftools/cmdutils.c
|
||||||
|
+++ b/fftools/cmdutils.c
|
||||||
|
@@ -51,6 +51,8 @@
|
||||||
|
#include "fopen_utf8.h"
|
||||||
|
#include "opt_common.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
+#define _WIN32_WINNT 0x0502
|
||||||
|
+#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include "compat/w32dlfcn.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
55
res/vcpkg/ffmpeg/0005-fix-nasm.patch
Normal file
55
res/vcpkg/ffmpeg/0005-fix-nasm.patch
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile
|
||||||
|
--- a/libavcodec/x86/Makefile
|
||||||
|
+++ b/libavcodec/x86/Makefile
|
||||||
|
@@ -158,6 +158,8 @@ X86ASM-OBJS-$(CONFIG_ALAC_DECODER) += x86/alacdsp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_APNG_DECODER) += x86/pngdsp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_CAVS_DECODER) += x86/cavsidct.o
|
||||||
|
+ifdef ARCH_X86_64
|
||||||
|
X86ASM-OBJS-$(CONFIG_CFHD_ENCODER) += x86/cfhdencdsp.o
|
||||||
|
+endif
|
||||||
|
X86ASM-OBJS-$(CONFIG_CFHD_DECODER) += x86/cfhddsp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_DCA_DECODER) += x86/dcadsp.o x86/synth_filter.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_DIRAC_DECODER) += x86/diracdsp.o \
|
||||||
|
@@ -175,15 +177,21 @@ x86/hevc_sao_10bit.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_JPEG2000_DECODER) += x86/jpeg2000dsp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_LSCR_DECODER) += x86/pngdsp.o
|
||||||
|
+ifdef ARCH_X86_64
|
||||||
|
X86ASM-OBJS-$(CONFIG_MLP_DECODER) += x86/mlpdsp.o
|
||||||
|
+endif
|
||||||
|
X86ASM-OBJS-$(CONFIG_MPEG4_DECODER) += x86/xvididct.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_PNG_DECODER) += x86/pngdsp.o
|
||||||
|
+ifdef ARCH_X86_64
|
||||||
|
X86ASM-OBJS-$(CONFIG_PRORES_DECODER) += x86/proresdsp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_PRORES_LGPL_DECODER) += x86/proresdsp.o
|
||||||
|
+endif
|
||||||
|
X86ASM-OBJS-$(CONFIG_RV40_DECODER) += x86/rv40dsp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_SBC_ENCODER) += x86/sbcdsp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_SVQ1_ENCODER) += x86/svq1enc.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_TAK_DECODER) += x86/takdsp.o
|
||||||
|
+ifdef ARCH_X86_64
|
||||||
|
X86ASM-OBJS-$(CONFIG_TRUEHD_DECODER) += x86/mlpdsp.o
|
||||||
|
+endif
|
||||||
|
X86ASM-OBJS-$(CONFIG_TTA_DECODER) += x86/ttadsp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_TTA_ENCODER) += x86/ttaencdsp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_UTVIDEO_DECODER) += x86/utvideodsp.o
|
||||||
|
diff --git a/libavfilter/x86/Makefile b/libavfilter/x86/Makefile
|
||||||
|
--- a/libavfilter/x86/Makefile
|
||||||
|
+++ b/libavfilter/x86/Makefile
|
||||||
|
@@ -44,6 +44,8 @@
|
||||||
|
X86ASM-OBJS-$(CONFIG_AFIR_FILTER) += x86/af_afir.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_ANLMDN_FILTER) += x86/af_anlmdn.o
|
||||||
|
+ifdef ARCH_X86_64
|
||||||
|
X86ASM-OBJS-$(CONFIG_ATADENOISE_FILTER) += x86/vf_atadenoise.o
|
||||||
|
+endif
|
||||||
|
X86ASM-OBJS-$(CONFIG_BLEND_FILTER) += x86/vf_blend.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_BWDIF_FILTER) += x86/vf_bwdif.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_COLORSPACE_FILTER) += x86/colorspacedsp.o
|
||||||
|
@@ -62,6 +62,8 @@ X86ASM-OBJS-$(CONFIG_LUT3D_FILTER) += x86/vf_lut3d.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_MASKEDCLAMP_FILTER) += x86/vf_maskedclamp.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_MASKEDMERGE_FILTER) += x86/vf_maskedmerge.o
|
||||||
|
+ifdef ARCH_X86_64
|
||||||
|
X86ASM-OBJS-$(CONFIG_NLMEANS_FILTER) += x86/vf_nlmeans.o
|
||||||
|
+endif
|
||||||
|
X86ASM-OBJS-$(CONFIG_OVERLAY_FILTER) += x86/vf_overlay.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_PP7_FILTER) += x86/vf_pp7.o
|
||||||
|
X86ASM-OBJS-$(CONFIG_PSNR_FILTER) += x86/vf_psnr.o
|
||||||
14
res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch
Normal file
14
res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
diff --git a/configure b/configure
|
||||||
|
index 2be953f7e7..e075949ffc 100755
|
||||||
|
--- a/configure
|
||||||
|
+++ b/configure
|
||||||
|
@@ -6497,6 +6497,7 @@ enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0
|
||||||
|
{ enabled gplv3 || ! enabled gpl || enabled nonfree || die "ERROR: OpenSSL >=3.0.0 requires --enable-version3"; }; } ||
|
||||||
|
{ enabled gpl && ! enabled nonfree && die "ERROR: OpenSSL <3.0.0 is incompatible with the gpl"; } ||
|
||||||
|
check_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl ||
|
||||||
|
check_pkg_config openssl openssl openssl/ssl.h SSL_library_init ||
|
||||||
|
+ check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto $pthreads_extralibs -ldl ||
|
||||||
|
check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto ||
|
||||||
|
check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto ||
|
||||||
|
check_lib openssl openssl/ssl.h SSL_library_init -lssl32 -leay32 ||
|
||||||
|
|
||||||
15
res/vcpkg/ffmpeg/0013-define-WINVER.patch
Normal file
15
res/vcpkg/ffmpeg/0013-define-WINVER.patch
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
diff --color -Naur src_old/libavcodec/mf_utils.c src/libavcodec/mf_utils.c
|
||||||
|
--- src_old/libavcodec/mf_utils.c 2020-07-11 05:26:17.000000000 +0700
|
||||||
|
+++ src/libavcodec/mf_utils.c 2020-11-13 12:55:57.226976400 +0700
|
||||||
|
@@ -22,6 +22,11 @@
|
||||||
|
#define _WIN32_WINNT 0x0602
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#if !defined(WINVER) || WINVER < 0x0602
|
||||||
|
+#undef WINVER
|
||||||
|
+#define WINVER 0x0602
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
#include "mf_utils.h"
|
||||||
|
#include "libavutil/pixdesc.h"
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
From f0b694749b38b2cfd94df4eed10e667342c234e5 Mon Sep 17 00:00:00 2001
|
||||||
|
From: 21pages <pages21@163.com>
|
||||||
|
Date: Sat, 24 Feb 2024 15:33:24 +0800
|
||||||
|
Subject: [PATCH 1/2] avcodec/amfenc: add query_timeout option for h264/hevc
|
||||||
|
|
||||||
|
Signed-off-by: 21pages <pages21@163.com>
|
||||||
|
---
|
||||||
|
libavcodec/amfenc.h | 1 +
|
||||||
|
libavcodec/amfenc_h264.c | 4 ++++
|
||||||
|
libavcodec/amfenc_hevc.c | 4 ++++
|
||||||
|
3 files changed, 9 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
|
||||||
|
index 1ab98d2f78..e92120ea39 100644
|
||||||
|
--- a/libavcodec/amfenc.h
|
||||||
|
+++ b/libavcodec/amfenc.h
|
||||||
|
@@ -87,6 +87,7 @@ typedef struct AmfContext {
|
||||||
|
int quality;
|
||||||
|
int b_frame_delta_qp;
|
||||||
|
int ref_b_frame_delta_qp;
|
||||||
|
+ int64_t query_timeout;
|
||||||
|
|
||||||
|
// Dynamic options, can be set after Init() call
|
||||||
|
|
||||||
|
diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c
|
||||||
|
index efb04589f6..f55dbc80f0 100644
|
||||||
|
--- a/libavcodec/amfenc_h264.c
|
||||||
|
+++ b/libavcodec/amfenc_h264.c
|
||||||
|
@@ -121,6 +121,7 @@ static const AVOption options[] = {
|
||||||
|
{ "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
|
||||||
|
|
||||||
|
{ "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg) , AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE },
|
||||||
|
+ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE },
|
||||||
|
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
@@ -155,6 +156,9 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx)
|
||||||
|
|
||||||
|
AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_FRAMERATE, framerate);
|
||||||
|
|
||||||
|
+ if (ctx->query_timeout >= 0)
|
||||||
|
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QUERY_TIMEOUT, ctx->query_timeout);
|
||||||
|
+
|
||||||
|
switch (avctx->profile) {
|
||||||
|
case FF_PROFILE_H264_BASELINE:
|
||||||
|
profile = AMF_VIDEO_ENCODER_PROFILE_BASELINE;
|
||||||
|
diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c
|
||||||
|
index 8ab9330730..7a40bcad31 100644
|
||||||
|
--- a/libavcodec/amfenc_hevc.c
|
||||||
|
+++ b/libavcodec/amfenc_hevc.c
|
||||||
|
@@ -89,6 +89,7 @@ static const AVOption options[] = {
|
||||||
|
{ "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE },
|
||||||
|
|
||||||
|
{ "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE },
|
||||||
|
+ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -122,6 +123,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx)
|
||||||
|
|
||||||
|
AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_FRAMERATE, framerate);
|
||||||
|
|
||||||
|
+ if (ctx->query_timeout >= 0)
|
||||||
|
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, ctx->query_timeout);
|
||||||
|
+
|
||||||
|
switch (avctx->profile) {
|
||||||
|
case FF_PROFILE_HEVC_MAIN:
|
||||||
|
profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN;
|
||||||
|
--
|
||||||
|
2.43.0.windows.1
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
From 4d0d20d96ad458cfec0444b9be0182ca6085ee0c Mon Sep 17 00:00:00 2001
|
||||||
|
From: 21pages <pages21@163.com>
|
||||||
|
Date: Sat, 24 Feb 2024 16:02:44 +0800
|
||||||
|
Subject: [PATCH 2/2] libavcodec/amfenc: reconfig when bitrate change
|
||||||
|
|
||||||
|
Signed-off-by: 21pages <pages21@163.com>
|
||||||
|
---
|
||||||
|
libavcodec/amfenc.c | 20 ++++++++++++++++++++
|
||||||
|
libavcodec/amfenc.h | 1 +
|
||||||
|
2 files changed, 21 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
|
||||||
|
index a033e1220e..3eab01a903 100644
|
||||||
|
--- a/libavcodec/amfenc.c
|
||||||
|
+++ b/libavcodec/amfenc.c
|
||||||
|
@@ -222,6 +222,7 @@ static int amf_init_context(AVCodecContext *avctx)
|
||||||
|
|
||||||
|
ctx->hwsurfaces_in_queue = 0;
|
||||||
|
ctx->hwsurfaces_in_queue_max = 16;
|
||||||
|
+ ctx->av_bitrate = avctx->bit_rate;
|
||||||
|
|
||||||
|
// configure AMF logger
|
||||||
|
// the return of these functions indicates old state and do not affect behaviour
|
||||||
|
@@ -575,6 +576,23 @@ static void amf_release_buffer_with_frame_ref(AMFBuffer *frame_ref_storage_buffe
|
||||||
|
frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int reconfig_encoder(AVCodecContext *avctx)
|
||||||
|
+{
|
||||||
|
+ AmfContext *ctx = avctx->priv_data;
|
||||||
|
+ AMF_RESULT res = AMF_OK;
|
||||||
|
+
|
||||||
|
+ if (ctx->av_bitrate != avctx->bit_rate) {
|
||||||
|
+ av_log(ctx, AV_LOG_INFO, "change bitrate from %d to %d\n", ctx->av_bitrate, avctx->bit_rate);
|
||||||
|
+ ctx->av_bitrate = avctx->bit_rate;
|
||||||
|
+ if (avctx->codec->id == AV_CODEC_ID_H264) {
|
||||||
|
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_TARGET_BITRATE, avctx->bit_rate);
|
||||||
|
+ } else if (avctx->codec->id == AV_CODEC_ID_HEVC) {
|
||||||
|
+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, avctx->bit_rate);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
|
||||||
|
{
|
||||||
|
AmfContext *ctx = avctx->priv_data;
|
||||||
|
@@ -586,6 +604,8 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
|
||||||
|
AVFrame *frame = ctx->delayed_frame;
|
||||||
|
int block_and_wait;
|
||||||
|
|
||||||
|
+ reconfig_encoder(avctx);
|
||||||
|
+
|
||||||
|
if (!ctx->encoder)
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
|
||||||
|
diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
|
||||||
|
index e92120ea39..31172645f2 100644
|
||||||
|
--- a/libavcodec/amfenc.h
|
||||||
|
+++ b/libavcodec/amfenc.h
|
||||||
|
@@ -107,6 +107,7 @@ typedef struct AmfContext {
|
||||||
|
int me_half_pel;
|
||||||
|
int me_quarter_pel;
|
||||||
|
int aud;
|
||||||
|
+ int64_t av_bitrate;
|
||||||
|
|
||||||
|
// HEVC - specific options
|
||||||
|
|
||||||
|
--
|
||||||
|
2.43.0.windows.1
|
||||||
|
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
From afe89a70f6bc7ebd0a6a0a31101801b88cbd60ee Mon Sep 17 00:00:00 2001
|
||||||
|
From: 21pages <pages21@163.com>
|
||||||
|
Date: Sun, 5 May 2024 12:45:23 +0800
|
||||||
|
Subject: [PATCH] use release/7.0's update_bitrate
|
||||||
|
|
||||||
|
Signed-off-by: 21pages <pages21@163.com>
|
||||||
|
---
|
||||||
|
libavcodec/qsvenc.c | 39 +++++++++++++++++++++++++++++++++++++++
|
||||||
|
libavcodec/qsvenc.h | 6 ++++++
|
||||||
|
2 files changed, 45 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c
|
||||||
|
index 2382c2f5f7..9b34f37eb3 100644
|
||||||
|
--- a/libavcodec/qsvenc.c
|
||||||
|
+++ b/libavcodec/qsvenc.c
|
||||||
|
@@ -714,6 +714,11 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
|
||||||
|
brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes),
|
||||||
|
initial_delay_in_kilobytes) + 0x10000) / 0x10000;
|
||||||
|
|
||||||
|
+ q->old_rc_buffer_size = avctx->rc_buffer_size;
|
||||||
|
+ q->old_rc_initial_buffer_occupancy = avctx->rc_initial_buffer_occupancy;
|
||||||
|
+ q->old_bit_rate = avctx->bit_rate;
|
||||||
|
+ q->old_rc_max_rate = avctx->rc_max_rate;
|
||||||
|
+
|
||||||
|
switch (q->param.mfx.RateControlMethod) {
|
||||||
|
case MFX_RATECONTROL_CBR:
|
||||||
|
case MFX_RATECONTROL_VBR:
|
||||||
|
@@ -1657,6 +1662,39 @@ static int update_qp(AVCodecContext *avctx, QSVEncContext *q,
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int update_bitrate(AVCodecContext *avctx, QSVEncContext *q)
|
||||||
|
+{
|
||||||
|
+ int updated = 0;
|
||||||
|
+ int target_bitrate_kbps, max_bitrate_kbps, brc_param_multiplier;
|
||||||
|
+ int buffer_size_in_kilobytes, initial_delay_in_kilobytes;
|
||||||
|
+
|
||||||
|
+ UPDATE_PARAM(q->old_rc_buffer_size, avctx->rc_buffer_size);
|
||||||
|
+ UPDATE_PARAM(q->old_rc_initial_buffer_occupancy, avctx->rc_initial_buffer_occupancy);
|
||||||
|
+ UPDATE_PARAM(q->old_bit_rate, avctx->bit_rate);
|
||||||
|
+ UPDATE_PARAM(q->old_rc_max_rate, avctx->rc_max_rate);
|
||||||
|
+ if (!updated)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ buffer_size_in_kilobytes = avctx->rc_buffer_size / 8000;
|
||||||
|
+ initial_delay_in_kilobytes = avctx->rc_initial_buffer_occupancy / 8000;
|
||||||
|
+ target_bitrate_kbps = avctx->bit_rate / 1000;
|
||||||
|
+ max_bitrate_kbps = avctx->rc_max_rate / 1000;
|
||||||
|
+ brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes),
|
||||||
|
+ initial_delay_in_kilobytes) + 0x10000) / 0x10000;
|
||||||
|
+
|
||||||
|
+ q->param.mfx.BufferSizeInKB = buffer_size_in_kilobytes / brc_param_multiplier;
|
||||||
|
+ q->param.mfx.InitialDelayInKB = initial_delay_in_kilobytes / brc_param_multiplier;
|
||||||
|
+ q->param.mfx.TargetKbps = target_bitrate_kbps / brc_param_multiplier;
|
||||||
|
+ q->param.mfx.MaxKbps = max_bitrate_kbps / brc_param_multiplier;
|
||||||
|
+ q->param.mfx.BRCParamMultiplier = brc_param_multiplier;
|
||||||
|
+ av_log(avctx, AV_LOG_VERBOSE,
|
||||||
|
+ "Reset BufferSizeInKB: %d; InitialDelayInKB: %d; "
|
||||||
|
+ "TargetKbps: %d; MaxKbps: %d; BRCParamMultiplier: %d\n",
|
||||||
|
+ q->param.mfx.BufferSizeInKB, q->param.mfx.InitialDelayInKB,
|
||||||
|
+ q->param.mfx.TargetKbps, q->param.mfx.MaxKbps, q->param.mfx.BRCParamMultiplier);
|
||||||
|
+ return updated;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int update_parameters(AVCodecContext *avctx, QSVEncContext *q,
|
||||||
|
const AVFrame *frame)
|
||||||
|
{
|
||||||
|
@@ -1666,6 +1704,7 @@ static int update_parameters(AVCodecContext *avctx, QSVEncContext *q,
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
needReset = update_qp(avctx, q, frame);
|
||||||
|
+ needReset |= update_bitrate(avctx, q);
|
||||||
|
if (!needReset)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h
|
||||||
|
index b754ac4b56..5745533165 100644
|
||||||
|
--- a/libavcodec/qsvenc.h
|
||||||
|
+++ b/libavcodec/qsvenc.h
|
||||||
|
@@ -224,6 +224,12 @@ typedef struct QSVEncContext {
|
||||||
|
int min_qp_p;
|
||||||
|
int max_qp_b;
|
||||||
|
int min_qp_b;
|
||||||
|
+
|
||||||
|
+ // These are used for bitrate control reset
|
||||||
|
+ int old_bit_rate;
|
||||||
|
+ int old_rc_buffer_size;
|
||||||
|
+ int old_rc_initial_buffer_occupancy;
|
||||||
|
+ int old_rc_max_rate;
|
||||||
|
} QSVEncContext;
|
||||||
|
|
||||||
|
int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q);
|
||||||
|
--
|
||||||
|
2.43.0.windows.1
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user