mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
Compare commits
76 Commits
2.0.0-beta
...
2.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d9cfc79cf | ||
|
|
5624a44104 | ||
|
|
2aaa6371ab | ||
|
|
fdc1b0c07d | ||
|
|
8ec7e416ea | ||
|
|
ae7730fb35 | ||
|
|
f99e3e9147 | ||
|
|
5fcda04544 | ||
|
|
aca0fbb1e3 | ||
|
|
4f8ffbf225 | ||
|
|
8b08edf38f | ||
|
|
e3d43111e8 | ||
|
|
6bb9a33a04 | ||
|
|
ca64880a01 | ||
|
|
c8d70e2a5a | ||
|
|
cee7eb8928 | ||
|
|
abc5f203d0 | ||
|
|
c3fc8f8e12 | ||
|
|
afd4f8baa9 | ||
|
|
b5ca8b988c | ||
|
|
89b22ba984 | ||
|
|
f329ef60df | ||
|
|
8acfdb8bca | ||
|
|
a7aec52f2a | ||
|
|
7f1317a9a7 | ||
|
|
a8a1fea91b | ||
|
|
675ad4608a | ||
|
|
72ba3757e2 | ||
|
|
c58e84d2ae | ||
|
|
18a7a5059b | ||
|
|
f0102b6f13 | ||
|
|
0cf8eb3c17 | ||
|
|
c08a9f2b18 | ||
|
|
728f1f2802 | ||
|
|
7310211fba | ||
|
|
1f3267de0a | ||
|
|
8ddad59c70 | ||
|
|
9ff6d0afa1 | ||
|
|
2341b09f81 | ||
|
|
5830aa937a | ||
|
|
56a9361e86 | ||
|
|
5868aa4d2f | ||
|
|
45135b7299 | ||
|
|
a0020fede1 | ||
|
|
6f1eaab456 | ||
|
|
6173eae772 | ||
|
|
0bb366b1f7 | ||
|
|
9a4d6f7f4d | ||
|
|
a4ae11e301 | ||
|
|
5af0acb619 | ||
|
|
042434b8f8 | ||
|
|
eddc7ef0c6 | ||
|
|
c96ca2d424 | ||
|
|
45be9008fd | ||
|
|
057da4e297 | ||
|
|
e4e41667ff | ||
|
|
95ca0a4af7 | ||
|
|
702dee7983 | ||
|
|
165d544448 | ||
|
|
ecf61bedc4 | ||
|
|
66a81a5da3 | ||
|
|
574c816ebb | ||
|
|
e7cafb74e4 | ||
|
|
5050aa37f5 | ||
|
|
53d3d96a06 | ||
|
|
d40b8a4c9c | ||
|
|
728671509c | ||
|
|
b7178a30fb | ||
|
|
939d6a1fd7 | ||
|
|
2986a9cc46 | ||
|
|
f36afaf5d3 | ||
|
|
8cec835583 | ||
|
|
a32838dad6 | ||
|
|
d54671757e | ||
|
|
d1dba56bcd | ||
|
|
919c06779d |
52
.github/workflows/linux-aarch64.yml
vendored
52
.github/workflows/linux-aarch64.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: Linux aarch64
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# download jdk
|
||||
- run: wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-linux-aarch64-b1034.51.tar.gz
|
||||
|
||||
# appimagetool
|
||||
- run: sudo apt install libfuse2
|
||||
|
||||
# install jdk
|
||||
- name: Installing Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'jdkfile'
|
||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
||||
java-version: '21.0.7'
|
||||
architecture: aarch64
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||
|
||||
# test build
|
||||
- run: |
|
||||
./gradlew classes -x test --no-daemon
|
||||
./gradlew clean --no-daemon
|
||||
|
||||
# dist
|
||||
- run: |
|
||||
./gradlew dist --no-daemon
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-linux-aarch64
|
||||
path: |
|
||||
build/distributions/*.tar.gz
|
||||
build/distributions/*.AppImage
|
||||
52
.github/workflows/linux-x86-64.yml
vendored
52
.github/workflows/linux-x86-64.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: Linux x86-64
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# download jdk
|
||||
- run: wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-linux-x64-b1034.51.tar.gz
|
||||
|
||||
# appimagetool
|
||||
- run: sudo apt install libfuse2
|
||||
|
||||
# install jdk
|
||||
- name: Installing Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'jdkfile'
|
||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
||||
java-version: '21.0.7'
|
||||
architecture: x64
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||
|
||||
# test build
|
||||
- run: |
|
||||
./gradlew classes -x test --no-daemon
|
||||
./gradlew clean --no-daemon
|
||||
|
||||
# dist
|
||||
- run: |
|
||||
./gradlew dist --no-daemon
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-linux-x86-64
|
||||
path: |
|
||||
build/distributions/*.tar.gz
|
||||
build/distributions/*.AppImage
|
||||
81
.github/workflows/linux.yml
vendored
Normal file
81
.github/workflows/linux.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Linux
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
env:
|
||||
JBR_MAJOR: 21.0.7
|
||||
JBR_PATCH: b1038.58
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-24.04-arm, ubuntu-latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-gradlexyz-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-gradlexyz-
|
||||
|
||||
- name: Set dynamic DOCKER_NAME
|
||||
run: |
|
||||
echo "DOCKER_NAME=hstyi/jbr:${{ env.JBR_MAJOR }}${{ env.JBR_PATCH }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Create docker-run.sh helper script
|
||||
shell: bash
|
||||
run: |
|
||||
cat <<'EOF' > docker-run.sh
|
||||
#!/bin/bash
|
||||
docker run --rm -v $HOME/.gradle:/root/.gradle -v "$(pwd)":/app -w /app "$@"
|
||||
EOF
|
||||
chmod +x docker-run.sh
|
||||
|
||||
- name: Compile
|
||||
shell: bash
|
||||
run: ./docker-run.sh $DOCKER_NAME bash -c './gradlew :check-license && ./gradlew classes -x test'
|
||||
|
||||
- name: JLink
|
||||
shell: bash
|
||||
run: ./docker-run.sh $DOCKER_NAME bash -c './gradlew :jar :copy-dependencies :plugins:migration:build :jlink'
|
||||
|
||||
- name: Package Deb
|
||||
shell: bash
|
||||
run: ./docker-run.sh -e TERMORA_TYPE=deb $DOCKER_NAME bash -c './gradlew :jpackage && ./gradlew :dist'
|
||||
|
||||
- name: Package AppImage
|
||||
shell: bash
|
||||
run: ./docker-run.sh --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined $DOCKER_NAME bash -c 'rm -rf build/jpackage && ./gradlew :jpackage && ./gradlew :dist'
|
||||
|
||||
- name: Make ~/.gradle world-writable
|
||||
shell: bash
|
||||
run: sudo chmod -R 777 ~/.gradle
|
||||
|
||||
- name: Upload targz artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-linux-targz-${{ runner.arch }}
|
||||
path: |
|
||||
build/distributions/*.tar.gz
|
||||
|
||||
- name: Upload AppImage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-linux-AppImage-${{ runner.arch }}
|
||||
path: |
|
||||
build/distributions/*.AppImage
|
||||
|
||||
- name: Upload deb artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-linux-deb-${{ runner.arch }}
|
||||
path: |
|
||||
build/distributions/*.deb
|
||||
89
.github/workflows/osx-aarch64.yml
vendored
89
.github/workflows/osx-aarch64.yml
vendored
@@ -1,89 +0,0 @@
|
||||
name: macOS aarch64
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install the Apple certificate
|
||||
if: github.event_name == 'push' && github.repository == 'TermoraDev/termora'
|
||||
env:
|
||||
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
||||
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
run: |
|
||||
# create variables
|
||||
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||
|
||||
# import certificate from secrets
|
||||
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
|
||||
|
||||
# create temporary keychain
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
|
||||
# import certificate to keychain
|
||||
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
- name: Setup the Notary information
|
||||
if: "startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora'"
|
||||
env:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
STORE_CREDENTIALS: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
||||
run: |
|
||||
xcrun notarytool store-credentials "$STORE_CREDENTIALS" --apple-id "$APPLE_ID" --team-id "$TEAM_ID" --password "$APPLE_PASSWORD"
|
||||
|
||||
# download jdk
|
||||
- run: wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-osx-aarch64-b1034.51.tar.gz
|
||||
|
||||
# install jdk
|
||||
- name: Installing Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'jdkfile'
|
||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
||||
java-version: '21.0.7'
|
||||
architecture: aarch64
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||
|
||||
# test build
|
||||
- run: |
|
||||
./gradlew classes -x test --no-daemon
|
||||
./gradlew clean --no-daemon
|
||||
|
||||
# dist
|
||||
- name: Dist
|
||||
env:
|
||||
TERMORA_MAC_SIGN: ${{ github.event_name == 'push' && github.repository == 'TermoraDev/termora' }}
|
||||
TERMORA_MAC_SIGN_USER_NAME: ${{ secrets.TERMORA_MAC_SIGN_USER_NAME }}
|
||||
# 只有发布版本时才需要公证
|
||||
TERMORA_MAC_NOTARY: "${{ startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora' }}"
|
||||
TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
||||
run: |
|
||||
./gradlew dist --no-daemon
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-osx-aarch64
|
||||
path: |
|
||||
build/distributions/*.zip
|
||||
build/distributions/*.dmg
|
||||
@@ -1,17 +1,29 @@
|
||||
name: macOS x86-64
|
||||
name: macOS
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
env:
|
||||
TERMORA_MAC_SIGN: "${{ github.repository == 'TermoraDev/termora' }}"
|
||||
TERMORA_MAC_SIGN_USER_NAME: ${{ secrets.TERMORA_MAC_SIGN_USER_NAME }}
|
||||
# 只有发布版本时才需要公证
|
||||
TERMORA_MAC_NOTARY: "${{ startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora' }}"
|
||||
TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
||||
JBR_MAJOR: 21.0.7
|
||||
JBR_PATCH: b1038.58
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-13
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macos-15, macos-13 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install the Apple certificate
|
||||
if: github.event_name == 'push' && github.repository == 'TermoraDev/termora'
|
||||
if: ${{ fromJSON(env.TERMORA_MAC_SIGN) && env.BUILD_CERTIFICATE_BASE64 != '' }}
|
||||
env:
|
||||
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
||||
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||||
@@ -34,7 +46,7 @@ jobs:
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
- name: Setup the Notary information
|
||||
if: "startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora'"
|
||||
if: ${{ fromJSON(env.TERMORA_MAC_NOTARY) && env.APPLE_ID != '' }}
|
||||
env:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
@@ -43,8 +55,14 @@ jobs:
|
||||
run: |
|
||||
xcrun notarytool store-credentials "$STORE_CREDENTIALS" --apple-id "$APPLE_ID" --team-id "$TEAM_ID" --password "$APPLE_PASSWORD"
|
||||
|
||||
# download jdk
|
||||
- run: wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-osx-x64-b1034.51.tar.gz
|
||||
- name: Download Java
|
||||
run: |
|
||||
if [[ "$(uname -m)" == "arm64" ]]; then
|
||||
ARCH="aarch64"
|
||||
else
|
||||
ARCH="x64"
|
||||
fi
|
||||
wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-${{ env.JBR_MAJOR }}-osx-$ARCH-${{ env.JBR_PATCH }}.tar.gz
|
||||
|
||||
# install jdk
|
||||
- name: Installing Java
|
||||
@@ -53,8 +71,6 @@ jobs:
|
||||
distribution: 'jdkfile'
|
||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
||||
java-version: '21.0.7'
|
||||
architecture: x64
|
||||
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
@@ -65,27 +81,28 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||
|
||||
- name: Compile
|
||||
shell: bash
|
||||
run: ./gradlew :check-license && ./gradlew classes -x test
|
||||
|
||||
# test build
|
||||
- run: |
|
||||
./gradlew classes -x test --no-daemon
|
||||
./gradlew clean --no-daemon
|
||||
- name: JLink
|
||||
shell: bash
|
||||
run: ./gradlew :jar :copy-dependencies :plugins:migration:build :jlink
|
||||
|
||||
# dist
|
||||
- name: Dist
|
||||
env:
|
||||
TERMORA_MAC_SIGN: ${{ github.event_name == 'push' && github.repository == 'TermoraDev/termora' }}
|
||||
TERMORA_MAC_SIGN_USER_NAME: ${{ secrets.TERMORA_MAC_SIGN_USER_NAME }}
|
||||
# 只有发布版本时才需要公证
|
||||
TERMORA_MAC_NOTARY: "${{ startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora' }}"
|
||||
TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
||||
run: |
|
||||
./gradlew dist --no-daemon
|
||||
- name: Package
|
||||
shell: bash
|
||||
run: ./gradlew :jpackage && ./gradlew :dist
|
||||
|
||||
- name: Upload artifact
|
||||
- name: Upload zip artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-osx-x86-64
|
||||
name: termora-osx-zip-${{ runner.arch }}
|
||||
path: |
|
||||
build/distributions/*.zip
|
||||
|
||||
- name: Upload dmg artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-osx-dmg-${{ runner.arch }}
|
||||
path: |
|
||||
build/distributions/*.dmg
|
||||
53
.github/workflows/windows-x86-64.yml
vendored
53
.github/workflows/windows-x86-64.yml
vendored
@@ -1,53 +0,0 @@
|
||||
name: Windows x86-64
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install zip
|
||||
run: |
|
||||
$system32 = [System.Environment]::GetEnvironmentVariable("WINDIR") + "\System32"
|
||||
Invoke-WebRequest -Uri "http://stahlworks.com/dev/zip.exe" -OutFile "$system32\zip.exe"
|
||||
Invoke-WebRequest -Uri "http://stahlworks.com/dev/unzip.exe" -OutFile "$system32\unzip.exe"
|
||||
|
||||
- name: Install 7z
|
||||
uses: milliewalky/setup-7-zip@v2
|
||||
|
||||
- name: Installing Java
|
||||
run: |
|
||||
curl -s --output ${{ runner.temp }}\java_package.zip -L https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-windows-x64-b1034.51.zip
|
||||
unzip -q ${{ runner.temp }}\java_package.zip -d ${{ runner.temp }}\jbr
|
||||
echo "JAVA_HOME=${{ runner.temp }}\jbr\jbrsdk-21.0.7-windows-x64-b1034.51" >> $env:GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||
|
||||
# test build
|
||||
- run: |
|
||||
.\gradlew classes -x test --no-daemon
|
||||
.\gradlew clean --no-daemon
|
||||
|
||||
# dist
|
||||
- run: |
|
||||
.\gradlew.bat dist --no-daemon
|
||||
.\gradlew.bat --stop
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-windows-x86-64
|
||||
path: |
|
||||
build/distributions/*.zip
|
||||
build/distributions/*.exe
|
||||
116
.github/workflows/windows.yml
vendored
Normal file
116
.github/workflows/windows.yml
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
name: Windows
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
env:
|
||||
JBR_MAJOR: 21.0.7
|
||||
JBR_PATCH: b1038.58
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-11-arm, windows-2022 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup MSbuild
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Set architecture
|
||||
id: set-arch
|
||||
run: |
|
||||
if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") {
|
||||
echo "ARCH=aarch64" >> $env:GITHUB_ENV
|
||||
} else {
|
||||
echo "ARCH=x64" >> $env:GITHUB_ENV
|
||||
}
|
||||
|
||||
- name: Find MakeAppx
|
||||
shell: pwsh
|
||||
run: |
|
||||
$installedRootsKey = "HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots"
|
||||
$kitsRoot = (Get-ItemProperty $installedRootsKey).KitsRoot10
|
||||
$versions = Get-ChildItem -Path $installedRootsKey | Select-Object -ExpandProperty PSChildName
|
||||
$maxVersion = $versions | ForEach-Object { [version]$_ } | Sort-Object -Descending | Select-Object -First 1
|
||||
$arch = if ($env:ARCH -eq "aarch64") { "arm64" } else { "x64" }
|
||||
$makeAppXPath = Join-Path -Path $kitsRoot -ChildPath "bin\$maxVersion\$arch\makeappx.exe"
|
||||
Write-Output "MakeAppx.exe path: $makeAppXPath"
|
||||
|
||||
if (Test-Path $makeAppXPath) {
|
||||
"MAKEAPPX_PATH=$makeAppXPath" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
} else {
|
||||
Write-Output "MakeAppx.exe not found!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Install zip
|
||||
run: |
|
||||
$system32 = [System.Environment]::GetEnvironmentVariable("WINDIR") + "\System32"
|
||||
Invoke-WebRequest -Uri "http://stahlworks.com/dev/zip.exe" -OutFile "$system32\zip.exe"
|
||||
Invoke-WebRequest -Uri "http://stahlworks.com/dev/unzip.exe" -OutFile "$system32\unzip.exe"
|
||||
|
||||
- name: Install 7z
|
||||
uses: milliewalky/setup-7-zip@v2
|
||||
|
||||
- name: Installing Java
|
||||
run: |
|
||||
$zipPath = "${{ runner.temp }}\java_package.zip"
|
||||
$extractDir = "${{ runner.temp }}\jbr"
|
||||
$url = "https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-${{ env.JBR_MAJOR }}-windows-${{ env.ARCH }}-${{ env.JBR_PATCH }}.zip"
|
||||
curl -s --output $zipPath -L $url
|
||||
unzip -q $zipPath -d $extractDir
|
||||
$jbrDir = Get-ChildItem $extractDir | Select-Object -First 1
|
||||
echo "JAVA_HOME=$($jbrDir.FullName)" >> $env:GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||
|
||||
- name: Compile
|
||||
run: .\gradlew :check-license && .\gradlew classes -x test
|
||||
|
||||
- name: JLink
|
||||
run: .\gradlew :jar :copy-dependencies :plugins:migration:build :jlink
|
||||
|
||||
- name: Package
|
||||
run: .\gradlew :jpackage && .\gradlew :dist
|
||||
|
||||
- name: MSIX
|
||||
env:
|
||||
TERMORA_TYPE: appx
|
||||
run: |
|
||||
.\gradlew --stop
|
||||
.\gradlew :dist
|
||||
|
||||
- name: Stop Gradle
|
||||
run: .\gradlew.bat --stop
|
||||
|
||||
- name: Upload zip artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-windows-zip-${{ runner.arch }}
|
||||
path: |
|
||||
build/distributions/*.zip
|
||||
|
||||
- name: Upload exe artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-windows-exe-${{ runner.arch }}
|
||||
path: |
|
||||
build/distributions/*.exe
|
||||
|
||||
- name: Upload msix artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: termora-windows-msix-${{ runner.arch }}
|
||||
path: |
|
||||
build/distributions/*.msix
|
||||
@@ -90,8 +90,6 @@ Termora is developed using [**Kotlin/JVM**](https://kotlinlang.org/) and partial
|
||||
We recommend using the [JetBrainsRuntime](https://github.com/JetBrains/JetBrainsRuntime) JDK for development.
|
||||
|
||||
- Run locally: `./gradlew :run`
|
||||
- Build for current OS: `./gradlew :dist`
|
||||
|
||||
|
||||
|
||||
## 📄 License
|
||||
|
||||
@@ -88,8 +88,6 @@ Termora 使用 [**Kotlin/JVM**](https://kotlinlang.org/) 开发,支持(正
|
||||
建议使用 [JetBrainsRuntime](https://github.com/JetBrains/JetBrainsRuntime) JDK 运行环境。
|
||||
|
||||
- 本地运行:`./gradlew :run`
|
||||
- 构建当前系统安装包:`./gradlew :dist`
|
||||
|
||||
|
||||
|
||||
## 📄 授权协议
|
||||
|
||||
388
build.gradle.kts
388
build.gradle.kts
@@ -1,3 +1,4 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
import org.gradle.internal.jvm.Jvm
|
||||
import org.gradle.kotlin.dsl.support.uppercaseFirstChar
|
||||
import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
|
||||
@@ -28,7 +29,9 @@ version = rootProject.projectDir.resolve("VERSION").readText().trim()
|
||||
val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem()
|
||||
val arch: ArchitectureInternal = DefaultNativePlatform.getCurrentArchitecture()
|
||||
val appVersion = project.version.toString().split("-")[0]
|
||||
val isDeb = os.isLinux && System.getProperty("type") == "deb"
|
||||
val makeAppx = if (os.isWindows) StringUtils.defaultString(System.getenv("MAKEAPPX_PATH")) else StringUtils.EMPTY
|
||||
val isDeb = os.isLinux && System.getenv("TERMORA_TYPE") == "deb"
|
||||
val isAppx = os.isWindows && makeAppx.isNotBlank() && System.getenv("TERMORA_TYPE") == "appx"
|
||||
|
||||
// macOS 签名信息
|
||||
val macOSSignUsername = System.getenv("TERMORA_MAC_SIGN_USER_NAME") ?: StringUtils.EMPTY
|
||||
@@ -62,9 +65,6 @@ dependencies {
|
||||
testImplementation(libs.h2)
|
||||
testImplementation(libs.exposed.migration)
|
||||
|
||||
// implementation(platform(libs.koin.bom))
|
||||
// implementation(libs.koin.core)
|
||||
|
||||
api(kotlin("reflect"))
|
||||
api(libs.slf4j.api)
|
||||
api(libs.pty4j)
|
||||
@@ -105,7 +105,6 @@ dependencies {
|
||||
|
||||
api(libs.colorpicker)
|
||||
api(libs.mixpanel)
|
||||
api(libs.jSerialComm)
|
||||
api(libs.ini4j)
|
||||
api(libs.restart4j)
|
||||
api(libs.exposed.core)
|
||||
@@ -173,102 +172,145 @@ publishing {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
filesMatching("**/AppxManifest.xml") {
|
||||
filter<ReplaceTokens>(
|
||||
"tokens" to mapOf(
|
||||
"version" to appVersion,
|
||||
"architecture" to if (arch.isArm64) "arm64" else "x64",
|
||||
"projectDir" to project.projectDir.absolutePath,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
@Suppress("CascadeIf")
|
||||
tasks.register<Copy>("copy-dependencies") {
|
||||
val dir = layout.buildDirectory.dir("libs")
|
||||
from(configurations.runtimeClasspath).into(dir)
|
||||
val jna = libs.jna.asProvider().get()
|
||||
val pty4j = libs.pty4j.get()
|
||||
val flatlaf = libs.flatlaf.get()
|
||||
val jSerialComm = libs.jSerialComm.get()
|
||||
val restart4j = libs.restart4j.get()
|
||||
val sqlite = libs.sqlite.get()
|
||||
val archName = if (arch.isArm) "aarch64" else "x86_64"
|
||||
val dylib = dir.get().dir("dylib").asFile
|
||||
|
||||
// 对 JNA 和 PTY4J 的本地库提取
|
||||
// 提取出来是为了单独签名,不然无法通过公证
|
||||
if (os.isMacOsX && macOSSign) {
|
||||
doLast {
|
||||
val archName = if (arch.isArm) "aarch64" else "x86_64"
|
||||
val dylib = dir.get().dir("dylib").asFile
|
||||
for (file in dir.get().asFile.listFiles() ?: emptyArray()) {
|
||||
if ("${jna.name}-${jna.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = File(dylib, jna.name)
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
doLast {
|
||||
for (file in dir.get().asFile.listFiles() ?: emptyArray()) {
|
||||
if ("${jna.name}-${jna.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = File(dylib, jna.name)
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
if (os.isWindows) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/win32-${arch.name}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
} else if (os.isLinux) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/linux-${arch.name}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
} else if (os.isMacOsX) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/darwin-${arch.name}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
// 删除所有二进制类库
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/darwin-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/sunos-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/openbsd-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/linux-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/freebsd-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/dragonflybsd-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/aix-*") }
|
||||
} else if ("${pty4j.name}-${pty4j.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = FileUtils.getFile(dylib, pty4j.name, "darwin")
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
}
|
||||
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/linux-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/darwin-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/sunos-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/openbsd-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/freebsd-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/dragonflybsd-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/aix-*") }
|
||||
} else if ("${pty4j.name}-${pty4j.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = FileUtils.getFile(dylib, pty4j.name, if (os.isWindows) "win32" else "linux")
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
val myArchName = if (arch.isArm) "aarch64" else "x86-64"
|
||||
if (os.isWindows) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/*win/${myArchName}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
} else if (os.isLinux) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/*linux/${myArchName}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
} else if (os.isMacOsX) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/com/pty4j/native/darwin*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
// 删除所有二进制类库
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*") }
|
||||
} else if ("${jSerialComm.name}-${jSerialComm.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = FileUtils.getFile(dylib, jSerialComm.name, "OSX", archName)
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
}
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*") }
|
||||
} else if ("${restart4j.name}-${restart4j.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = FileUtils.getFile(dylib, restart4j.name)
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
if (os.isWindows) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "OSX/${archName}/*", "-d", targetDir.absolutePath) }
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "win32/${archName}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
// 删除所有二进制类库
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "Android/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "FreeBSD/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "Linux/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "OpenBSD/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "OSX/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "Solaris/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "Windows/*") }
|
||||
} else if ("${restart4j.name}-${restart4j.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = FileUtils.getFile(dylib, restart4j.name)
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
} else if (os.isLinux) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "linux/${archName}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
} else if (os.isMacOsX) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "darwin/${archName}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
// 删除所有二进制类库
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "win32/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "darwin/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "linux/*") }
|
||||
// 设置可执行权限
|
||||
for (e in FileUtils.listFiles(
|
||||
targetDir,
|
||||
FileFilterUtils.trueFileFilter(),
|
||||
FileFilterUtils.falseFileFilter()
|
||||
)) {
|
||||
e.setExecutable(true)
|
||||
}
|
||||
} else if ("${sqlite.name}-${sqlite.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = FileUtils.getFile(dylib, sqlite.name)
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
}
|
||||
// 设置可执行权限
|
||||
for (e in FileUtils.listFiles(
|
||||
targetDir,
|
||||
FileFilterUtils.trueFileFilter(),
|
||||
FileFilterUtils.falseFileFilter()
|
||||
)) e.setExecutable(true)
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "win32/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "darwin/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "linux/*") }
|
||||
} else if ("${sqlite.name}-${sqlite.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = FileUtils.getFile(dylib, sqlite.name)
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
if (os.isWindows) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Windows/${archName}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
} else if (os.isLinux) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Linux/${archName}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
} else if (os.isMacOsX) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Mac/${archName}/*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
// 删除所有二进制类库
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/*") }
|
||||
} else if ("${flatlaf.name}-${flatlaf.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = FileUtils.getFile(dylib, flatlaf.name)
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
val isArm = arch.isArm
|
||||
}
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/*") }
|
||||
} else if ("${flatlaf.name}-${flatlaf.version}" == file.nameWithoutExtension) {
|
||||
val targetDir = FileUtils.getFile(dylib, flatlaf.name)
|
||||
FileUtils.forceMkdir(targetDir)
|
||||
val isArm = arch.isArm
|
||||
if (os.isWindows) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*windows*${if (isArm) "arm64" else "x86_64"}*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
} else if (os.isLinux) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*linux*${if (isArm) "arm64" else "x86_64"}*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
} else if (os.isMacOsX) {
|
||||
// @formatter:off
|
||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*macos*${if (isArm) "arm" else "x86"}*", "-d", targetDir.absolutePath) }
|
||||
// @formatter:on
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*") }
|
||||
}
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*") }
|
||||
}
|
||||
}
|
||||
|
||||
// 对二进制签名
|
||||
// 对二进制签名
|
||||
if (os.isMacOsX) {
|
||||
Files.walk(dylib.toPath()).use { paths ->
|
||||
for (path in paths) {
|
||||
if (Files.isRegularFile(path)) {
|
||||
@@ -277,116 +319,8 @@ tasks.register<Copy>("copy-dependencies") {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (os.isLinux || os.isWindows) { // 缩减安装包
|
||||
doLast {
|
||||
for (file in dir.get().asFile.listFiles() ?: emptyArray()) {
|
||||
if ("${jna.name}-${jna.version}" == file.nameWithoutExtension) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/darwin-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/sunos-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/openbsd-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/freebsd-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/dragonflybsd-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/aix-*") }
|
||||
if (os.isWindows) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/linux-*") }
|
||||
if (arch.isArm) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-x86*") }
|
||||
} else {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-aarch64/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-x86/*") }
|
||||
}
|
||||
} else if (os.isLinux) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-*") }
|
||||
}
|
||||
} else if ("${pty4j.name}-${pty4j.version}" == file.nameWithoutExtension) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*darwin*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*freebsd*") }
|
||||
if (os.isWindows) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*linux*") }
|
||||
if (arch.isArm) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win/x86/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win/x86-64*") }
|
||||
} else {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win/x86/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win/aarch64/*") }
|
||||
}
|
||||
} else if (os.isLinux) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win*") }
|
||||
}
|
||||
} else if ("${jSerialComm.name}-${jSerialComm.version}" == file.nameWithoutExtension) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "Android/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "FreeBSD/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "OpenBSD/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "OSX/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "Solaris/*") }
|
||||
if (os.isWindows) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "Linux/*") }
|
||||
} else if (os.isLinux) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "Windows/*") }
|
||||
}
|
||||
} else if ("${restart4j.name}-${restart4j.version}" == file.nameWithoutExtension) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "darwin/*") }
|
||||
if (os.isWindows) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "linux/*") }
|
||||
if (arch.isArm) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "win32/x86_64/*") }
|
||||
} else {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "win32/aarch64/*") }
|
||||
}
|
||||
} else if (os.isLinux) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "win32/*") }
|
||||
if (arch.isArm) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "linux/x86_64/*") }
|
||||
} else {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "linux/aarch64/*") }
|
||||
}
|
||||
}
|
||||
} else if ("${sqlite.name}-${sqlite.version}" == file.nameWithoutExtension) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux-*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/FreeBSD/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Mac/*") }
|
||||
if (os.isWindows) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/armv7/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/x86/*") }
|
||||
if (arch.isArm) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/x86_64/*") }
|
||||
} else {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/aarch64/*") }
|
||||
}
|
||||
} else if (os.isLinux) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/arm*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/ppc64/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/riscv64/*") }
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/x86/*") }
|
||||
if (arch.isArm) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/x86_64/*") }
|
||||
} else {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/aarch64/*") }
|
||||
}
|
||||
}
|
||||
} else if ("${flatlaf.name}-${flatlaf.version}" == file.nameWithoutExtension) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*macos*") }
|
||||
if (os.isWindows) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*linux*") }
|
||||
if (arch.isArm) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*x86*") }
|
||||
} else {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*x86.dll") }
|
||||
}
|
||||
} else if (os.isLinux) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*windows*") }
|
||||
if (arch.isArm) {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*x86*") }
|
||||
} else {
|
||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*arm*") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tasks.register<Exec>("jlink") {
|
||||
@@ -484,10 +418,6 @@ tasks.register<Exec>("jpackage") {
|
||||
}
|
||||
|
||||
if (os.isWindows) {
|
||||
arguments.add("--win-dir-chooser")
|
||||
arguments.add("--win-shortcut")
|
||||
arguments.add("--win-shortcut-prompt")
|
||||
arguments.addAll(listOf("--win-upgrade-uuid", "E1D93CAD-5BF8-442E-93BA-6E90DE601E4C"))
|
||||
arguments.addAll(listOf("--icon", "${projectDir.absolutePath}/src/main/resources/icons/termora.ico"))
|
||||
}
|
||||
|
||||
@@ -500,7 +430,7 @@ tasks.register<Exec>("jpackage") {
|
||||
if (os.isMacOsX) {
|
||||
arguments.add("dmg")
|
||||
} else if (os.isWindows) {
|
||||
arguments.add("msi")
|
||||
arguments.add("app-image")
|
||||
} else if (os.isLinux) {
|
||||
arguments.add(if (isDeb) "deb" else "app-image")
|
||||
if (isDeb) {
|
||||
@@ -523,31 +453,20 @@ tasks.register<Exec>("jpackage") {
|
||||
|
||||
tasks.register("dist") {
|
||||
doLast {
|
||||
val osName = if (os.isMacOsX) "osx" else if (os.isWindows) "windows" else "linux"
|
||||
val distributionDir = layout.buildDirectory.dir("distributions").get()
|
||||
val finalFilenameWithoutExtension = "${project.name}-${project.version}-${osName}-${arch.name}"
|
||||
val projectName = project.name.uppercaseFirstChar()
|
||||
|
||||
val gradlew = File(projectDir, if (os.isWindows) "gradlew.bat" else "gradlew").absolutePath
|
||||
|
||||
// 清空目录
|
||||
exec { commandLine(gradlew, "clean") }
|
||||
|
||||
// 构建自带的插件
|
||||
exec { commandLine(gradlew, ":plugins:migration:build") }
|
||||
|
||||
// 打包并复制依赖
|
||||
exec {
|
||||
commandLine(gradlew, ":jar", ":copy-dependencies")
|
||||
if (os.isWindows) {
|
||||
packOnWindows(distributionDir, finalFilenameWithoutExtension, projectName)
|
||||
} else if (os.isLinux) {
|
||||
packOnLinux(distributionDir, finalFilenameWithoutExtension, projectName)
|
||||
} else if (os.isMacOsX) {
|
||||
packOnMac(distributionDir, finalFilenameWithoutExtension, projectName)
|
||||
} else {
|
||||
throw GradleException("${os.name} is not supported")
|
||||
}
|
||||
|
||||
// 检查依赖的开源协议
|
||||
exec { commandLine(gradlew, ":check-license") }
|
||||
|
||||
// jlink
|
||||
exec { commandLine(gradlew, ":jlink") }
|
||||
|
||||
// 打包
|
||||
exec { commandLine(gradlew, ":jpackage", "-Dtype=${System.getProperty("type")}") }
|
||||
|
||||
// 根据不同的系统构建不同的二进制包
|
||||
pack()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,35 +497,36 @@ tasks.register("check-license") {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建包
|
||||
*/
|
||||
fun pack() {
|
||||
val osName = if (os.isMacOsX) "osx" else if (os.isWindows) "windows" else "linux"
|
||||
val distributionDir = layout.buildDirectory.dir("distributions").get()
|
||||
val finalFilenameWithoutExtension = "${project.name}-${project.version}-${osName}-${arch.name}"
|
||||
val projectName = project.name.uppercaseFirstChar()
|
||||
|
||||
if (os.isWindows) {
|
||||
packOnWindows(distributionDir, finalFilenameWithoutExtension, projectName)
|
||||
} else if (os.isLinux) {
|
||||
packOnLinux(distributionDir, finalFilenameWithoutExtension, projectName)
|
||||
} else if (os.isMacOsX) {
|
||||
packOnMac(distributionDir, finalFilenameWithoutExtension, projectName)
|
||||
} else {
|
||||
throw GradleException("${os.name} is not supported")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 zip、msi
|
||||
*/
|
||||
fun packOnWindows(distributionDir: Directory, finalFilenameWithoutExtension: String, projectName: String) {
|
||||
val dir = layout.buildDirectory.dir("jpackage/images/win-msi.image/").get().asFile
|
||||
val dir = layout.buildDirectory.dir("distributions").get().asFile
|
||||
val cfg = FileUtils.getFile(dir, projectName, "app", "${projectName}.cfg")
|
||||
val configText = cfg.readText()
|
||||
|
||||
// appx
|
||||
if (isAppx) {
|
||||
cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=appx").toString())
|
||||
val appxManifest = FileUtils.getFile(dir, projectName, "AppxManifest.xml")
|
||||
layout.buildDirectory.file("resources/main/AppxManifest.xml").get().asFile
|
||||
.renameTo(appxManifest)
|
||||
val icons = setOf("termora.png", "termora_44x44.png", "termora_150x150.png")
|
||||
for (file in projectDir.resolve("src/main/resources/icons/").listFiles()) {
|
||||
if (icons.contains(file.name)) {
|
||||
val p = appxManifest.parentFile.resolve("icons/${file.name}")
|
||||
FileUtils.forceMkdirParent(p)
|
||||
file.copyTo(p, true)
|
||||
}
|
||||
}
|
||||
exec {
|
||||
commandLine(makeAppx, "pack", "/d", projectName, "/p", "${finalFilenameWithoutExtension}.msix")
|
||||
workingDir = dir
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// zip
|
||||
cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=zip").toString())
|
||||
exec {
|
||||
@@ -628,21 +548,13 @@ fun packOnWindows(distributionDir: Directory, finalFilenameWithoutExtension: Str
|
||||
"/DMyAppVersion=${appVersion}",
|
||||
"/DMyOutputDir=${distributionDir.asFile.absolutePath}",
|
||||
"/DMySetupIconFile=${FileUtils.getFile(projectDir, "src", "main", "resources", "icons", "termora.ico")}",
|
||||
"/DMySourceDir=${layout.buildDirectory.dir("jpackage/images/win-msi.image/${projectName}").get().asFile}",
|
||||
"/DMyWizardSmallImageFile=${FileUtils.getFile(projectDir, "src", "main", "resources", "icons", "termora_128x128.bmp")}",
|
||||
"/DMySourceDir=${FileUtils.getFile(dir, projectName).absolutePath}",
|
||||
"/F${finalFilenameWithoutExtension}",
|
||||
FileUtils.getFile(projectDir, "src", "main", "resources", "termora.iss")
|
||||
)
|
||||
}
|
||||
|
||||
// msi
|
||||
exec {
|
||||
commandLine(
|
||||
"cmd", "/c", "move",
|
||||
"${projectName}-${appVersion}.msi",
|
||||
"${finalFilenameWithoutExtension}.msi"
|
||||
)
|
||||
workingDir = distributionDir.asFile
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -658,7 +570,7 @@ fun packOnMac(distributionDir: Directory, finalFilenameWithoutExtension: String,
|
||||
// @formatter:on
|
||||
|
||||
// sign dmg
|
||||
if (macOSSign) signMacOSLocalFile(dmgFile)
|
||||
signMacOSLocalFile(dmgFile)
|
||||
|
||||
// 找到 .app
|
||||
val imageFile = layout.buildDirectory.dir("jpackage/images/").get().asFile
|
||||
@@ -671,7 +583,7 @@ fun packOnMac(distributionDir: Directory, finalFilenameWithoutExtension: String,
|
||||
// @formatter:on
|
||||
|
||||
// sign zip
|
||||
if (macOSSign) signMacOSLocalFile(zipFile)
|
||||
signMacOSLocalFile(zipFile)
|
||||
|
||||
// 公证
|
||||
if (macOSNotary) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[versions]
|
||||
kotlin = "2.2.0"
|
||||
slf4j = "2.0.17"
|
||||
pty4j = "0.13.6"
|
||||
pty4j = "0.13.10"
|
||||
tinylog = "2.7.0"
|
||||
kotlinx-coroutines = "1.10.2"
|
||||
flatlaf = "3.6"
|
||||
flatlaf = "3.6.1"
|
||||
kotlinx-serialization-json = "1.9.0"
|
||||
commons-codec = "1.18.0"
|
||||
commons-lang3 = "3.17.0"
|
||||
commons-lang3 = "3.18.0"
|
||||
commons-csv = "1.14.0"
|
||||
commons-net = "3.11.1"
|
||||
commons-text = "1.13.1"
|
||||
@@ -22,9 +22,9 @@ jna = "5.17.0"
|
||||
jSystemThemeDetector = "3.9.1"
|
||||
commons-io = "2.19.0"
|
||||
jbr-api = "17.1.10.1"
|
||||
hutool = "5.8.37"
|
||||
jsch = "0.2.26"
|
||||
okhttp = "4.12.0"
|
||||
hutool = "5.8.39"
|
||||
jsch = "2.27.2"
|
||||
okhttp = "5.1.0"
|
||||
sshj = "0.39.0"
|
||||
sshd-core = "2.15.0"
|
||||
jgit = "7.2.0.202503040940-r"
|
||||
@@ -41,12 +41,12 @@ jSerialComm = "2.11.2"
|
||||
ini4j = "0.5.5-2"
|
||||
restart4j = "0.0.1"
|
||||
eddsa = "0.3.0"
|
||||
exposed = "1.0.0-beta-3"
|
||||
exposed = "1.0.0-beta-4"
|
||||
h2 = "2.3.232"
|
||||
sqlite = "3.50.2.0"
|
||||
jug = "5.1.0"
|
||||
semver4j = "6.0.0"
|
||||
jsvg = "1.4.0"
|
||||
jsvg = "2.0.0"
|
||||
dom4j = "2.2.0"
|
||||
|
||||
[libraries]
|
||||
|
||||
@@ -5,6 +5,7 @@ The files in this catalogue are for public access only. Specific descriptions ar
|
||||
- You may view and study the contents of these files;
|
||||
- You may NOT use them for any commercial purpose;
|
||||
- You may NOT modify, copy, distribute, republish, or use them to create derivative works;
|
||||
- Written permission must be obtained from the author for any use beyond personal viewing.
|
||||
- Written permission must be obtained from the author for any use beyond personal viewing;
|
||||
- If you submit a Pull Request that modifies, supplements, or adds to the files in this directory or its subdirectories, unless otherwise agreed in writing, you agree that the copyright of your contribution is owned by hstyi and may be used and managed under the current license terms.
|
||||
|
||||
All rights reserved.
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
|
||||
project.version = "0.0.4"
|
||||
project.version = "0.0.5"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package app.termora.plugins.bg
|
||||
|
||||
import app.termora.*
|
||||
import app.termora.database.DatabaseManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
@@ -96,9 +95,7 @@ internal class BackgroundManager private constructor() : Disposable, GlassPaneAw
|
||||
return
|
||||
}
|
||||
val body = response.body
|
||||
if (body != null) {
|
||||
tempFile.outputStream().use { IOUtils.copy(body.byteStream(), it) }
|
||||
}
|
||||
tempFile.outputStream().use { IOUtils.copy(body.byteStream(), it) }
|
||||
IOUtils.closeQuietly(body)
|
||||
return@use tempFile
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
}
|
||||
|
||||
|
||||
project.version = "0.0.1"
|
||||
|
||||
project.version = "0.0.2"
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
compileOnly(project(":"))
|
||||
implementation("org.apache.commons:commons-pool2:2.12.1")
|
||||
testImplementation(project(":"))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import org.apache.commons.vfs2.Capability
|
||||
import org.apache.commons.vfs2.FileName
|
||||
import org.apache.commons.vfs2.FileSystem
|
||||
import org.apache.commons.vfs2.FileSystemOptions
|
||||
import org.apache.commons.vfs2.provider.AbstractOriginatingFileProvider
|
||||
|
||||
class FTPFileProvider private constructor() : AbstractOriginatingFileProvider() {
|
||||
|
||||
companion object {
|
||||
val instance by lazy { FTPFileProvider() }
|
||||
val capabilities = listOf(
|
||||
Capability.CREATE,
|
||||
Capability.DELETE,
|
||||
Capability.RENAME,
|
||||
Capability.GET_TYPE,
|
||||
Capability.LIST_CHILDREN,
|
||||
Capability.READ_CONTENT,
|
||||
Capability.URI,
|
||||
Capability.WRITE_CONTENT,
|
||||
Capability.GET_LAST_MODIFIED,
|
||||
Capability.SET_LAST_MODIFIED_FILE,
|
||||
Capability.RANDOM_ACCESS_READ,
|
||||
Capability.APPEND_CONTENT
|
||||
)
|
||||
}
|
||||
|
||||
override fun getCapabilities(): Collection<Capability> {
|
||||
return FTPFileProvider.capabilities
|
||||
}
|
||||
|
||||
override fun doCreateFileSystem(
|
||||
rootFileName: FileName,
|
||||
fileSystemOptions: FileSystemOptions
|
||||
): FileSystem? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import app.termora.transfer.s3.S3FileSystem
|
||||
import app.termora.transfer.s3.S3Path
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.apache.commons.net.ftp.FTPClient
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool
|
||||
|
||||
class FTPFileSystem(private val pool: GenericObjectPool<FTPClient>) : S3FileSystem(FTPSystemProvider(pool)) {
|
||||
|
||||
override fun create(root: String?, names: List<String>): S3Path {
|
||||
val path = FTPPath(this, root, names)
|
||||
if (names.isEmpty()) {
|
||||
path.attributes = path.attributes.copy(directory = true)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
IOUtils.closeQuietly(pool)
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import app.termora.*
|
||||
import app.termora.keymgr.KeyManager
|
||||
import app.termora.plugin.internal.BasicProxyOption
|
||||
import com.formdev.flatlaf.FlatClientProperties
|
||||
import com.formdev.flatlaf.extras.components.FlatComboBox
|
||||
import com.formdev.flatlaf.ui.FlatTextBorder
|
||||
import com.jgoodies.forms.builder.FormBuilder
|
||||
import com.jgoodies.forms.layout.FormLayout
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Component
|
||||
import java.awt.KeyboardFocusManager
|
||||
import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import java.awt.event.ItemEvent
|
||||
import java.nio.charset.Charset
|
||||
import javax.swing.*
|
||||
|
||||
class FTPHostOptionsPane : OptionsPane() {
|
||||
private val generalOption = GeneralOption()
|
||||
private val proxyOption = BasicProxyOption(authenticationTypes = listOf())
|
||||
private val sftpOption = SFTPOption()
|
||||
|
||||
init {
|
||||
addOption(generalOption)
|
||||
addOption(proxyOption)
|
||||
addOption(sftpOption)
|
||||
|
||||
}
|
||||
|
||||
fun getHost(): Host {
|
||||
val name = generalOption.nameTextField.text
|
||||
val protocol = FTPProtocolProvider.PROTOCOL
|
||||
val port = generalOption.portTextField.value as Int
|
||||
var authentication = Authentication.Companion.No
|
||||
var proxy = Proxy.Companion.No
|
||||
val authenticationType = AuthenticationType.Password
|
||||
|
||||
authentication = authentication.copy(
|
||||
type = authenticationType,
|
||||
password = String(generalOption.passwordTextField.password)
|
||||
)
|
||||
|
||||
|
||||
if (proxyOption.proxyTypeComboBox.selectedItem != ProxyType.No) {
|
||||
proxy = proxy.copy(
|
||||
type = proxyOption.proxyTypeComboBox.selectedItem as ProxyType,
|
||||
host = proxyOption.proxyHostTextField.text,
|
||||
username = proxyOption.proxyUsernameTextField.text,
|
||||
password = String(proxyOption.proxyPasswordTextField.password),
|
||||
port = proxyOption.proxyPortTextField.value as Int,
|
||||
authenticationType = proxyOption.proxyAuthenticationTypeComboBox.selectedItem as AuthenticationType,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
val options = Options.Default.copy(
|
||||
sftpDefaultDirectory = sftpOption.defaultDirectoryField.text,
|
||||
encoding = sftpOption.charsetComboBox.selectedItem as String,
|
||||
extras = mutableMapOf("passive" to (sftpOption.passiveComboBox.selectedItem as PassiveMode).name)
|
||||
)
|
||||
|
||||
return Host(
|
||||
name = name,
|
||||
protocol = protocol,
|
||||
port = port,
|
||||
host = generalOption.hostTextField.text,
|
||||
username = generalOption.usernameTextField.text,
|
||||
authentication = authentication,
|
||||
proxy = proxy,
|
||||
sort = System.currentTimeMillis(),
|
||||
remark = generalOption.remarkTextArea.text,
|
||||
options = options,
|
||||
)
|
||||
}
|
||||
|
||||
fun setHost(host: Host) {
|
||||
generalOption.nameTextField.text = host.name
|
||||
generalOption.usernameTextField.text = host.username
|
||||
generalOption.remarkTextArea.text = host.remark
|
||||
generalOption.passwordTextField.text = host.authentication.password
|
||||
generalOption.hostTextField.text = host.host
|
||||
generalOption.portTextField.value = host.port
|
||||
|
||||
proxyOption.proxyTypeComboBox.selectedItem = host.proxy.type
|
||||
proxyOption.proxyHostTextField.text = host.proxy.host
|
||||
proxyOption.proxyPasswordTextField.text = host.proxy.password
|
||||
proxyOption.proxyUsernameTextField.text = host.proxy.username
|
||||
proxyOption.proxyPortTextField.value = host.proxy.port
|
||||
proxyOption.proxyAuthenticationTypeComboBox.selectedItem = host.proxy.authenticationType
|
||||
|
||||
|
||||
val passive = host.options.extras["passive"] ?: PassiveMode.Local.name
|
||||
sftpOption.charsetComboBox.selectedItem = host.options.encoding
|
||||
sftpOption.passiveComboBox.selectedItem = runCatching { PassiveMode.valueOf(passive) }
|
||||
.getOrNull() ?: PassiveMode.Local
|
||||
sftpOption.defaultDirectoryField.text = host.options.sftpDefaultDirectory
|
||||
}
|
||||
|
||||
fun validateFields(): Boolean {
|
||||
val host = getHost()
|
||||
|
||||
// general
|
||||
if (validateField(generalOption.nameTextField)) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
if (validateField(generalOption.hostTextField)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(generalOption.usernameTextField.text) || generalOption.passwordTextField.password.isNotEmpty()) {
|
||||
if (validateField(generalOption.usernameTextField)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (validateField(generalOption.passwordTextField)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// proxy
|
||||
if (host.proxy.type != ProxyType.No) {
|
||||
if (validateField(proxyOption.proxyHostTextField)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (host.proxy.authenticationType != AuthenticationType.No) {
|
||||
if (validateField(proxyOption.proxyUsernameTextField)
|
||||
|| validateField(proxyOption.proxyPasswordTextField)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 true 表示有错误
|
||||
*/
|
||||
private fun validateField(textField: JTextField): Boolean {
|
||||
if (textField.isEnabled && (if (textField is JPasswordField) textField.password.isEmpty() else textField.text.isBlank())) {
|
||||
setOutlineError(textField)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun setOutlineError(c: JComponent) {
|
||||
selectOptionJComponent(c)
|
||||
c.putClientProperty(FlatClientProperties.OUTLINE, FlatClientProperties.OUTLINE_ERROR)
|
||||
c.requestFocusInWindow()
|
||||
}
|
||||
|
||||
|
||||
inner class GeneralOption : JPanel(BorderLayout()), Option {
|
||||
val portTextField = PortSpinner(21)
|
||||
val nameTextField = OutlineTextField(128)
|
||||
val usernameTextField = OutlineTextField(128)
|
||||
val hostTextField = OutlineTextField(255)
|
||||
val passwordTextField = OutlinePasswordField(255)
|
||||
val publicKeyComboBox = OutlineComboBox<String>()
|
||||
val remarkTextArea = FixedLengthTextArea(512)
|
||||
val authenticationTypeComboBox = FlatComboBox<AuthenticationType>()
|
||||
|
||||
init {
|
||||
initView()
|
||||
initEvents()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
add(getCenterComponent(), BorderLayout.CENTER)
|
||||
|
||||
publicKeyComboBox.isEditable = false
|
||||
|
||||
publicKeyComboBox.renderer = object : DefaultListCellRenderer() {
|
||||
override fun getListCellRendererComponent(
|
||||
list: JList<*>?,
|
||||
value: Any?,
|
||||
index: Int,
|
||||
isSelected: Boolean,
|
||||
cellHasFocus: Boolean
|
||||
): Component {
|
||||
var text = StringUtils.EMPTY
|
||||
if (value is String) {
|
||||
text = KeyManager.getInstance().getOhKeyPair(value)?.name ?: text
|
||||
}
|
||||
return super.getListCellRendererComponent(
|
||||
list,
|
||||
text,
|
||||
index,
|
||||
isSelected,
|
||||
cellHasFocus
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
authenticationTypeComboBox.renderer = object : DefaultListCellRenderer() {
|
||||
override fun getListCellRendererComponent(
|
||||
list: JList<*>?,
|
||||
value: Any?,
|
||||
index: Int,
|
||||
isSelected: Boolean,
|
||||
cellHasFocus: Boolean
|
||||
): Component {
|
||||
var text = value?.toString() ?: ""
|
||||
when (value) {
|
||||
AuthenticationType.Password -> {
|
||||
text = "Password"
|
||||
}
|
||||
|
||||
AuthenticationType.PublicKey -> {
|
||||
text = "Public Key"
|
||||
}
|
||||
|
||||
AuthenticationType.KeyboardInteractive -> {
|
||||
text = "Keyboard Interactive"
|
||||
}
|
||||
}
|
||||
return super.getListCellRendererComponent(
|
||||
list,
|
||||
text,
|
||||
index,
|
||||
isSelected,
|
||||
cellHasFocus
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
authenticationTypeComboBox.addItem(AuthenticationType.No)
|
||||
authenticationTypeComboBox.addItem(AuthenticationType.Password)
|
||||
|
||||
authenticationTypeComboBox.selectedItem = AuthenticationType.Password
|
||||
|
||||
}
|
||||
|
||||
private fun initEvents() {
|
||||
addComponentListener(object : ComponentAdapter() {
|
||||
override fun componentResized(e: ComponentEvent) {
|
||||
SwingUtilities.invokeLater { nameTextField.requestFocusInWindow() }
|
||||
removeComponentListener(this)
|
||||
}
|
||||
})
|
||||
|
||||
authenticationTypeComboBox.addItemListener {
|
||||
if (it.stateChange == ItemEvent.SELECTED) {
|
||||
passwordTextField.isEnabled = authenticationTypeComboBox.selectedItem == AuthenticationType.Password
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIcon(isSelected: Boolean): Icon {
|
||||
return Icons.settings
|
||||
}
|
||||
|
||||
override fun getTitle(): String {
|
||||
return I18n.getString("termora.new-host.general")
|
||||
}
|
||||
|
||||
override fun getJComponent(): JComponent {
|
||||
return this
|
||||
}
|
||||
|
||||
private fun getCenterComponent(): JComponent {
|
||||
val layout = FormLayout(
|
||||
"left:pref, $FORM_MARGIN, default:grow, $FORM_MARGIN, pref, $FORM_MARGIN, default",
|
||||
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||
)
|
||||
remarkTextArea.setFocusTraversalKeys(
|
||||
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)
|
||||
)
|
||||
remarkTextArea.setFocusTraversalKeys(
|
||||
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||
.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)
|
||||
)
|
||||
|
||||
remarkTextArea.rows = 8
|
||||
remarkTextArea.lineWrap = true
|
||||
remarkTextArea.border = BorderFactory.createEmptyBorder(4, 4, 4, 4)
|
||||
|
||||
|
||||
var rows = 1
|
||||
val step = 2
|
||||
val panel = FormBuilder.create().layout(layout)
|
||||
.add("${I18n.getString("termora.new-host.general.name")}:").xy(1, rows)
|
||||
.add(nameTextField).xyw(3, rows, 5).apply { rows += step }
|
||||
|
||||
.add("${I18n.getString("termora.new-host.general.host")}:").xy(1, rows)
|
||||
.add(hostTextField).xy(3, rows)
|
||||
.add("${I18n.getString("termora.new-host.general.port")}:").xy(5, rows)
|
||||
.add(portTextField).xy(7, rows).apply { rows += step }
|
||||
|
||||
.add("${I18n.getString("termora.new-host.general.username")}:").xy(1, rows)
|
||||
.add(usernameTextField).xyw(3, rows, 5).apply { rows += step }
|
||||
|
||||
.add("${I18n.getString("termora.new-host.general.authentication")}:").xy(1, rows)
|
||||
.add(authenticationTypeComboBox).xyw(3, rows, 5).apply { rows += step }
|
||||
|
||||
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
|
||||
.add(passwordTextField).xyw(3, rows, 5).apply { rows += step }
|
||||
|
||||
.add("${I18n.getString("termora.new-host.general.remark")}:").xy(1, rows)
|
||||
.add(JScrollPane(remarkTextArea).apply { border = FlatTextBorder() })
|
||||
.xyw(3, rows, 5).apply { rows += step }
|
||||
|
||||
.build()
|
||||
|
||||
|
||||
return panel
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private inner class SFTPOption : JPanel(BorderLayout()), Option {
|
||||
val defaultDirectoryField = OutlineTextField(255)
|
||||
val charsetComboBox = JComboBox<String>()
|
||||
val passiveComboBox = JComboBox<PassiveMode>()
|
||||
|
||||
|
||||
init {
|
||||
initView()
|
||||
initEvents()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
|
||||
for (e in Charset.availableCharsets()) {
|
||||
charsetComboBox.addItem(e.key)
|
||||
}
|
||||
|
||||
charsetComboBox.selectedItem = "UTF-8"
|
||||
|
||||
passiveComboBox.addItem(PassiveMode.Local)
|
||||
passiveComboBox.addItem(PassiveMode.Remote)
|
||||
|
||||
add(getCenterComponent(), BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
private fun initEvents() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun getIcon(isSelected: Boolean): Icon {
|
||||
return Icons.folder
|
||||
}
|
||||
|
||||
override fun getTitle(): String {
|
||||
return I18n.getString("termora.transport.sftp")
|
||||
}
|
||||
|
||||
override fun getJComponent(): JComponent {
|
||||
return this
|
||||
}
|
||||
|
||||
private fun getCenterComponent(): JComponent {
|
||||
val layout = FormLayout(
|
||||
"left:pref, $FORM_MARGIN, default:grow",
|
||||
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||
)
|
||||
|
||||
var rows = 1
|
||||
val step = 2
|
||||
val panel = FormBuilder.create().layout(layout)
|
||||
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
|
||||
.add(charsetComboBox).xy(3, rows).apply { rows += step }
|
||||
.add("${FTPI18n.getString("termora.plugins.ftp.passive")}:").xy(1, rows)
|
||||
.add(passiveComboBox).xy(3, rows).apply { rows += step }
|
||||
.add("${I18n.getString("termora.settings.sftp.default-directory")}:").xy(1, rows)
|
||||
.add(defaultDirectoryField).xy(3, rows).apply { rows += step }
|
||||
.build()
|
||||
|
||||
|
||||
return panel
|
||||
}
|
||||
}
|
||||
|
||||
enum class PassiveMode {
|
||||
Local,
|
||||
Remote,
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import app.termora.I18n
|
||||
import app.termora.NamedI18n
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.*
|
||||
|
||||
object FTPI18n : NamedI18n("i18n/messages") {
|
||||
private val log = LoggerFactory.getLogger(FTPI18n::class.java)
|
||||
|
||||
override fun getLogger(): Logger {
|
||||
return log
|
||||
}
|
||||
|
||||
override fun getString(key: String): String {
|
||||
return try {
|
||||
substitutor.replace(getBundle().getString(key))
|
||||
} catch (_: MissingResourceException) {
|
||||
I18n.getString(key)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import app.termora.transfer.s3.S3Path
|
||||
|
||||
class FTPPath(fileSystem: FTPFileSystem, root: String?, names: List<String>) : S3Path(fileSystem, root, names) {
|
||||
override val isBucket: Boolean
|
||||
get() = false
|
||||
|
||||
override val bucketName: String
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
override val objectName: String
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
override fun getCustomType(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import app.termora.DynamicIcon
|
||||
import app.termora.I18n
|
||||
import app.termora.Icons
|
||||
import app.termora.plugin.Extension
|
||||
import app.termora.plugin.ExtensionSupport
|
||||
import app.termora.plugin.PaidPlugin
|
||||
@@ -27,6 +24,7 @@ class FTPPlugin : PaidPlugin {
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun <T : Extension> getExtensions(clazz: Class<T>): List<T> {
|
||||
return support.getExtensions(clazz)
|
||||
}
|
||||
|
||||
@@ -1,22 +1,36 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import app.termora.Disposer
|
||||
import app.termora.Host
|
||||
import app.termora.protocol.ProtocolHostPanel
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.awt.BorderLayout
|
||||
|
||||
class FTPProtocolHostPanel : ProtocolHostPanel() {
|
||||
|
||||
private val pane = FTPHostOptionsPane()
|
||||
|
||||
init {
|
||||
initView()
|
||||
initEvents()
|
||||
}
|
||||
|
||||
|
||||
private fun initView() {
|
||||
add(pane, BorderLayout.CENTER)
|
||||
Disposer.register(this, pane)
|
||||
}
|
||||
|
||||
private fun initEvents() {}
|
||||
|
||||
override fun getHost(): Host {
|
||||
return Host(
|
||||
name = StringUtils.EMPTY,
|
||||
protocol = FTPProtocolProvider.PROTOCOL
|
||||
)
|
||||
return pane.getHost()
|
||||
}
|
||||
|
||||
override fun setHost(host: Host) {
|
||||
|
||||
pane.setHost(host)
|
||||
}
|
||||
|
||||
override fun validateFields(): Boolean {
|
||||
return true
|
||||
return pane.validateFields()
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import app.termora.account.AccountOwner
|
||||
import app.termora.protocol.ProtocolHostPanel
|
||||
import app.termora.protocol.ProtocolHostPanelExtension
|
||||
import app.termora.protocol.ProtocolProvider
|
||||
|
||||
class FTPProtocolHostPanelExtension private constructor() : ProtocolHostPanelExtension {
|
||||
companion object {
|
||||
val instance by lazy { FTPProtocolHostPanelExtension() }
|
||||
val instance = FTPProtocolHostPanelExtension()
|
||||
}
|
||||
|
||||
override fun getProtocolProvider(): ProtocolProvider {
|
||||
return FTPProtocolProvider.instance
|
||||
}
|
||||
|
||||
override fun createProtocolHostPanel(): ProtocolHostPanel {
|
||||
override fun createProtocolHostPanel(accountOwner: AccountOwner): ProtocolHostPanel {
|
||||
return FTPProtocolHostPanel()
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,33 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import app.termora.AuthenticationType
|
||||
import app.termora.DynamicIcon
|
||||
import app.termora.Icons
|
||||
import app.termora.protocol.FileObjectHandler
|
||||
import app.termora.protocol.FileObjectRequest
|
||||
import app.termora.ProxyType
|
||||
import app.termora.protocol.PathHandler
|
||||
import app.termora.protocol.PathHandlerRequest
|
||||
import app.termora.protocol.TransferProtocolProvider
|
||||
import org.apache.commons.vfs2.provider.FileProvider
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.apache.commons.net.ftp.FTPClient
|
||||
import org.apache.commons.pool2.BasePooledObjectFactory
|
||||
import org.apache.commons.pool2.PooledObject
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import java.nio.charset.Charset
|
||||
import java.time.Duration
|
||||
|
||||
|
||||
class FTPProtocolProvider private constructor() : TransferProtocolProvider {
|
||||
|
||||
|
||||
companion object {
|
||||
val instance by lazy { FTPProtocolProvider() }
|
||||
private val log = LoggerFactory.getLogger(FTPProtocolProvider::class.java)
|
||||
|
||||
val instance = FTPProtocolProvider()
|
||||
const val PROTOCOL = "FTP"
|
||||
}
|
||||
|
||||
@@ -22,12 +39,82 @@ class FTPProtocolProvider private constructor() : TransferProtocolProvider {
|
||||
return Icons.ftp
|
||||
}
|
||||
|
||||
override fun getFileProvider(): FileProvider {
|
||||
return FTPFileProvider.instance
|
||||
override fun createPathHandler(requester: PathHandlerRequest): PathHandler {
|
||||
val host = requester.host
|
||||
|
||||
val config = GenericObjectPoolConfig<FTPClient>().apply {
|
||||
maxTotal = 12
|
||||
// 与 transfer 最大传输量匹配
|
||||
maxIdle = 6
|
||||
minIdle = 1
|
||||
testOnBorrow = false
|
||||
testWhileIdle = true
|
||||
// 检测空闲对象线程每次运行时检测的空闲对象的数量
|
||||
timeBetweenEvictionRuns = Duration.ofSeconds(30)
|
||||
// 连接空闲的最小时间,达到此值后空闲链接将会被移除,且保留 minIdle 个空闲连接数
|
||||
softMinEvictableIdleDuration = Duration.ofSeconds(30)
|
||||
// 连接的最小空闲时间,达到此值后该空闲连接可能会被移除(还需看是否已达最大空闲连接数)
|
||||
minEvictableIdleDuration = Duration.ofMinutes(3)
|
||||
}
|
||||
|
||||
val ftpClientPool = GenericObjectPool(object : BasePooledObjectFactory<FTPClient>() {
|
||||
override fun create(): FTPClient {
|
||||
val client = FTPClient()
|
||||
client.charset = Charset.forName(host.options.encoding)
|
||||
client.controlEncoding = client.charset.name()
|
||||
client.connect(host.host, host.port)
|
||||
if (client.isConnected.not()) {
|
||||
throw IllegalStateException("FTP client is not connected")
|
||||
}
|
||||
|
||||
if (host.proxy.type == ProxyType.HTTP) {
|
||||
client.proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress(host.proxy.host, host.proxy.port))
|
||||
} else if (host.proxy.type == ProxyType.SOCKS5) {
|
||||
client.proxy = Proxy(Proxy.Type.SOCKS, InetSocketAddress(host.proxy.host, host.proxy.port))
|
||||
}
|
||||
|
||||
val password = if (host.authentication.type == AuthenticationType.Password)
|
||||
host.authentication.password else StringUtils.EMPTY
|
||||
if (client.login(host.username, password).not()) {
|
||||
throw IllegalStateException("Incorrect account or password")
|
||||
}
|
||||
|
||||
if (host.options.extras["passive"] == FTPHostOptionsPane.PassiveMode.Remote.name) {
|
||||
client.enterRemotePassiveMode()
|
||||
} else {
|
||||
client.enterLocalPassiveMode()
|
||||
}
|
||||
|
||||
client.listHiddenFiles = true
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
override fun wrap(obj: FTPClient): PooledObject<FTPClient> {
|
||||
return DefaultPooledObject(obj)
|
||||
}
|
||||
|
||||
override fun validateObject(p: PooledObject<FTPClient>): Boolean {
|
||||
val ftp = p.`object`
|
||||
return ftp.isConnected.not() && ftp.sendNoOp()
|
||||
}
|
||||
|
||||
override fun destroyObject(p: PooledObject<FTPClient>) {
|
||||
try {
|
||||
p.`object`.disconnect()
|
||||
} catch (e: Exception) {
|
||||
if (log.isWarnEnabled) {
|
||||
log.warn(e.message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, config)
|
||||
|
||||
val defaultPath = host.options.sftpDefaultDirectory
|
||||
val fs = FTPFileSystem(ftpClientPool)
|
||||
return PathHandler(fs, fs.getPath(defaultPath))
|
||||
}
|
||||
|
||||
override fun getRootFileObject(requester: FileObjectRequest): FileObjectHandler {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,10 +5,10 @@ import app.termora.protocol.ProtocolProviderExtension
|
||||
|
||||
class FTPProtocolProviderExtension private constructor() : ProtocolProviderExtension {
|
||||
companion object {
|
||||
val instance by lazy { FTPProtocolProviderExtension() }
|
||||
val instance = FTPProtocolProviderExtension()
|
||||
}
|
||||
|
||||
override fun getProtocolProvider(): ProtocolProvider {
|
||||
return FTPProtocolProvider.Companion.instance
|
||||
return FTPProtocolProvider.instance
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package app.termora.plugins.ftp
|
||||
|
||||
import app.termora.transfer.s3.S3FileSystemProvider
|
||||
import app.termora.transfer.s3.S3Path
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.apache.commons.net.ftp.FTPClient
|
||||
import org.apache.commons.net.ftp.FTPFile
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.nio.file.AccessMode
|
||||
import java.nio.file.CopyOption
|
||||
import java.nio.file.NoSuchFileException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.attribute.FileAttribute
|
||||
import java.nio.file.attribute.PosixFilePermission
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.exists
|
||||
|
||||
class FTPSystemProvider(private val pool: GenericObjectPool<FTPClient>) : S3FileSystemProvider() {
|
||||
|
||||
|
||||
override fun getScheme(): String? {
|
||||
return "ftp"
|
||||
}
|
||||
|
||||
override fun getOutputStream(path: S3Path): OutputStream {
|
||||
return createStreamer(path)
|
||||
}
|
||||
|
||||
override fun getInputStream(path: S3Path): InputStream {
|
||||
val ftp = pool.borrowObject()
|
||||
val fs = ftp.retrieveFileStream(path.absolutePathString())
|
||||
return object : InputStream() {
|
||||
override fun read(): Int {
|
||||
return fs.read()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
IOUtils.closeQuietly(fs)
|
||||
ftp.completePendingCommand()
|
||||
pool.returnObject(ftp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createStreamer(path: S3Path): OutputStream {
|
||||
val ftp = pool.borrowObject()
|
||||
val os = ftp.storeFileStream(path.absolutePathString())
|
||||
return object : OutputStream() {
|
||||
override fun write(b: Int) {
|
||||
os.write(b)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
IOUtils.closeQuietly(os)
|
||||
ftp.completePendingCommand()
|
||||
pool.returnObject(ftp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun fetchChildren(path: S3Path): MutableList<S3Path> {
|
||||
val paths = mutableListOf<S3Path>()
|
||||
if (path.exists().not()) {
|
||||
throw NoSuchFileException(path.absolutePathString())
|
||||
}
|
||||
|
||||
withFtpClient {
|
||||
val files = it.listFiles(path.absolutePathString())
|
||||
for (file in files) {
|
||||
val p = path.resolve(file.name)
|
||||
p.attributes = p.attributes.copy(
|
||||
directory = file.isDirectory,
|
||||
regularFile = file.isFile,
|
||||
size = file.size,
|
||||
lastModifiedTime = file.timestamp.timeInMillis,
|
||||
)
|
||||
p.attributes.permissions = ftpPermissionsToPosix(file)
|
||||
paths.add(p)
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun ftpPermissionsToPosix(file: FTPFile): Set<PosixFilePermission> {
|
||||
val perms = mutableSetOf<PosixFilePermission>()
|
||||
|
||||
if (file.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION))
|
||||
perms.add(PosixFilePermission.OWNER_READ)
|
||||
if (file.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION))
|
||||
perms.add(PosixFilePermission.OWNER_WRITE)
|
||||
if (file.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION))
|
||||
perms.add(PosixFilePermission.OWNER_EXECUTE)
|
||||
|
||||
if (file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION))
|
||||
perms.add(PosixFilePermission.GROUP_READ)
|
||||
if (file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION))
|
||||
perms.add(PosixFilePermission.GROUP_WRITE)
|
||||
if (file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION))
|
||||
perms.add(PosixFilePermission.GROUP_EXECUTE)
|
||||
|
||||
if (file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION))
|
||||
perms.add(PosixFilePermission.OTHERS_READ)
|
||||
if (file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION))
|
||||
perms.add(PosixFilePermission.OTHERS_WRITE)
|
||||
if (file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION))
|
||||
perms.add(PosixFilePermission.OTHERS_EXECUTE)
|
||||
|
||||
return perms
|
||||
}
|
||||
|
||||
override fun createDirectory(dir: Path, vararg attrs: FileAttribute<*>) {
|
||||
withFtpClient { it.mkd(dir.absolutePathString()) }
|
||||
}
|
||||
|
||||
override fun move(source: Path?, target: Path?, vararg options: CopyOption?) {
|
||||
if (source != null && target != null) {
|
||||
withFtpClient {
|
||||
it.rename(source.absolutePathString(), target.absolutePathString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun delete(path: S3Path, isDirectory: Boolean) {
|
||||
withFtpClient {
|
||||
if (isDirectory) {
|
||||
it.rmd(path.absolutePathString())
|
||||
} else {
|
||||
it.deleteFile(path.absolutePathString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkAccess(path: S3Path, vararg modes: AccessMode) {
|
||||
withFtpClient {
|
||||
if (it.cwd(path.absolutePathString()) == 250) {
|
||||
return
|
||||
}
|
||||
if (it.listFiles(path.absolutePathString()).isNotEmpty()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
throw NoSuchFileException(path.absolutePathString())
|
||||
}
|
||||
|
||||
private inline fun <T> withFtpClient(block: (FTPClient) -> T): T {
|
||||
val client = pool.borrowObject()
|
||||
return try {
|
||||
block(client)
|
||||
} finally {
|
||||
pool.returnObject(client)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<descriptions>
|
||||
<description>Connecting to FTP</description>
|
||||
<description language="zh_CN">支持连接到到 FTP</description>
|
||||
<description language="zh_CN">支持连接到 FTP</description>
|
||||
<description language="zh_TW">支援連接到 FTP</description>
|
||||
</descriptions>
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg t="1747213953443" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1523" width="16" height="16"><path d="M851.4776 101.12H170.72239999A80.1984 80.1984 0 0 0 90.61999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.75520001c44.16 0 80.1024-35.9424 80.10239999-80.1216V181.2224c0-44.16-35.9424-80.1024-80.10239999-80.1024zM877.81999999 679.5776c0 14.5344-11.8272 26.3424-26.34239999 26.3424H170.72239999A26.3808 26.3808 0 0 1 144.38 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.34239999-26.3424h680.75520001c14.5152 0 26.3424 11.8272 26.34239999 26.3424v498.3552zM731.9 840.32h-441.60000001a26.88 26.88 0 0 0 0 53.76h441.60000001a26.88 26.88 0 0 0 0-53.76z" p-id="1524" fill="#6C707E"></path><path d="M242.3576 554.72h46.90559999v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H242.3576zM408.51439999 359.1296h65.9328v195.5904h46.92480001V359.1296h66.56639999v-38.9952h-179.424zM703.06159999 320.1344h-77.03039999v234.5664h46.90559999v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44000001 36.4416 0.0192 26.9568-15.5136 40.5888-47.84640001 40.5888z" p-id="1525" fill="#6C707E"></path></svg>
|
||||
<svg t="1751945257078" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1612" width="16" height="16"><path d="M853.97759999 101.12H173.22239999A80.1984 80.1984 0 0 0 93.11999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.7552c44.16 0 80.1024-35.9424 80.1024-80.1216V181.2224c0-44.16-35.9424-80.1024-80.1024-80.1024zM880.31999999 679.5776c0 14.5344-11.8272 26.3424-26.3424 26.3424H173.22239999A26.3808 26.3808 0 0 1 146.87999999 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.3424-26.3424h680.7552c14.5152 0 26.3424 11.8272 26.3424 26.3424v498.3552zM734.39999999 840.32h-441.6a26.88 26.88 0 0 0 0 53.76h441.6a26.88 26.88 0 0 0 0-53.76z" p-id="1613" fill="#6C707E"></path><path d="M244.85759999 554.72h46.9056v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H244.85759999zM411.01439999 359.1296h65.9328v195.5904h46.9248V359.1296h66.5664v-38.9952h-179.424zM705.56159999 320.1344h-77.0304v234.5664h46.9056v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44 36.4416 0.0192 26.9568-15.5136 40.5888-47.8464 40.5888z" p-id="1614" fill="#6C707E"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1 +1 @@
|
||||
<svg t="1747213953443" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1523" width="16" height="16"><path d="M851.4776 101.12H170.72239999A80.1984 80.1984 0 0 0 90.61999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.75520001c44.16 0 80.1024-35.9424 80.10239999-80.1216V181.2224c0-44.16-35.9424-80.1024-80.10239999-80.1024zM877.81999999 679.5776c0 14.5344-11.8272 26.3424-26.34239999 26.3424H170.72239999A26.3808 26.3808 0 0 1 144.38 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.34239999-26.3424h680.75520001c14.5152 0 26.3424 11.8272 26.34239999 26.3424v498.3552zM731.9 840.32h-441.60000001a26.88 26.88 0 0 0 0 53.76h441.60000001a26.88 26.88 0 0 0 0-53.76z" p-id="1524" fill="#CED0D6"></path><path d="M242.3576 554.72h46.90559999v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H242.3576zM408.51439999 359.1296h65.9328v195.5904h46.92480001V359.1296h66.56639999v-38.9952h-179.424zM703.06159999 320.1344h-77.03039999v234.5664h46.90559999v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44000001 36.4416 0.0192 26.9568-15.5136 40.5888-47.84640001 40.5888z" p-id="1525" fill="#CED0D6"></path></svg>
|
||||
<svg t="1751945257078" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1612" width="16" height="16"><path d="M853.97759999 101.12H173.22239999A80.1984 80.1984 0 0 0 93.11999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.7552c44.16 0 80.1024-35.9424 80.1024-80.1216V181.2224c0-44.16-35.9424-80.1024-80.1024-80.1024zM880.31999999 679.5776c0 14.5344-11.8272 26.3424-26.3424 26.3424H173.22239999A26.3808 26.3808 0 0 1 146.87999999 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.3424-26.3424h680.7552c14.5152 0 26.3424 11.8272 26.3424 26.3424v498.3552zM734.39999999 840.32h-441.6a26.88 26.88 0 0 0 0 53.76h441.6a26.88 26.88 0 0 0 0-53.76z" p-id="1613" fill="#CED0D6"></path><path d="M244.85759999 554.72h46.9056v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H244.85759999zM411.01439999 359.1296h65.9328v195.5904h46.9248V359.1296h66.5664v-38.9952h-179.424zM705.56159999 320.1344h-77.0304v234.5664h46.9056v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44 36.4416 0.0192 26.9568-15.5136 40.5888-47.8464 40.5888z" p-id="1614" fill="#CED0D6"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
1
plugins/ftp/src/main/resources/i18n/messages.properties
Normal file
1
plugins/ftp/src/main/resources/i18n/messages.properties
Normal file
@@ -0,0 +1 @@
|
||||
termora.plugins.ftp.passive=Passive Mode
|
||||
@@ -0,0 +1 @@
|
||||
termora.plugins.ftp.passive=被动模式
|
||||
@@ -0,0 +1 @@
|
||||
termora.plugins.ftp.passive=被動模式
|
||||
@@ -2,14 +2,14 @@ plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
}
|
||||
|
||||
project.version = "0.0.6"
|
||||
project.version = "0.0.7"
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
compileOnly(project(":"))
|
||||
implementation("com.maxmind.geoip2:geoip2:4.3.1")
|
||||
// https://github.com/hstyi/geolite2
|
||||
implementation("com.github.hstyi:geolite2:v1.0-202506280327")
|
||||
implementation("com.github.hstyi:geolite2:v1.0-202507070058")
|
||||
}
|
||||
|
||||
apply(from = "$rootDir/plugins/common.gradle.kts")
|
||||
|
||||
@@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
|
||||
project.version = "0.0.2"
|
||||
project.version = "0.0.4"
|
||||
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -46,7 +46,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
||||
controlsVisible = false
|
||||
|
||||
if (SystemInfo.isWindows || SystemInfo.isLinux) {
|
||||
title = I18n.getString("termora.doorman.safe")
|
||||
title = MigrationI18n.getString("termora.doorman.safe")
|
||||
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_TITLE, false)
|
||||
}
|
||||
|
||||
@@ -65,8 +65,8 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent {
|
||||
label.text = I18n.getString("termora.doorman.safe")
|
||||
tip.text = I18n.getString("termora.doorman.unlock-data")
|
||||
label.text = MigrationI18n.getString("termora.doorman.safe")
|
||||
tip.text = MigrationI18n.getString("termora.doorman.unlock-data")
|
||||
icon.icon = FlatSVGIcon(Icons.role.name, 80, 80)
|
||||
safeBtn.icon = Icons.unlocked
|
||||
|
||||
@@ -95,24 +95,24 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
||||
.add(passwordTextField).xy(2, rows)
|
||||
.add(safeBtn).xy(4, rows).apply { rows += step }
|
||||
.add(tip).xyw(2, rows, 4, "center, fill").apply { rows += step }
|
||||
.add(JXHyperlink(object : AnAction(I18n.getString("termora.doorman.forget-password")) {
|
||||
.add(JXHyperlink(object : AnAction(MigrationI18n.getString("termora.doorman.forget-password")) {
|
||||
override fun actionPerformed(evt: AnActionEvent) {
|
||||
val option = OptionPane.showConfirmDialog(
|
||||
this@DoormanDialog, I18n.getString("termora.doorman.forget-password-message"),
|
||||
this@DoormanDialog, MigrationI18n.getString("termora.doorman.forget-password-message"),
|
||||
options = arrayOf(
|
||||
I18n.getString("termora.doorman.have-a-mnemonic"),
|
||||
I18n.getString("termora.doorman.dont-have-a-mnemonic"),
|
||||
MigrationI18n.getString("termora.doorman.have-a-mnemonic"),
|
||||
MigrationI18n.getString("termora.doorman.dont-have-a-mnemonic"),
|
||||
),
|
||||
optionType = JOptionPane.YES_NO_OPTION,
|
||||
messageType = JOptionPane.INFORMATION_MESSAGE,
|
||||
initialValue = I18n.getString("termora.doorman.have-a-mnemonic")
|
||||
initialValue = MigrationI18n.getString("termora.doorman.have-a-mnemonic")
|
||||
)
|
||||
if (option == JOptionPane.YES_OPTION) {
|
||||
showMnemonicsDialog()
|
||||
} else if (option == JOptionPane.NO_OPTION) {
|
||||
OptionPane.showMessageDialog(
|
||||
this@DoormanDialog,
|
||||
I18n.getString("termora.doorman.delete-data"),
|
||||
MigrationI18n.getString("termora.doorman.delete-data"),
|
||||
messageType = JOptionPane.WARNING_MESSAGE
|
||||
)
|
||||
Application.browse(MigrationApplicationRunnerExtension.instance.getDatabaseFile().toURI())
|
||||
@@ -141,7 +141,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
||||
log.error(e.message, e)
|
||||
}
|
||||
OptionPane.showMessageDialog(
|
||||
this, I18n.getString("termora.doorman.mnemonic-data-corrupted"),
|
||||
this, MigrationI18n.getString("termora.doorman.mnemonic-data-corrupted"),
|
||||
messageType = JOptionPane.ERROR_MESSAGE
|
||||
)
|
||||
passwordTextField.outline = "error"
|
||||
@@ -166,7 +166,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
||||
} catch (e: Exception) {
|
||||
if (e is PasswordWrongException) {
|
||||
OptionPane.showMessageDialog(
|
||||
this, I18n.getString("termora.doorman.password-wrong"),
|
||||
this, MigrationI18n.getString("termora.doorman.password-wrong"),
|
||||
messageType = JOptionPane.ERROR_MESSAGE
|
||||
)
|
||||
}
|
||||
@@ -197,7 +197,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
||||
isModal = true
|
||||
isResizable = true
|
||||
controlsVisible = false
|
||||
title = I18n.getString("termora.doorman.mnemonic.title")
|
||||
title = MigrationI18n.getString("termora.doorman.mnemonic.title")
|
||||
init()
|
||||
pack()
|
||||
size = Dimension(max(size.width, UIManager.getInt("Dialog.width") - 250), size.height)
|
||||
@@ -251,7 +251,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
||||
} catch (e: Exception) {
|
||||
OptionPane.showMessageDialog(
|
||||
this,
|
||||
I18n.getString("termora.doorman.mnemonic.incorrect"),
|
||||
MigrationI18n.getString("termora.doorman.mnemonic.incorrect"),
|
||||
messageType = JOptionPane.ERROR_MESSAGE
|
||||
)
|
||||
return
|
||||
|
||||
@@ -187,8 +187,6 @@ class MigrationApplicationRunnerExtension private constructor() : ApplicationRun
|
||||
// 重启
|
||||
TermoraRestarter.getInstance().scheduleRestart(null, ask = false)
|
||||
|
||||
// 退出程序
|
||||
Disposer.dispose(TermoraFrameManager.getInstance())
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,3 +7,18 @@ termora.plugins.migration.message=<html> \
|
||||
<h3 align="center">📎 For more information, please see: <a href="https://github.com/TermoraDev/termora/issues/645">TermoraDev/termora/issues/645</a></h3> \
|
||||
</html>
|
||||
termora.plugins.migration.migrate=Migrate
|
||||
|
||||
# Doorman
|
||||
termora.doorman.safe=Data is encrypted
|
||||
termora.doorman.unlock-data=Enter password to unlock data
|
||||
termora.doorman.password-wrong=Wrong password
|
||||
termora.doorman.forget-password=Forgot password?
|
||||
termora.doorman.delete-data=Delete the data catalog and restart, This will lose all data
|
||||
termora.doorman.forget-password-message=Unlock data with a mnemonic. Without it, data cannot be accessed
|
||||
termora.doorman.have-a-mnemonic=I have a mnemonic
|
||||
termora.doorman.dont-have-a-mnemonic=I don't have a mnemonic
|
||||
termora.doorman.mnemonic-data-corrupted=Unable to decrypt data with the mnemonic, the data maybe corrupted
|
||||
|
||||
termora.doorman.mnemonic.title=Enter 12 mnemonic words
|
||||
termora.doorman.mnemonic.incorrect=Incorrect mnemonic
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
# Doorman
|
||||
termora.doorman.safe=Данные защифрованы
|
||||
termora.doorman.unlock-data=Введите пароль для разблокировки данных
|
||||
termora.doorman.verify-password=Введите пароль для проверки
|
||||
termora.doorman.password-wrong=Неверный пароль
|
||||
termora.doorman.password-correct=Пароль верный
|
||||
termora.doorman.unsafe=Данные не зашифрованы
|
||||
termora.doorman.lock-data=Спрашивать пароль при запуске
|
||||
termora.doorman.forget-password=Забыли пароль?
|
||||
termora.doorman.delete-data=Удалить данные и перезапустить, это приведет к потере всех данных
|
||||
termora.doorman.forget-password-message=Разблокировать данные с помощью мнемоники. Без него доступ к данным невозможен.
|
||||
termora.doorman.have-a-mnemonic=У меня есть мнемоники
|
||||
termora.doorman.dont-have-a-mnemonic=У меня нет мнемоники
|
||||
termora.doorman.mnemonic-data-corrupted=Невозможно расшифровать данные с помощью мнемоники, возможно, данные повреждены.
|
||||
|
||||
termora.doorman.mnemonic.title=Введите 12 слов мнемоники
|
||||
termora.doorman.mnemonic.incorrect=Неверные мнемоники
|
||||
@@ -7,3 +7,17 @@ termora.plugins.migration.message=<html> \
|
||||
<h3 align="center">📎 更多信息请查看:<a href="https://github.com/TermoraDev/termora/issues/645">TermoraDev/termora/issues/645</a></h3> \
|
||||
</html>
|
||||
termora.plugins.migration.migrate=迁移
|
||||
|
||||
# Doorman
|
||||
termora.doorman.safe=数据已加密
|
||||
termora.doorman.unlock-data=输入密码解锁数据
|
||||
termora.doorman.password-wrong=密码错误
|
||||
termora.doorman.forget-password=忘记密码?
|
||||
termora.doorman.delete-data=删除数据目录后重新启动程序,这样会丢失所有数据
|
||||
termora.doorman.forget-password-message=通过助记词解锁数据,没有助记词则无法解锁
|
||||
termora.doorman.have-a-mnemonic=我有助记词
|
||||
termora.doorman.dont-have-a-mnemonic=我没有助记词
|
||||
termora.doorman.mnemonic-data-corrupted=无法从助记词解密数据,数据可能已经损坏
|
||||
|
||||
termora.doorman.mnemonic.title=输入 12 个助记词
|
||||
termora.doorman.mnemonic.incorrect=助记词错误
|
||||
|
||||
@@ -7,3 +7,18 @@ termora.plugins.migration.message=<html> \
|
||||
<h3 align="center">📎 更多資訊請參見:<a href="https://github.com/TermoraDev/termora/issues/645">TermoraDev/termora/issues/645</a></h3> \
|
||||
</html>
|
||||
termora.plugins.migration.migrate=遷移
|
||||
|
||||
|
||||
# Doorman
|
||||
termora.doorman.safe=資料已加密
|
||||
termora.doorman.unlock-data=輸入密碼解鎖資料
|
||||
termora.doorman.password-wrong=密碼錯誤
|
||||
termora.doorman.forget-password=忘記密碼?
|
||||
termora.doorman.delete-data=刪除資料目錄後重新啟動程序,這樣會遺失所有數據
|
||||
termora.doorman.forget-password-message=透過助記詞解鎖數據,沒有助記詞則無法解鎖
|
||||
termora.doorman.have-a-mnemonic=我有助記詞
|
||||
termora.doorman.dont-have-a-mnemonic=我沒有助記詞
|
||||
termora.doorman.mnemonic-data-corrupted=無法從助記詞解密數據,資料可能已損壞
|
||||
termora.doorman.mnemonic.title=輸入 12 個助記詞
|
||||
termora.doorman.mnemonic.incorrect=助記詞錯誤
|
||||
|
||||
|
||||
17
plugins/serial/build.gradle.kts
Normal file
17
plugins/serial/build.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
}
|
||||
|
||||
|
||||
|
||||
project.version = "0.0.4"
|
||||
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
compileOnly(project(":"))
|
||||
implementation("com.fazecast:jSerialComm:2.11.2")
|
||||
}
|
||||
|
||||
apply(from = "$rootDir/plugins/common.gradle.kts")
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package app.termora.plugin.internal.serial
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.*
|
||||
import app.termora.account.AccountOwner
|
||||
import app.termora.highlight.KeywordHighlight
|
||||
import app.termora.plugin.internal.AltKeyModifier
|
||||
import app.termora.plugin.internal.BasicGeneralOption
|
||||
import app.termora.plugin.internal.BasicTerminalOption
|
||||
import com.fazecast.jSerialComm.SerialPort
|
||||
import com.formdev.flatlaf.FlatClientProperties
|
||||
import com.jgoodies.forms.builder.FormBuilder
|
||||
@@ -15,12 +19,16 @@ import java.awt.BorderLayout
|
||||
import java.awt.Component
|
||||
import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import java.nio.charset.Charset
|
||||
import javax.swing.*
|
||||
|
||||
class SerialHostOptionsPane : OptionsPane() {
|
||||
class SerialHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() {
|
||||
private val generalOption = BasicGeneralOption()
|
||||
private val terminalOption = TerminalOption()
|
||||
private val terminalOption = BasicTerminalOption().apply {
|
||||
showCharsetComboBox = true
|
||||
showStartupCommandTextField = true
|
||||
accountOwner = this@SerialHostOptionsPane.accountOwner
|
||||
init()
|
||||
}
|
||||
private val serialCommOption = SerialCommOption()
|
||||
|
||||
init {
|
||||
@@ -48,6 +56,12 @@ class SerialHostOptionsPane : OptionsPane() {
|
||||
encoding = terminalOption.charsetComboBox.selectedItem as String,
|
||||
startupCommand = terminalOption.startupCommandTextField.text,
|
||||
serialComm = serialComm,
|
||||
extras = mutableMapOf(
|
||||
"altModifier" to (terminalOption.altModifierComboBox.selectedItem?.toString()
|
||||
?: AltKeyModifier.EightBit.name),
|
||||
"keywordHighlightSetId" to ((terminalOption.highlightSetComboBox.selectedItem as? KeywordHighlight)?.id
|
||||
?: "-1"),
|
||||
)
|
||||
)
|
||||
|
||||
return Host(
|
||||
@@ -76,6 +90,20 @@ class SerialHostOptionsPane : OptionsPane() {
|
||||
serialCommOption.stopBitsComboBox.selectedItem = serialComm.stopBits
|
||||
serialCommOption.flowControlComboBox.selectedItem = serialComm.flowControl
|
||||
|
||||
val altModifier = host.options.extras["altModifier"] ?: AltKeyModifier.EightBit.name
|
||||
terminalOption.altModifierComboBox.selectedItem = runCatching { AltKeyModifier.valueOf(altModifier) }
|
||||
.getOrNull() ?: AltKeyModifier.EightBit
|
||||
|
||||
|
||||
val keywordHighlightSetId = host.options.extras["keywordHighlightSetId"]
|
||||
for (i in 0 until terminalOption.highlightSetComboBox.itemCount) {
|
||||
val item = terminalOption.highlightSetComboBox.getItemAt(i)
|
||||
if (item.id == keywordHighlightSetId) {
|
||||
terminalOption.highlightSetComboBox.selectedItem = item
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun validateFields(): Boolean {
|
||||
@@ -128,67 +156,6 @@ class SerialHostOptionsPane : OptionsPane() {
|
||||
}
|
||||
|
||||
|
||||
protected inner class TerminalOption : JPanel(BorderLayout()), Option {
|
||||
val charsetComboBox = JComboBox<String>()
|
||||
val startupCommandTextField = OutlineTextField()
|
||||
|
||||
|
||||
init {
|
||||
initView()
|
||||
initEvents()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
add(getCenterComponent(), BorderLayout.CENTER)
|
||||
|
||||
|
||||
for (e in Charset.availableCharsets()) {
|
||||
charsetComboBox.addItem(e.key)
|
||||
}
|
||||
|
||||
charsetComboBox.selectedItem = "UTF-8"
|
||||
|
||||
}
|
||||
|
||||
private fun initEvents() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun getIcon(isSelected: Boolean): Icon {
|
||||
return Icons.terminal
|
||||
}
|
||||
|
||||
override fun getTitle(): String {
|
||||
return I18n.getString("termora.new-host.terminal")
|
||||
}
|
||||
|
||||
override fun getJComponent(): JComponent {
|
||||
return this
|
||||
}
|
||||
|
||||
private fun getCenterComponent(): JComponent {
|
||||
val layout = FormLayout(
|
||||
"left:pref, $FORM_MARGIN, default:grow",
|
||||
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||
)
|
||||
|
||||
var rows = 1
|
||||
val step = 2
|
||||
val panel = FormBuilder.create().layout(layout)
|
||||
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
|
||||
.add(charsetComboBox).xy(3, rows).apply { rows += step }
|
||||
.add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
|
||||
.add(startupCommandTextField).xy(3, rows).apply { rows += step }
|
||||
.apply { rows += step }
|
||||
.build()
|
||||
|
||||
|
||||
return panel
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected inner class SerialCommOption : JPanel(BorderLayout()), Option {
|
||||
val serialPortComboBox = OutlineComboBox<String>()
|
||||
val baudRateComboBox = OutlineComboBox<Int>()
|
||||
@@ -0,0 +1,32 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.plugin.Extension
|
||||
import app.termora.plugin.ExtensionSupport
|
||||
import app.termora.plugin.Plugin
|
||||
import app.termora.protocol.ProtocolHostPanelExtension
|
||||
import app.termora.protocol.ProtocolProviderExtension
|
||||
|
||||
internal class SerialPlugin : Plugin {
|
||||
private val support = ExtensionSupport()
|
||||
|
||||
override fun getAuthor(): String {
|
||||
return "TermoraDev"
|
||||
}
|
||||
|
||||
init {
|
||||
support.addExtension(ProtocolProviderExtension::class.java) { SerialProtocolProviderExtension.instance }
|
||||
support.addExtension(ProtocolHostPanelExtension::class.java) { SerialProtocolHostPanelExtension.instance }
|
||||
}
|
||||
|
||||
|
||||
override fun getName(): String {
|
||||
return "Serial Comm"
|
||||
}
|
||||
|
||||
|
||||
override fun <T : Extension> getExtensions(clazz: Class<T>): List<T> {
|
||||
return support.getExtensions(clazz)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.termora
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.terminal.PtyConnector
|
||||
import com.fazecast.jSerialComm.SerialPort
|
||||
@@ -0,0 +1,37 @@
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.Disposer
|
||||
import app.termora.Host
|
||||
import app.termora.account.AccountOwner
|
||||
import app.termora.protocol.ProtocolHostPanel
|
||||
import java.awt.BorderLayout
|
||||
|
||||
class SerialProtocolHostPanel(accountOwner: AccountOwner) : ProtocolHostPanel() {
|
||||
private val pane = SerialHostOptionsPane(accountOwner)
|
||||
|
||||
init {
|
||||
initView()
|
||||
initEvents()
|
||||
}
|
||||
|
||||
|
||||
private fun initView() {
|
||||
add(pane, BorderLayout.CENTER)
|
||||
Disposer.register(this, pane)
|
||||
}
|
||||
|
||||
private fun initEvents() {}
|
||||
|
||||
|
||||
override fun getHost(): Host {
|
||||
return pane.getHost()
|
||||
}
|
||||
|
||||
override fun setHost(host: Host) {
|
||||
pane.setHost(host)
|
||||
}
|
||||
|
||||
override fun validateFields(): Boolean {
|
||||
return pane.validateFields()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.termora.plugin.internal.serial
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.account.AccountOwner
|
||||
import app.termora.protocol.ProtocolHostPanel
|
||||
@@ -16,7 +16,10 @@ internal class SerialProtocolHostPanelExtension private constructor() : Protocol
|
||||
}
|
||||
|
||||
override fun createProtocolHostPanel(accountOwner: AccountOwner): ProtocolHostPanel {
|
||||
return SerialProtocolHostPanel()
|
||||
return SerialProtocolHostPanel(accountOwner)
|
||||
}
|
||||
|
||||
override fun ordered(): Long {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.termora.plugin.internal.serial
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.*
|
||||
import app.termora.actions.DataProvider
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.termora.plugin.internal.serial
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.protocol.ProtocolProvider
|
||||
import app.termora.protocol.ProtocolProviderExtension
|
||||
@@ -1,6 +1,9 @@
|
||||
package app.termora.plugin.internal.serial
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.*
|
||||
import app.termora.Host
|
||||
import app.termora.Icons
|
||||
import app.termora.PtyHostTerminalTab
|
||||
import app.termora.WindowScope
|
||||
import app.termora.terminal.PtyConnector
|
||||
import org.apache.commons.io.Charsets
|
||||
import java.nio.charset.StandardCharsets
|
||||
@@ -1,5 +1,8 @@
|
||||
package app.termora
|
||||
package app.termora.plugins.serial
|
||||
|
||||
import app.termora.Host
|
||||
import app.termora.SerialCommFlowControl
|
||||
import app.termora.SerialCommParity
|
||||
import com.fazecast.jSerialComm.SerialPort
|
||||
|
||||
object Serials {
|
||||
22
plugins/serial/src/main/resources/META-INF/plugin.xml
Normal file
22
plugins/serial/src/main/resources/META-INF/plugin.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<termora-plugin>
|
||||
|
||||
<id>serial</id>
|
||||
|
||||
<name>Serial Comm</name>
|
||||
|
||||
<version>${projectVersion}</version>
|
||||
|
||||
<termora-version since=">=${rootProjectVersion}" until=""/>
|
||||
|
||||
<entry>app.termora.plugins.serial.SerialPlugin</entry>
|
||||
|
||||
<descriptions>
|
||||
<description>Supports access to serial ports</description>
|
||||
<description language="zh_CN">支持访问串口</description>
|
||||
<description language="zh_TW">支援訪問串口</description>
|
||||
</descriptions>
|
||||
|
||||
<vendor url="https://github.com/TermoraDev">TermoraDev</vendor>
|
||||
|
||||
|
||||
</termora-plugin>
|
||||
@@ -0,0 +1 @@
|
||||
<svg t="1747210120200" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1169" width="16" height="16"><path d="M806.11718723 531.44140652l-78.2578125 78.50390571L410.22265625 291.21874973l82.08984402-78.53906223a231.46874973 231.46874973 0 0 1 162.49218723-68.66015625 206.54296902 206.54296902 0 0 1 131.02734402 44.296875L856.98828125 117.125a35.15625027 35.15625027 0 0 1 49.67578125-0.03515652h0.03515652a35.15625027 35.15625027 0 0 1 0 49.74609429l-74.35546875 74.35546875a225.21093777 225.21093777 0 0 1-26.22656304 290.25z m-191.63671875 27.07031223a24.57421875 24.57421875 0 0 1-1.51171821 33.08203098l-57.72656277 57.76171929 63.98437473 63.73828071-83.42578125 83.46093777a230.62499973 230.62499973 0 0 1-161.71874946 68.66015625 204.92578125 204.92578125 0 0 1-136.75781304-49.21874973l-95.83593723 95.80078071a24.18750027 24.18750027 0 0 1-34.24218723-0.07031223 24.890625 24.890625 0 0 1-0.35156277-34.76953098l95.94140598-100.12500027a225.98437473 225.98437473 0 0 1 21.09375-299.28515598L305.98437473 394.0859375l68.66015625 68.66015625L427.13281277 411.10156277a24.39843777 24.39843777 0 0 1 34.34765598 34.59375L409.94140652 497.234375l117.9140625 117.63281277 56.53125-57.5859375a20.28515652 20.28515652 0 0 1 30.09374946 1.23046848z m-112.32421875 199.19531223l44.05078179-44.05078098-240.22265679-240.46875-44.296875 44.57812473a169.91015625 169.91015625 0 0 0-4.67578071 240.18750027l4.640625 4.92187473a151.875 151.875 0 0 0 117.9140625 48.97265652 175.35937473 175.35937473 0 0 0 122.58984321-54.70312473v0.56249946zM933.875 512c0 232.98046875-188.89453125 421.875-421.875 421.875-7.87499973 0-15.71484375-0.2109375-23.48437473-0.6328125l73.12499946-73.16015598a351.77343777 351.77343777 0 0 0 298.44140679-298.40625027l73.12499946-73.16015598c0.45703152 7.76953098 0.66796902 15.609375 0.66796902 23.48437473zM535.48437473 90.7578125L462.35937527 163.953125a351.77343777 351.77343777 0 0 0-298.44140679 298.40625027l-73.12499946 73.16015598A428.625 428.625 0 0 1 90.125 512C90.125 279.01953125 279.01953125 90.125 512 90.125c7.87499973 0 15.71484375 0.2109375 23.48437473 0.6328125z" fill="#6C707E" p-id="1170"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg t="1747210120200" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1169"
|
||||
width="16" height="16">
|
||||
<path d="M806.11718723 531.44140652l-78.2578125 78.50390571L410.22265625 291.21874973l82.08984402-78.53906223a231.46874973 231.46874973 0 0 1 162.49218723-68.66015625 206.54296902 206.54296902 0 0 1 131.02734402 44.296875L856.98828125 117.125a35.15625027 35.15625027 0 0 1 49.67578125-0.03515652h0.03515652a35.15625027 35.15625027 0 0 1 0 49.74609429l-74.35546875 74.35546875a225.21093777 225.21093777 0 0 1-26.22656304 290.25z m-191.63671875 27.07031223a24.57421875 24.57421875 0 0 1-1.51171821 33.08203098l-57.72656277 57.76171929 63.98437473 63.73828071-83.42578125 83.46093777a230.62499973 230.62499973 0 0 1-161.71874946 68.66015625 204.92578125 204.92578125 0 0 1-136.75781304-49.21874973l-95.83593723 95.80078071a24.18750027 24.18750027 0 0 1-34.24218723-0.07031223 24.890625 24.890625 0 0 1-0.35156277-34.76953098l95.94140598-100.12500027a225.98437473 225.98437473 0 0 1 21.09375-299.28515598L305.98437473 394.0859375l68.66015625 68.66015625L427.13281277 411.10156277a24.39843777 24.39843777 0 0 1 34.34765598 34.59375L409.94140652 497.234375l117.9140625 117.63281277 56.53125-57.5859375a20.28515652 20.28515652 0 0 1 30.09374946 1.23046848z m-112.32421875 199.19531223l44.05078179-44.05078098-240.22265679-240.46875-44.296875 44.57812473a169.91015625 169.91015625 0 0 0-4.67578071 240.18750027l4.640625 4.92187473a151.875 151.875 0 0 0 117.9140625 48.97265652 175.35937473 175.35937473 0 0 0 122.58984321-54.70312473v0.56249946zM933.875 512c0 232.98046875-188.89453125 421.875-421.875 421.875-7.87499973 0-15.71484375-0.2109375-23.48437473-0.6328125l73.12499946-73.16015598a351.77343777 351.77343777 0 0 0 298.44140679-298.40625027l73.12499946-73.16015598c0.45703152 7.76953098 0.66796902 15.609375 0.66796902 23.48437473zM535.48437473 90.7578125L462.35937527 163.953125a351.77343777 351.77343777 0 0 0-298.44140679 298.40625027l-73.12499946 73.16015598A428.625 428.625 0 0 1 90.125 512C90.125 279.01953125 279.01953125 90.125 512 90.125c7.87499973 0 15.71484375 0.2109375 23.48437473 0.6328125z"
|
||||
fill="#CED0D6" p-id="1170"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -2,7 +2,7 @@ plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
}
|
||||
|
||||
project.version = "0.0.2"
|
||||
project.version = "0.0.3"
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
package app.termora.plugins.smb
|
||||
|
||||
import app.termora.transfer.s3.S3FileSystem
|
||||
import app.termora.transfer.s3.S3Path
|
||||
import com.hierynomus.smbj.session.Session
|
||||
import com.hierynomus.smbj.share.DiskShare
|
||||
|
||||
class SMBFileSystem(private val share: DiskShare, session: Session) :
|
||||
S3FileSystem(SMBFileSystemProvider(share, session)) {
|
||||
|
||||
override fun create(root: String?, names: List<String>): S3Path {
|
||||
val path = SMBPath(this, root, names)
|
||||
if (names.isEmpty()) {
|
||||
path.attributes = path.attributes.copy(directory = true)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
|
||||
override fun close() {
|
||||
share.close()
|
||||
super.close()
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package app.termora.plugins.smb
|
||||
|
||||
import app.termora.transfer.s3.S3Path
|
||||
|
||||
class SMBPath(fileSystem: SMBFileSystem, root: String?, names: List<String>) : S3Path(fileSystem, root, names) {
|
||||
override val isBucket: Boolean
|
||||
get() = false
|
||||
|
||||
override val bucketName: String
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
override val objectName: String
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
override fun getCustomType(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
17
plugins/vnc/build.gradle.kts
Normal file
17
plugins/vnc/build.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
}
|
||||
|
||||
|
||||
project.version = "0.0.1"
|
||||
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
testImplementation(project(":"))
|
||||
implementation(files("${project.projectDir}/libs/trilead-ssh2-build217-jenkins-8.jar"))
|
||||
compileOnly(project(":"))
|
||||
}
|
||||
|
||||
|
||||
apply(from = "$rootDir/plugins/common.gradle.kts")
|
||||
BIN
plugins/vnc/libs/trilead-ssh2-build217-jenkins-8.jar
Normal file
BIN
plugins/vnc/libs/trilead-ssh2-build217-jenkins-8.jar
Normal file
Binary file not shown.
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.core;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class SettingsChangedEvent {
|
||||
private final Object source;
|
||||
|
||||
public SettingsChangedEvent(Object source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
144
plugins/vnc/src/main/java/com/glavsoft/drawing/ColorDecoder.java
Normal file
144
plugins/vnc/src/main/java/com/glavsoft/drawing/ColorDecoder.java
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.drawing;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class ColorDecoder {
|
||||
protected byte redShift;
|
||||
protected byte greenShift;
|
||||
protected byte blueShift;
|
||||
public short redMax;
|
||||
public short greenMax;
|
||||
public short blueMax;
|
||||
public final int bytesPerPixel;
|
||||
public final int bytesPerCPixel;
|
||||
public final int bytesPerPixelTight;
|
||||
private final byte[] buff;
|
||||
|
||||
private int startShift;
|
||||
private int startShiftCompact;
|
||||
private int addShiftItem;
|
||||
private final boolean isTightSpecific;
|
||||
|
||||
public ColorDecoder(PixelFormat pf) {
|
||||
redShift = pf.redShift;
|
||||
greenShift = pf.greenShift;
|
||||
blueShift = pf.blueShift;
|
||||
redMax = pf.redMax;
|
||||
greenMax = pf.greenMax;
|
||||
blueMax = pf.blueMax;
|
||||
bytesPerPixel = pf.bitsPerPixel / 8;
|
||||
final long significant = redMax << redShift | greenMax << greenShift | blueMax << blueShift;
|
||||
bytesPerCPixel = pf.depth <= 24 // as in RFB
|
||||
// || 32 == pf.depth) // UltraVNC use this... :(
|
||||
&& 32 == pf.bitsPerPixel
|
||||
&& ((significant & 0x00ff000000L) == 0 || (significant & 0x000000ffL) == 0)
|
||||
? 3
|
||||
: bytesPerPixel;
|
||||
bytesPerPixelTight = 24 == pf.depth && 32 == pf.bitsPerPixel ? 3 : bytesPerPixel;
|
||||
buff = new byte[bytesPerPixel];
|
||||
if (0 == pf.bigEndianFlag) {
|
||||
startShift = 0;
|
||||
startShiftCompact = 0;
|
||||
addShiftItem = 8;
|
||||
} else {
|
||||
startShift = pf.bitsPerPixel - 8;
|
||||
startShiftCompact = Math.max(0, pf.depth - 8);
|
||||
addShiftItem = -8;
|
||||
}
|
||||
isTightSpecific = 4==bytesPerPixel && 3==bytesPerPixelTight &&
|
||||
255 == redMax && 255 == greenMax && 255 == blueMax;
|
||||
}
|
||||
|
||||
protected int readColor(Transport transport) throws TransportException {
|
||||
return getColor(transport.readBytes(buff, 0, bytesPerPixel), 0);
|
||||
}
|
||||
|
||||
protected int readCompactColor(Transport transport) throws TransportException {
|
||||
return getCompactColor(transport.readBytes(buff, 0, bytesPerCPixel), 0);
|
||||
}
|
||||
|
||||
protected int readTightColor(Transport transport) throws TransportException {
|
||||
return getTightColor(transport.readBytes(buff, 0, bytesPerPixelTight), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert rfb encoded pixel color into 0x00rrggbb int value.
|
||||
* @param rawColor - bytes are ordered in right sequence (little/big endian transformations already done
|
||||
* @return 0x00rrggbb
|
||||
*/
|
||||
protected int convertColor(int rawColor) {
|
||||
return 255 * (rawColor >> redShift & redMax) / redMax << 16 |
|
||||
255 * (rawColor >> greenShift & greenMax) / greenMax << 8 |
|
||||
255 * (rawColor >> blueShift & blueMax) / blueMax;
|
||||
}
|
||||
|
||||
public void fillRawComponents(byte[] comp, byte[] bytes, int offset) {
|
||||
int rawColor = getRawTightColor(bytes, offset);
|
||||
comp[0] = (byte) (rawColor >> redShift & redMax);
|
||||
comp[1] = (byte) (rawColor >> greenShift & greenMax);
|
||||
comp[2] = (byte) (rawColor >> blueShift & blueMax);
|
||||
}
|
||||
|
||||
public int getTightColor(byte[] bytes, int offset) {
|
||||
return convertColor(getRawTightColor(bytes, offset));
|
||||
}
|
||||
|
||||
private int getRawTightColor(byte[] bytes, int offset) {
|
||||
if (isTightSpecific)
|
||||
return (bytes[offset++] & 0xff)<<16 |
|
||||
(bytes[offset++] & 0xff)<<8 |
|
||||
bytes[offset] & 0xff;
|
||||
else
|
||||
return getRawColor(bytes, offset);
|
||||
}
|
||||
|
||||
protected int getColor(byte[] bytes, int offset) {
|
||||
return convertColor(getRawColor(bytes, offset));
|
||||
}
|
||||
|
||||
private int getRawColor(byte[] bytes, int offset) {
|
||||
int shift = startShift;
|
||||
int item = addShiftItem;
|
||||
int rawColor = (bytes[offset++] & 0xff)<<shift;
|
||||
for (int i=1; i<bytesPerPixel; ++i) {
|
||||
rawColor |= (bytes[offset++] & 0xff)<<(shift+=item);
|
||||
}
|
||||
return rawColor;
|
||||
}
|
||||
|
||||
protected int getCompactColor(byte[] bytes, int offset) {
|
||||
int shift = startShiftCompact;
|
||||
int item = addShiftItem;
|
||||
int rawColor = (bytes[offset++] & 0xff)<<shift;
|
||||
for (int i=1; i< bytesPerCPixel; ++i) {
|
||||
rawColor |= (bytes[offset++] & 0xff)<<(shift+=item);
|
||||
}
|
||||
return convertColor(rawColor);
|
||||
}
|
||||
|
||||
}
|
||||
340
plugins/vnc/src/main/java/com/glavsoft/drawing/Renderer.java
Normal file
340
plugins/vnc/src/main/java/com/glavsoft/drawing/Renderer.java
Normal file
@@ -0,0 +1,340 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.drawing;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.rfb.encoding.decoder.FramebufferUpdateRectangle;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Render bitmap data
|
||||
*
|
||||
* @author dime @ tightvnc.com
|
||||
*/
|
||||
public abstract class Renderer {
|
||||
protected final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public abstract void drawJpegImage(byte[] bytes, int offset,
|
||||
int jpegBufferLength, FramebufferUpdateRectangle rect);
|
||||
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected int[] pixels;
|
||||
protected SoftCursor cursor;
|
||||
protected ColorDecoder colorDecoder;
|
||||
|
||||
protected void init(int width, int height, PixelFormat pixelFormat) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
initColorDecoder(pixelFormat);
|
||||
pixels = new int[width * height];
|
||||
Arrays.fill(pixels, 0);
|
||||
}
|
||||
|
||||
public void initColorDecoder(PixelFormat pixelFormat) {
|
||||
lock.lock();
|
||||
colorDecoder = new ColorDecoder(pixelFormat);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw byte array bitmap data
|
||||
*
|
||||
* @param bytes bitmap data
|
||||
* @param x bitmap x position
|
||||
* @param y bitmap y position
|
||||
* @param width bitmap width
|
||||
* @param height bitmap height
|
||||
*/
|
||||
public void drawBytes(byte[] bytes, int x, int y, int width, int height) {
|
||||
int i = 0;
|
||||
lock.lock();
|
||||
for (int ly = y; ly < y + height; ++ly) {
|
||||
int end = ly * this.width + x + width;
|
||||
for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||
pixels[pixelsOffset] = getPixelColor(bytes, i);
|
||||
i += colorDecoder.bytesPerPixel;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw byte array bitmap data (for ZRLE)
|
||||
*/
|
||||
public int drawCompactBytes(byte[] bytes, int offset, int x, int y, int width, int height) {
|
||||
int i = offset;
|
||||
lock.lock();
|
||||
for (int ly = y; ly < y + height; ++ly) {
|
||||
int end = ly * this.width + x + width;
|
||||
for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||
pixels[pixelsOffset] = getCompactPixelColor(bytes, i);
|
||||
i += colorDecoder.bytesPerCPixel;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
return i - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw int (colors) array bitmap data (for ZRLE)
|
||||
*/
|
||||
public void drawColoredBitmap(int[] colors, int x, int y, int width, int height) {
|
||||
int i = 0;
|
||||
lock.lock();
|
||||
for (int ly = y; ly < y + height; ++ly) {
|
||||
int end = ly * this.width + x + width;
|
||||
for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||
pixels[pixelsOffset] = colors[i++];
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw byte array bitmap data (for Tight)
|
||||
*/
|
||||
public int drawTightBytes(byte[] bytes, int offset, int x, int y, int width, int height) {
|
||||
int i = offset;
|
||||
lock.lock();
|
||||
for (int ly = y; ly < y + height; ++ly) {
|
||||
int end = ly * this.width + x + width;
|
||||
for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||
pixels[pixelsOffset] = colorDecoder.getTightColor(bytes, i);
|
||||
i += colorDecoder.bytesPerPixelTight;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
return i - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw byte array bitmap data (from array with plain RGB color components. Assumed: rrrrrrrr gggggggg bbbbbbbb)
|
||||
*/
|
||||
public void drawUncaliberedRGBLine(byte[] bytes, int x, int y, int width) {
|
||||
int end = y * this.width + x + width;
|
||||
lock.lock();
|
||||
for (int i = 3, pixelsOffset = y * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||
pixels[pixelsOffset] =
|
||||
// (0xff & bytes[i++]) << 16 |
|
||||
// (0xff & bytes[i++]) << 8 |
|
||||
// 0xff & bytes[i++];
|
||||
(0xff & 255 * (colorDecoder.redMax & bytes[i++]) / colorDecoder.redMax) << 16 |
|
||||
(0xff & 255 * (colorDecoder.greenMax & bytes[i++]) / colorDecoder.greenMax) << 8 |
|
||||
0xff & 255 * (colorDecoder.blueMax & bytes[i++]) / colorDecoder.blueMax;
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw paletted byte array bitmap data
|
||||
*
|
||||
* @param buffer bitmap data
|
||||
* @param rect bitmap location and dimensions
|
||||
* @param palette colour palette
|
||||
* @param paletteSize number of colors in palette
|
||||
*/
|
||||
public void drawBytesWithPalette(byte[] buffer, FramebufferUpdateRectangle rect, int[] palette, int paletteSize) {
|
||||
lock.lock();
|
||||
// 2 colors
|
||||
if (2 == paletteSize) {
|
||||
int dx, dy, n;
|
||||
int i = rect.y * this.width + rect.x;
|
||||
int rowBytes = (rect.width + 7) / 8;
|
||||
byte b;
|
||||
|
||||
for (dy = 0; dy < rect.height; dy++) {
|
||||
for (dx = 0; dx < rect.width / 8; dx++) {
|
||||
b = buffer[dy * rowBytes + dx];
|
||||
for (n = 7; n >= 0; n--) {
|
||||
pixels[i++] = palette[b >> n & 1];
|
||||
}
|
||||
}
|
||||
for (n = 7; n >= 8 - rect.width % 8; n--) {
|
||||
pixels[i++] = palette[buffer[dy * rowBytes + dx] >> n & 1];
|
||||
}
|
||||
i += this.width - rect.width;
|
||||
}
|
||||
} else {
|
||||
// 3..255 colors (assuming bytesPixel == 4).
|
||||
int i = 0;
|
||||
for (int ly = rect.y; ly < rect.y + rect.height; ++ly) {
|
||||
for (int lx = rect.x; lx < rect.x + rect.width; ++lx) {
|
||||
int pixelsOffset = ly * this.width + lx;
|
||||
pixels[pixelsOffset] = palette[buffer[i++] & 0xFF];
|
||||
}
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy rectangle region from one position to another. Regions may be overlapped.
|
||||
*
|
||||
* @param srcX source rectangle x position
|
||||
* @param srcY source rectangle y position
|
||||
* @param dstRect destination rectangle
|
||||
*/
|
||||
public void copyRect(int srcX, int srcY, FramebufferUpdateRectangle dstRect) {
|
||||
int startSrcY, endSrcY, dstY, deltaY;
|
||||
if (srcY > dstRect.y) {
|
||||
startSrcY = srcY;
|
||||
endSrcY = srcY + dstRect.height;
|
||||
dstY = dstRect.y;
|
||||
deltaY = +1;
|
||||
} else {
|
||||
startSrcY = srcY + dstRect.height - 1;
|
||||
endSrcY = srcY - 1;
|
||||
dstY = dstRect.y + dstRect.height - 1;
|
||||
deltaY = -1;
|
||||
}
|
||||
lock.lock();
|
||||
for (int y = startSrcY; y != endSrcY; y += deltaY) {
|
||||
System.arraycopy(pixels, y * width + srcX,
|
||||
pixels, dstY * width + dstRect.x, dstRect.width);
|
||||
dstY += deltaY;
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill rectangle region with specified colour
|
||||
*
|
||||
* @param color colour to fill with
|
||||
* @param rect rectangle region positions and dimensions
|
||||
*/
|
||||
public void fillRect(int color, FramebufferUpdateRectangle rect) {
|
||||
fillRect(color, rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill rectangle region with specified colour
|
||||
*
|
||||
* @param color colour to fill with
|
||||
* @param x rectangle x position
|
||||
* @param y rectangle y position
|
||||
* @param width rectangle width
|
||||
* @param height rectangle height
|
||||
*/
|
||||
public void fillRect(int color, int x, int y, int width, int height) {
|
||||
lock.lock();
|
||||
int sy = y * this.width + x;
|
||||
int ey = sy + height * this.width;
|
||||
for (int i = sy; i < ey; i += this.width) {
|
||||
Arrays.fill(pixels, i, i + width, color);
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads color bytes (PIXEL) from transport, returns int combined RGB
|
||||
* value consisting of the red component in bits 16-23, the green component
|
||||
* in bits 8-15, and the blue component in bits 0-7. May be used directly for
|
||||
* creation awt.Color object
|
||||
*/
|
||||
public int readPixelColor(Transport transport) throws TransportException {
|
||||
return colorDecoder.readColor(transport);
|
||||
}
|
||||
|
||||
public int readTightPixelColor(Transport transport) throws TransportException {
|
||||
return colorDecoder.readTightColor(transport);
|
||||
}
|
||||
|
||||
public ColorDecoder getColorDecoder() {
|
||||
return colorDecoder;
|
||||
}
|
||||
|
||||
public int getCompactPixelColor(byte[] bytes, int offset) {
|
||||
return colorDecoder.getCompactColor(bytes, offset);
|
||||
}
|
||||
|
||||
public int getPixelColor(byte[] bytes, int offset) {
|
||||
return colorDecoder.getColor(bytes, offset);
|
||||
}
|
||||
|
||||
public int getBytesPerPixel() {
|
||||
return colorDecoder.bytesPerPixel;
|
||||
}
|
||||
|
||||
public int getBytesPerCPixel() {
|
||||
return colorDecoder.bytesPerCPixel;
|
||||
}
|
||||
|
||||
public int getBytesPerPixelTight() {
|
||||
return colorDecoder.bytesPerPixelTight;
|
||||
}
|
||||
|
||||
public void fillColorBitmapWithColor(int[] bitmapData, int decodedOffset, int rlength, int color) {
|
||||
while (rlength-- > 0) {
|
||||
bitmapData[decodedOffset++] = color;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Width of rendered image
|
||||
*
|
||||
* @return width
|
||||
*/
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Height of rendered image
|
||||
*
|
||||
* @return height
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and decode cursor image
|
||||
*
|
||||
* @param rect new cursor hot point position and cursor dimensions
|
||||
* @throws TransportException
|
||||
*/
|
||||
public void createCursor(int[] cursorPixels, FramebufferUpdateRectangle rect)
|
||||
throws TransportException {
|
||||
synchronized (cursor.getLock()) {
|
||||
cursor.createCursor(cursorPixels, rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and decode new cursor position
|
||||
*
|
||||
* @param rect cursor position
|
||||
*/
|
||||
public void decodeCursorPosition(FramebufferUpdateRectangle rect) {
|
||||
synchronized (cursor.getLock()) {
|
||||
cursor.updatePosition(rect.x, rect.y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.drawing;
|
||||
|
||||
/**
|
||||
* Abstract class for operations with soft cursor positions, dimensions and
|
||||
* hot point position.
|
||||
*/
|
||||
public abstract class SoftCursor {
|
||||
|
||||
protected int hotX, hotY;
|
||||
protected int x, y;
|
||||
public int width, height;
|
||||
public int rX, rY;
|
||||
public int oldRX, oldRY;
|
||||
public int oldWidth, oldHeight;
|
||||
private final Object lock = new Object();
|
||||
|
||||
public SoftCursor(int hotX, int hotY, int width, int height) {
|
||||
this.hotX = hotX;
|
||||
this.hotY = hotY;
|
||||
oldWidth = this.width = width;
|
||||
oldHeight = this.height = height;
|
||||
oldRX = rX = 0;
|
||||
oldRY = rY = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cursor position
|
||||
*
|
||||
* @param newX
|
||||
* @param newY
|
||||
*/
|
||||
public void updatePosition(int newX, int newY) {
|
||||
oldRX = rX; oldRY = rY;
|
||||
oldWidth = width; oldHeight = height;
|
||||
x = newX; y = newY;
|
||||
rX = x - hotX; rY = y - hotY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new cursor dimensions and hot point position
|
||||
*
|
||||
* @param hotX
|
||||
* @param hotY
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public void setNewDimensions(int hotX, int hotY, int width, int height) {
|
||||
this.hotX = hotX;
|
||||
this.hotY = hotY;
|
||||
oldWidth = this.width;
|
||||
oldHeight = this.height;
|
||||
oldRX = rX; oldRY = rY;
|
||||
rX = x - hotX; rY = y - hotY;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void createCursor(int[] cursorPixels, int hotX, int hotY, int width, int height) {
|
||||
createNewCursorImage(cursorPixels, hotX, hotY, width, height);
|
||||
setNewDimensions(hotX, hotY, width, height);
|
||||
}
|
||||
|
||||
protected abstract void createNewCursorImage(int[] cursorPixels, int hotX, int hotY, int width, int height);
|
||||
|
||||
public Object getLock() {
|
||||
return lock;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
/**
|
||||
* Throws when authentication was wrong
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AuthenticationFailedException extends ProtocolException {
|
||||
|
||||
private String reason;
|
||||
|
||||
public AuthenticationFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public AuthenticationFailedException(String message, String reason) {
|
||||
super(message);
|
||||
this.reason = reason;
|
||||
}
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
/**
|
||||
* Throwed when connection closed (EOF)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ClosedConnectionException extends TransportException {
|
||||
|
||||
public ClosedConnectionException(Throwable exception) {
|
||||
super(exception);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class CommonException extends Exception {
|
||||
|
||||
public CommonException(Throwable exception) {
|
||||
super(exception);
|
||||
}
|
||||
public CommonException(String message, Throwable exception) {
|
||||
super(message, exception);
|
||||
}
|
||||
public CommonException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class CouldNotConnectException extends TransportException {
|
||||
|
||||
public CouldNotConnectException(Throwable exception) {
|
||||
super(exception);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
/**
|
||||
* Throws when problem with DES (or smth else) cryptosystem occured
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CryptoException extends FatalException {
|
||||
public CryptoException(String message, Throwable exception) {
|
||||
super(message, exception);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
/**
|
||||
* Trhows when further normal program execution unavailable
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class FatalException extends CommonException {
|
||||
public FatalException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ProtocolException extends CommonException {
|
||||
public ProtocolException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class TransportException extends CommonException {
|
||||
public TransportException(String message, Throwable exception) {
|
||||
super(message, exception);
|
||||
}
|
||||
public TransportException(Throwable exception) {
|
||||
super(exception);
|
||||
}
|
||||
public TransportException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class UnsupportedProtocolVersionException extends ProtocolException {
|
||||
public UnsupportedProtocolVersionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class UnsupportedSecurityTypeException extends ProtocolException {
|
||||
public UnsupportedSecurityTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb;
|
||||
|
||||
/**
|
||||
* Interface for handling clipboard texts
|
||||
*/
|
||||
public interface ClipboardController extends IChangeSettingsListener {
|
||||
void updateSystemClipboard(byte[] bytes);
|
||||
|
||||
/**
|
||||
* Get text clipboard contens when needed send to remote, or null vise versa
|
||||
* Implement this method such a way in swing context, because swing's clipboard
|
||||
* update listener invoked only on DataFlavor changes not content changes.
|
||||
* Implement as returned null on systems where clipboard listeners work correctly.
|
||||
*
|
||||
* @return clipboad string contents if it is changed from last method call
|
||||
* or null when clipboard contains non text object or clipboard contents didn't changed
|
||||
*/
|
||||
String getRenewedClipboardText();
|
||||
|
||||
/**
|
||||
* Returns clipboard text content previously retrieved frim system clipboard by
|
||||
* updateSavedClippoardContent()
|
||||
*
|
||||
* @return clipboard text content
|
||||
*/
|
||||
String getClipboardText();
|
||||
|
||||
/**
|
||||
* Enable/disable clipboard transfer
|
||||
*
|
||||
* @param enable
|
||||
*/
|
||||
void setEnabled(boolean enable);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb;
|
||||
|
||||
import com.glavsoft.core.SettingsChangedEvent;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public interface IChangeSettingsListener {
|
||||
void settingsChanged(SettingsChangedEvent event);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.rfb.encoding.decoder.FramebufferUpdateRectangle;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Interface for sending repaint event from worker thread to GUI thread
|
||||
*/
|
||||
public interface IRepaintController extends IChangeSettingsListener {
|
||||
void repaintBitmap(FramebufferUpdateRectangle rect);
|
||||
void repaintBitmap(int x, int y, int width, int height);
|
||||
void repaintCursor();
|
||||
void updateCursorPosition(short x, short y);
|
||||
Renderer createRenderer(Transport transport, int width, int height, PixelFormat pixelFormat);
|
||||
void setPixelFormat(PixelFormat pixelFormat);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb;
|
||||
|
||||
public interface IRequestString {
|
||||
String getResult();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb;
|
||||
|
||||
/**
|
||||
* Fires Rfb session [start]-stop events
|
||||
*/
|
||||
public interface IRfbSessionListener {
|
||||
|
||||
/**
|
||||
* Fired after rfb session closed (threads stopped, other resources fried).
|
||||
* Note: this event may be fired from unpredictable thread.
|
||||
* @param reason reason to show why session closed
|
||||
*/
|
||||
void rfbSessionStopped(String reason);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Structure used to describe protocol options such as tunneling methods,
|
||||
* authentication schemes and message types (protocol versions 3.7t, 3.8t).
|
||||
* typedef struct _rfbCapabilityInfo {
|
||||
* CARD32 code; // numeric identifier
|
||||
* CARD8 vendorSignature[4]; // vendor identification
|
||||
* CARD8 nameSignature[8]; // abbreviated option name
|
||||
* } rfbCapabilityInfo;
|
||||
*/
|
||||
public class RfbCapabilityInfo {
|
||||
/*
|
||||
* Vendors known by TightVNC: standard VNC/RealVNC, TridiaVNC, and TightVNC.
|
||||
* #define rfbStandardVendor "STDV"
|
||||
* #define rfbTridiaVncVendor "TRDV"
|
||||
* #define rfbTightVncVendor "TGHT"
|
||||
*/
|
||||
public static final String VENDOR_STANDARD = "STDV";
|
||||
public static final String VENDOR_TRIADA = "TRDV";
|
||||
public static final String VENDOR_TIGHT = "TGHT";
|
||||
|
||||
public static final String TUNNELING_NO_TUNNEL = "NOTUNNEL";
|
||||
|
||||
public static final String AUTHENTICATION_NO_AUTH = "NOAUTH__";
|
||||
public static final String AUTHENTICATION_VNC_AUTH ="VNCAUTH_";
|
||||
|
||||
public static final String ENCODING_COPYRECT = "COPYRECT";
|
||||
public static final String ENCODING_HEXTILE = "HEXTILE_";
|
||||
public static final String ENCODING_ZLIB = "ZLIB____";
|
||||
public static final String ENCODING_ZRLE = "ZRLE____";
|
||||
public static final String ENCODING_RRE = "RRE_____";
|
||||
public static final String ENCODING_TIGHT = "TIGHT___";
|
||||
// "Pseudo" encoding types
|
||||
public static final String ENCODING_RICH_CURSOR = "RCHCURSR";
|
||||
public static final String ENCODING_CURSOR_POS = "POINTPOS";
|
||||
public static final String ENCODING_DESKTOP_SIZE = "NEWFBSIZ";
|
||||
|
||||
private int code;
|
||||
private String vendorSignature;
|
||||
private String nameSignature;
|
||||
private boolean enable;
|
||||
|
||||
public RfbCapabilityInfo(int code, String vendorSignature, String nameSignature) {
|
||||
this.code = code;
|
||||
this.vendorSignature = vendorSignature;
|
||||
this.nameSignature = nameSignature;
|
||||
enable = true;
|
||||
}
|
||||
|
||||
public RfbCapabilityInfo() {
|
||||
this(0, "", "");
|
||||
}
|
||||
|
||||
public RfbCapabilityInfo readFrom(Transport transport) throws TransportException {
|
||||
code = transport.readInt32();
|
||||
vendorSignature = transport.readString(4);
|
||||
nameSignature = transport.readString(8);
|
||||
enable = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RfbCapabilityInfo that = (RfbCapabilityInfo) o;
|
||||
return code == that.code &&
|
||||
nameSignature.equals(that.nameSignature) &&
|
||||
vendorSignature.equals(that.vendorSignature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = code;
|
||||
result = 31 * result + vendorSignature.hashCode();
|
||||
result = 31 * result + nameSignature.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setEnable(boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getVendorSignature() {
|
||||
return vendorSignature;
|
||||
}
|
||||
|
||||
public String getNameSignature() {
|
||||
return nameSignature;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RfbCapabilityInfo{" +
|
||||
"code=" + code +
|
||||
", vendorSignature='" + vendorSignature + '\'' +
|
||||
", nameSignature='" + nameSignature + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.glavsoft.utils.Strings.getBytesWithCharset;
|
||||
|
||||
/**
|
||||
* ClientCutText
|
||||
* The client has new ISO 8859-1 (Latin-1) text in its cut buffer. Ends of lines are repre-
|
||||
* sented by the linefeed / newline character (value 10) alone. No carriage-return (value
|
||||
* 13) is needed. There is currently no way to transfer text outside the Latin-1 character
|
||||
* set.
|
||||
* 1 - U8 - 6
|
||||
* 3 - - padding
|
||||
* 4 - U32 - length
|
||||
* length - U8 array - text
|
||||
*/
|
||||
public class ClientCutTextMessage implements ClientToServerMessage {
|
||||
private final byte [] bytes;
|
||||
|
||||
public ClientCutTextMessage(String str, Charset charset) {
|
||||
final byte[] b = charset != null? getBytesWithCharset(str, charset): str.getBytes();
|
||||
this.bytes = Arrays.copyOf(b, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.CLIENT_CUT_TEXT.id)
|
||||
.zero(3) // padding
|
||||
.writeInt32(bytes.length)
|
||||
.write(bytes)
|
||||
.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClientCutTextMessage: [length: " + bytes.length +", text: ...]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public enum ClientMessageType {
|
||||
SET_PIXEL_FORMAT(0),
|
||||
SET_ENCODINGS(2),
|
||||
FRAMEBUFFER_UPDATE_REQUEST(3),
|
||||
KEY_EVENT(4),
|
||||
POINTER_EVENT(5),
|
||||
CLIENT_CUT_TEXT(6),
|
||||
|
||||
VIDEO_RECTANGLE_SELECTION(151),
|
||||
VIDEO_FREEZE(152);
|
||||
|
||||
final public int id;
|
||||
|
||||
ClientMessageType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private static final ClientMessageType [] standardTypes =
|
||||
{SET_PIXEL_FORMAT, SET_ENCODINGS, FRAMEBUFFER_UPDATE_REQUEST, KEY_EVENT, POINTER_EVENT, CLIENT_CUT_TEXT};
|
||||
|
||||
public static boolean isStandardType(ClientMessageType type) {
|
||||
for (final ClientMessageType it : standardTypes) {
|
||||
if (it == type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ClientMessageType byId(int id) {
|
||||
for (ClientMessageType type : values()) {
|
||||
if (type.id == id)
|
||||
return type;
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported client message type: " + id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public interface ClientToServerMessage {
|
||||
void send(Transport transport) throws TransportException;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
|
||||
public class FramebufferUpdateRequestMessage implements ClientToServerMessage {
|
||||
private final boolean incremental;
|
||||
private final int height;
|
||||
private final int width;
|
||||
private final int y;
|
||||
private final int x;
|
||||
|
||||
public FramebufferUpdateRequestMessage(int x, int y, int width,
|
||||
int height, boolean incremental) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.incremental = incremental;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.FRAMEBUFFER_UPDATE_REQUEST.id)
|
||||
.writeByte(incremental ? 1 : 0)
|
||||
.writeInt16(x)
|
||||
.writeInt16(y)
|
||||
.writeInt16(width)
|
||||
.writeInt16(height)
|
||||
.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FramebufferUpdateRequestMessage: [x: " + x + " y: " + y
|
||||
+ " width: " + width + " height: " + height +
|
||||
" incremental: " + incremental + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* A key press or release. Down-flag is non-zero (true) if the key is now pressed, zero
|
||||
* (false) if it is now released. The key itself is specified using the "keysym" values
|
||||
* defined by the X Window System.
|
||||
* 1 - U8 - message-type
|
||||
* 1 - U8 - down-flag
|
||||
* 2 - - - padding
|
||||
* 4 - U32 - key
|
||||
* For most ordinary keys, the "keysym" is the same as the corresponding ASCII value.
|
||||
* For full details, see The Xlib Reference Manual, published by O'Reilly & Associates,
|
||||
* or see the header file <X11/keysymdef.h> from any X Window System installation.
|
||||
*/
|
||||
public class KeyEventMessage implements ClientToServerMessage {
|
||||
|
||||
private final int key;
|
||||
private final boolean downFlag;
|
||||
|
||||
public KeyEventMessage(int key, boolean downFlag) {
|
||||
this.downFlag = downFlag;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.KEY_EVENT.id)
|
||||
.writeByte(downFlag ? 1 : 0)
|
||||
.zero(2) // padding
|
||||
.write(key)
|
||||
.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[KeyEventMessage: [down-flag: "+downFlag + ", key: " + key +"("+Integer.toHexString(key)+")]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class PointerEventMessage implements ClientToServerMessage {
|
||||
private final byte buttonMask;
|
||||
private final short x;
|
||||
private final short y;
|
||||
|
||||
public PointerEventMessage(byte buttonMask, short x, short y) {
|
||||
this.buttonMask = buttonMask;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.POINTER_EVENT.id)
|
||||
.writeByte(buttonMask)
|
||||
.writeInt16(x)
|
||||
.writeInt16(y)
|
||||
.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PointerEventMessage: [x: "+ x +", y: "+ y + ", button-mask: " +
|
||||
buttonMask +"]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.EncodingType;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class SetEncodingsMessage implements ClientToServerMessage {
|
||||
private final Set<EncodingType> encodings;
|
||||
|
||||
public SetEncodingsMessage(Set<EncodingType> set) {
|
||||
this.encodings = set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.SET_ENCODINGS.id)
|
||||
.zero(1) // padding byte
|
||||
.writeInt16(encodings.size());
|
||||
for (EncodingType enc : encodings) {
|
||||
transport.writeInt32(enc.getId());
|
||||
}
|
||||
transport.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("SetEncodingsMessage: [encodings: ");
|
||||
for (EncodingType enc : encodings) {
|
||||
sb.append(enc.name()).append(',');
|
||||
}
|
||||
sb.setLength(sb.length()-1);
|
||||
return sb.append(']').toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class SetPixelFormatMessage implements ClientToServerMessage {
|
||||
private final PixelFormat pixelFormat;
|
||||
|
||||
public SetPixelFormatMessage(PixelFormat pixelFormat) {
|
||||
this.pixelFormat = pixelFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.SET_PIXEL_FORMAT.id)
|
||||
.zero(3);
|
||||
pixelFormat.send(transport);
|
||||
transport.flush();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class VideoFreezeMessage implements ClientToServerMessage {
|
||||
|
||||
private boolean freeze;
|
||||
|
||||
public VideoFreezeMessage(boolean freeze) {
|
||||
this.freeze = freeze;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.VIDEO_FREEZE.id)
|
||||
.writeByte(freeze ? 1 : 0)
|
||||
.flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class VideoRectangleSelectionMessage implements ClientToServerMessage {
|
||||
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public VideoRectangleSelectionMessage(int x, int y, int width, int height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.VIDEO_RECTANGLE_SELECTION.id)
|
||||
.zero(1) // padding
|
||||
.writeInt16(x)
|
||||
.writeInt16(y)
|
||||
.writeInt16(width)
|
||||
.writeInt16(height)
|
||||
.flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding;
|
||||
|
||||
import com.glavsoft.rfb.encoding.decoder.CopyRectDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.CursorPosDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.Decoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.DesctopSizeDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.FakeDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.HextileDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.RREDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.RawDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.RichCursorDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.TightDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.ZRLEDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.ZlibDecoder;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
/**
|
||||
* Encoding types
|
||||
*/
|
||||
public enum EncodingType {
|
||||
/**
|
||||
* Desktop data representes as raw bytes stream
|
||||
*/
|
||||
RAW_ENCODING(0, "Raw", RawDecoder.class),
|
||||
/**
|
||||
* Specfies encodings which allow to copy part of image in client's
|
||||
* framebuffer from one place to another.
|
||||
*/
|
||||
COPY_RECT(1, "CopyRect", CopyRectDecoder.class),
|
||||
RRE(2, "RRE", RREDecoder.class),
|
||||
/**
|
||||
* Hextile encoding, uses palettes, filling and raw subencoding
|
||||
*/
|
||||
HEXTILE(5, "Hextile", HextileDecoder.class),
|
||||
/**
|
||||
* This encoding is like raw but previously all data compressed with zlib.
|
||||
*/
|
||||
ZLIB(6, "ZLib", ZlibDecoder.class),
|
||||
/**
|
||||
* Tight Encoding for slow connection. It is uses raw data, palettes, filling
|
||||
* and jpeg subencodings
|
||||
*/
|
||||
TIGHT(7, "Tight", TightDecoder.class),
|
||||
//ZlibHex(8),
|
||||
/**
|
||||
* ZRLE Encoding is like Hextile but previously all data compressed with zlib.
|
||||
*/
|
||||
ZRLE(16, "ZRLE", ZRLEDecoder.class),
|
||||
|
||||
/**
|
||||
* Rich Cursor pseudo encoding which allows to transfer cursor shape
|
||||
* with transparency
|
||||
*/
|
||||
RICH_CURSOR(0xFFFFFF11, "RichCursor", RichCursorDecoder.class),
|
||||
/**
|
||||
* Desktop Size Pseudo encoding allows to notificate client about
|
||||
* remote screen resolution changed.
|
||||
*/
|
||||
DESKTOP_SIZE(0xFFFFFF21, "DesctopSize", DesctopSizeDecoder.class),
|
||||
/**
|
||||
* Cusros position encoding allows to transfer remote cursor position to
|
||||
* client side.
|
||||
*/
|
||||
CURSOR_POS(0xFFFFFF18, "CursorPos", CursorPosDecoder.class),
|
||||
|
||||
COMPRESS_LEVEL_0(0xFFFFFF00 + 0, "CompressionLevel0", FakeDecoder.class),
|
||||
COMPRESS_LEVEL_1(0xFFFFFF00 + 1, "CompressionLevel1", null),
|
||||
COMPRESS_LEVEL_2(0xFFFFFF00 + 2, "CompressionLevel2", null),
|
||||
COMPRESS_LEVEL_3(0xFFFFFF00 + 3, "CompressionLevel3", null),
|
||||
COMPRESS_LEVEL_4(0xFFFFFF00 + 4, "CompressionLevel4", null),
|
||||
COMPRESS_LEVEL_5(0xFFFFFF00 + 5, "CompressionLevel5", null),
|
||||
COMPRESS_LEVEL_6(0xFFFFFF00 + 6, "CompressionLevel6", null),
|
||||
COMPRESS_LEVEL_7(0xFFFFFF00 + 7, "CompressionLevel7", null),
|
||||
COMPRESS_LEVEL_8(0xFFFFFF00 + 8, "CompressionLevel8", null),
|
||||
COMPRESS_LEVEL_9(0xFFFFFF00 + 9, "CompressionLevel9", null),
|
||||
|
||||
JPEG_QUALITY_LEVEL_0(0xFFFFFFE0 + 0, "JpegQualityLevel0", FakeDecoder.class),
|
||||
JPEG_QUALITY_LEVEL_1(0xFFFFFFE0 + 1, "JpegQualityLevel1", null),
|
||||
JPEG_QUALITY_LEVEL_2(0xFFFFFFE0 + 2, "JpegQualityLevel2", null),
|
||||
JPEG_QUALITY_LEVEL_3(0xFFFFFFE0 + 3, "JpegQualityLevel3", null),
|
||||
JPEG_QUALITY_LEVEL_4(0xFFFFFFE0 + 4, "JpegQualityLevel4", null),
|
||||
JPEG_QUALITY_LEVEL_5(0xFFFFFFE0 + 5, "JpegQualityLevel5", null),
|
||||
JPEG_QUALITY_LEVEL_6(0xFFFFFFE0 + 6, "JpegQualityLevel6", null),
|
||||
JPEG_QUALITY_LEVEL_7(0xFFFFFFE0 + 7, "JpegQualityLevel7", null),
|
||||
JPEG_QUALITY_LEVEL_8(0xFFFFFFE0 + 8, "JpegQualityLevel8", null),
|
||||
JPEG_QUALITY_LEVEL_9(0xFFFFFFE0 + 9, "JpegQualityLevel9", null);
|
||||
|
||||
private final int id;
|
||||
private final String name;
|
||||
public final Class<? extends Decoder> klass;
|
||||
|
||||
private EncodingType(int id, String name, Class<? extends Decoder> klass) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.klass = klass;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static final LinkedHashSet<EncodingType> ordinaryEncodings = new LinkedHashSet<EncodingType>();
|
||||
static {
|
||||
ordinaryEncodings.add(ZRLE);
|
||||
ordinaryEncodings.add(TIGHT);
|
||||
ordinaryEncodings.add(ZLIB);
|
||||
ordinaryEncodings.add(HEXTILE);
|
||||
ordinaryEncodings.add(RRE);
|
||||
ordinaryEncodings.add(COPY_RECT);
|
||||
ordinaryEncodings.add(RAW_ENCODING);
|
||||
}
|
||||
|
||||
public static final LinkedHashSet<EncodingType> pseudoEncodings = new LinkedHashSet<EncodingType>();
|
||||
static {
|
||||
pseudoEncodings.add(RICH_CURSOR);
|
||||
pseudoEncodings.add(CURSOR_POS);
|
||||
pseudoEncodings.add(DESKTOP_SIZE);
|
||||
}
|
||||
|
||||
public static EncodingType byId(int id) {
|
||||
// TODO needs to speedup with hash usage?
|
||||
for (EncodingType type : values()) {
|
||||
if (type.getId() == id)
|
||||
return type;
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported encoding code: " + id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Pixel Format:
|
||||
* 1 - U8 - bits-per-pixel
|
||||
* 1 - U8 - depth
|
||||
* 1 - U8 - big-endian-flag
|
||||
* 1 - U8 - true-color-flag
|
||||
* 2 - U16 - red-max
|
||||
* 2 - U16 - green-max
|
||||
* 2 - U16 - blue-max
|
||||
* 1 - U8 - red-shift
|
||||
* 1 - U8 - green-shift
|
||||
* 1 - U8 - blue-shift
|
||||
* 3 - - padding
|
||||
*/
|
||||
public class PixelFormat {
|
||||
public byte bitsPerPixel;
|
||||
public byte depth;
|
||||
public byte bigEndianFlag;
|
||||
public byte trueColourFlag;
|
||||
public short redMax;
|
||||
public short greenMax;
|
||||
public short blueMax;
|
||||
public byte redShift;
|
||||
public byte greenShift;
|
||||
public byte blueShift;
|
||||
|
||||
public void fill(Transport transport) throws TransportException {
|
||||
bitsPerPixel = transport.readByte();
|
||||
depth = transport.readByte();
|
||||
bigEndianFlag = transport.readByte();
|
||||
trueColourFlag = transport.readByte();
|
||||
redMax = transport.readInt16();
|
||||
greenMax = transport.readInt16();
|
||||
blueMax = transport.readInt16();
|
||||
redShift = transport.readByte();
|
||||
greenShift = transport.readByte();
|
||||
blueShift = transport.readByte();
|
||||
transport.readBytes(3); // skip padding bytes
|
||||
}
|
||||
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.write(bitsPerPixel)
|
||||
.write(depth)
|
||||
.write(bigEndianFlag)
|
||||
.write(trueColourFlag)
|
||||
.write(redMax)
|
||||
.write(greenMax)
|
||||
.write(blueMax)
|
||||
.write(redShift)
|
||||
.write(greenShift)
|
||||
.write(blueShift)
|
||||
.writeInt16(0) // padding bytes
|
||||
.writeByte(0); // padding bytes
|
||||
}
|
||||
|
||||
public static PixelFormat create24bitColorDepthPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 32;
|
||||
pixelFormat.blueMax = 255;
|
||||
pixelFormat.blueShift = 0;
|
||||
pixelFormat.greenMax = 255;
|
||||
pixelFormat.greenShift = 8;
|
||||
pixelFormat.redMax = 255;
|
||||
pixelFormat.redShift = 16;
|
||||
pixelFormat.depth = 24;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* specifies 65536 colors, 5bit per Red, 6bit per Green, 5bit per Blue
|
||||
*/
|
||||
public static PixelFormat create16bitColorDepthPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 16;
|
||||
pixelFormat.blueMax = 31;
|
||||
pixelFormat.blueShift = 0;
|
||||
pixelFormat.greenMax = 63;
|
||||
pixelFormat.greenShift = 5;
|
||||
pixelFormat.redMax = 31;
|
||||
pixelFormat.redShift = 11;
|
||||
pixelFormat.depth = 16;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* specifies 256 colors, 2bit per Blue, 3bit per Green & Red
|
||||
*/
|
||||
public static PixelFormat create8bitColorDepthBGRPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 8;
|
||||
pixelFormat.redMax = 7;
|
||||
pixelFormat.redShift = 0;
|
||||
pixelFormat.greenMax = 7;
|
||||
pixelFormat.greenShift = 3;
|
||||
pixelFormat.blueMax = 3;
|
||||
pixelFormat.blueShift = 6;
|
||||
pixelFormat.depth = 8;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* specifies 64 colors, 2bit per Red, Green & Blue
|
||||
*/
|
||||
public static PixelFormat create6bitColorDepthPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 8;
|
||||
pixelFormat.blueMax = 3;
|
||||
pixelFormat.blueShift = 0;
|
||||
pixelFormat.greenMax = 3;
|
||||
pixelFormat.greenShift = 2;
|
||||
pixelFormat.redMax = 3;
|
||||
pixelFormat.redShift = 4;
|
||||
pixelFormat.depth = 6;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* specifies 8 colors, 1bit per Red, Green & Blue
|
||||
*/
|
||||
public static PixelFormat create3bitColorDepthPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 8;
|
||||
pixelFormat.blueMax = 1;
|
||||
pixelFormat.blueShift = 0;
|
||||
pixelFormat.greenMax = 1;
|
||||
pixelFormat.greenShift = 1;
|
||||
pixelFormat.redMax = 1;
|
||||
pixelFormat.redShift = 2;
|
||||
pixelFormat.depth = 3;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PixelFormat: [bits-per-pixel: " + String.valueOf(0xff & bitsPerPixel) +
|
||||
", depth: " + String.valueOf(0xff & depth) +
|
||||
", big-endian-flag: " + String.valueOf(0xff & bigEndianFlag) +
|
||||
", true-color-flag: " + String.valueOf(0xff & trueColourFlag) +
|
||||
", red-max: " + String.valueOf(0xffff & redMax) +
|
||||
", green-max: " + String.valueOf(0xffff & greenMax) +
|
||||
", blue-max: " + String.valueOf(0xffff & blueMax) +
|
||||
", red-shift: " + String.valueOf(0xff & redShift) +
|
||||
", green-shift: " + String.valueOf(0xff & greenShift) +
|
||||
", blue-shift: " + String.valueOf(0xff & blueShift) +
|
||||
"]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Struct filled from the ServerInit message
|
||||
* 2 - U16 - framebuffer-width
|
||||
* 2 - U16 - framebuffer-height
|
||||
* 16 - PixelFormat - server-pixel-format
|
||||
* 4 - U32 - name-length
|
||||
* name-length - U8 array - name-string
|
||||
*/
|
||||
public class ServerInitMessage {
|
||||
protected String name;
|
||||
protected int framebufferWidth;
|
||||
protected int framebufferHeight;
|
||||
protected PixelFormat pixelFormat;
|
||||
|
||||
public ServerInitMessage readFrom(Transport transport) throws TransportException {
|
||||
framebufferWidth = transport.readUInt16();
|
||||
framebufferHeight = transport.readUInt16();
|
||||
pixelFormat = new PixelFormat();
|
||||
pixelFormat.fill(transport);
|
||||
name = transport.readString();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getFramebufferWidth() {
|
||||
return framebufferWidth;
|
||||
}
|
||||
|
||||
public int getFramebufferHeight() {
|
||||
return framebufferHeight;
|
||||
}
|
||||
|
||||
public PixelFormat getPixelFormat() {
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerInitMessage{" +
|
||||
"name='" + name + '\'' +
|
||||
", framebufferWidth=" + framebufferWidth +
|
||||
", framebufferHeight=" + framebufferHeight +
|
||||
", pixelFormat=" + pixelFormat +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding.decoder;
|
||||
|
||||
/**
|
||||
* Resizeable to needed length byte buffer
|
||||
* Singleton for share among decoders.
|
||||
*/
|
||||
public class ByteBuffer {
|
||||
private static ThreadLocal<ByteBuffer> threadLocal = new ThreadLocal<ByteBuffer>() {
|
||||
@Override
|
||||
protected ByteBuffer initialValue() {
|
||||
return new ByteBuffer();
|
||||
}
|
||||
};
|
||||
private byte [] buffer = new byte[0];
|
||||
|
||||
private ByteBuffer() { /*empty*/ }
|
||||
|
||||
public static ByteBuffer getInstance() {
|
||||
return threadLocal.get();
|
||||
}
|
||||
|
||||
public static void removeInstance() {
|
||||
threadLocal.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for buffer capacity is enougth ( < length) and enlarge it if not
|
||||
*/
|
||||
public void correctBufferCapacity(int length) {
|
||||
// procondition: buffer != null
|
||||
assert (buffer != null);
|
||||
if (buffer.length < length) {
|
||||
buffer = new byte[length];
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getBuffer(int length) {
|
||||
correctBufferCapacity(length);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class CopyRectDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
int srcX = transport.readUInt16();
|
||||
int srcY = transport.readUInt16();
|
||||
if (rect.width == 0 || rect.height == 0) return;
|
||||
renderer.copyRect(srcX, srcY, rect);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class CursorPosDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException {
|
||||
renderer.decodeCursorPosition(rect);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
|
||||
public abstract class Decoder {
|
||||
/**
|
||||
* Decode rectangle data.
|
||||
*/
|
||||
abstract public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException;
|
||||
|
||||
/**
|
||||
* Reset decoder when needed. Ex. reset ZLib stream inflaters for Z* and Tight decoders.
|
||||
*/
|
||||
public void reset() { /*empty*/ }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class DesctopSizeDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException {
|
||||
// do nothing, all real job will be done in ReceiverTask.framebufferUpdateMessage()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class FakeDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException {
|
||||
throw new IllegalStateException("Improper use of fake decoder");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.EncodingType;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Header for framebuffer-update-rectangle header server message
|
||||
* 2 - U16 - x-position
|
||||
* 2 - U16 - y-position
|
||||
* 2 - U16 - width
|
||||
* 2 - U16 - height
|
||||
* 4 - S32 - encoding-type
|
||||
* and then follows the pixel data in the specified encoding
|
||||
*/
|
||||
public class FramebufferUpdateRectangle {
|
||||
public int x;
|
||||
public int y;
|
||||
public int width;
|
||||
public int height;
|
||||
private EncodingType encodingType;
|
||||
|
||||
public FramebufferUpdateRectangle() {
|
||||
// nop
|
||||
}
|
||||
|
||||
public FramebufferUpdateRectangle(int x, int y, int w, int h) {
|
||||
this.x = x; this.y = y;
|
||||
width = w; height = h;
|
||||
}
|
||||
|
||||
public void fill(Transport transport) throws TransportException {
|
||||
x = transport.readUInt16();
|
||||
y = transport.readUInt16();
|
||||
width = transport.readUInt16();
|
||||
height = transport.readUInt16();
|
||||
int encoding = transport.readInt32();
|
||||
encodingType = EncodingType.byId(encoding);
|
||||
}
|
||||
|
||||
public EncodingType getEncodingType() {
|
||||
return encodingType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FramebufferUpdateRect: [x: " + x + ", y: " + y +
|
||||
", width: " + width + ", height: " + height +
|
||||
", encodingType: " + encodingType +
|
||||
"]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||
// All rights reserved.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// This file is part of the TightVNC software. Please visit our Web site:
|
||||
//
|
||||
// http://www.tightvnc.com/
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
// -----------------------------------------------------------------------
|
||||
//
|
||||
package com.glavsoft.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
|
||||
public class HextileDecoder extends Decoder {
|
||||
private static final int DEFAULT_TILE_SIZE = 16;
|
||||
private static final int RAW_MASK = 1;
|
||||
private static final int BACKGROUND_SPECIFIED_MASK = 2;
|
||||
private static final int FOREGROUND_SPECIFIED_MASK = 4;
|
||||
private static final int ANY_SUBRECTS_MASK = 8;
|
||||
private static final int SUBRECTS_COLOURED_MASK = 16;
|
||||
private static final int FG_COLOR_INDEX = 0;
|
||||
private static final int BG_COLOR_INDEX = 1;
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
if (rect.width == 0 || rect.height == 0) return;
|
||||
int[] colors = new int[] {-1, -1};
|
||||
int maxX = rect.x + rect.width;
|
||||
int maxY = rect.y + rect.height;
|
||||
for (int tileY = rect.y; tileY < maxY;
|
||||
tileY += DEFAULT_TILE_SIZE) {
|
||||
int tileHeight = Math.min(maxY - tileY, DEFAULT_TILE_SIZE);
|
||||
for (int tileX = rect.x; tileX < maxX;
|
||||
tileX += DEFAULT_TILE_SIZE) {
|
||||
int tileWidth = Math.min(maxX - tileX, DEFAULT_TILE_SIZE);
|
||||
decodeTile(transport, renderer, colors, tileX,
|
||||
tileY, tileWidth, tileHeight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void decodeTile(Transport transport,
|
||||
Renderer renderer, int[] colors,
|
||||
int tileX, int tileY, int tileWidth, int tileHeight)
|
||||
throws TransportException {
|
||||
int subencoding = transport.readUInt8();
|
||||
if ((subencoding & RAW_MASK) != 0) {
|
||||
RawDecoder.getInstance().decode(transport, renderer,
|
||||
tileX, tileY, tileWidth, tileHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((subencoding & BACKGROUND_SPECIFIED_MASK) != 0) {
|
||||
colors[BG_COLOR_INDEX] = renderer.readPixelColor(transport);
|
||||
}
|
||||
renderer.fillRect(colors[BG_COLOR_INDEX],
|
||||
tileX, tileY, tileWidth, tileHeight);
|
||||
|
||||
if ((subencoding & FOREGROUND_SPECIFIED_MASK) != 0) {
|
||||
colors[FG_COLOR_INDEX] = renderer.readPixelColor(transport);
|
||||
}
|
||||
|
||||
if ((subencoding & ANY_SUBRECTS_MASK) == 0)
|
||||
return;
|
||||
|
||||
int numberOfSubrectangles = transport.readUInt8();
|
||||
boolean colorSpecified = (subencoding & SUBRECTS_COLOURED_MASK) != 0;
|
||||
for (int i = 0; i < numberOfSubrectangles; ++i) {
|
||||
int color = colorSpecified ? renderer.readPixelColor(transport) : colors[FG_COLOR_INDEX];
|
||||
colors[FG_COLOR_INDEX] = color;
|
||||
byte dimensions = transport.readByte(); // bits 7-4 for x, bits 3-0 for y
|
||||
int subtileX = dimensions >> 4 & 0x0f;
|
||||
int subtileY = dimensions & 0x0f;
|
||||
dimensions = transport.readByte(); // bits 7-4 for w, bits 3-0 for h
|
||||
int subtileWidth = 1 + (dimensions >> 4 & 0x0f);
|
||||
int subtileHeight = 1 + (dimensions & 0x0f);
|
||||
renderer.fillRect(color,
|
||||
tileX + subtileX, tileY + subtileY,
|
||||
subtileWidth, subtileHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user