Compare commits
102 Commits
release
...
v1.0.1-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bd27d0b17 | ||
|
|
ee5612da8b | ||
|
|
c7a2023c88 | ||
|
|
c031a8c145 | ||
|
|
0bf83f07ad | ||
|
|
3638b712bd | ||
|
|
17f9536476 | ||
|
|
0ef51e3faf | ||
|
|
cccf5dadb2 | ||
|
|
2f0b0ffc22 | ||
|
|
c7411b59f1 | ||
|
|
8222782522 | ||
|
|
5fed09c1aa | ||
|
|
8e499772f9 | ||
|
|
0da812e7e9 | ||
|
|
3d67b6e9d6 | ||
|
|
506b2893c6 | ||
|
|
56abe9e690 | ||
|
|
ab13fa582d | ||
|
|
b9e8192eee | ||
|
|
5590aaeb1e | ||
|
|
adfe14809f | ||
|
|
a21dbc8d69 | ||
|
|
9d45e497f4 | ||
|
|
e5891eb397 | ||
|
|
b9c70f54d3 | ||
|
|
9cd617d078 | ||
|
|
92fd7f2e89 | ||
|
|
b8535fff6f | ||
|
|
09f34a81ad | ||
|
|
22cc552e85 | ||
|
|
69a4dfcbb9 | ||
|
|
e1390ca2d3 | ||
|
|
dccdcd1b6f | ||
|
|
276c3a336f | ||
|
|
08518a9409 | ||
|
|
f6b0767bb1 | ||
|
|
641fc84430 | ||
|
|
4ce79b87a3 | ||
|
|
477b204913 | ||
|
|
98f349ea2f | ||
|
|
47b1e15eef | ||
|
|
590bf50924 | ||
|
|
097633e47d | ||
|
|
d9ba88107d | ||
|
|
f6a6ef0b08 | ||
|
|
a93ee0c19e | ||
|
|
7e73553aca | ||
|
|
b858bd78c0 | ||
|
|
f10947edf9 | ||
|
|
c723cca8f9 | ||
|
|
b8f8b07ebe | ||
|
|
f9c6e5a6ef | ||
|
|
15bf8ef8c0 | ||
|
|
6565816c0e | ||
|
|
2aa67ccd57 | ||
|
|
88c75f94e4 | ||
|
|
aea9505c4c | ||
|
|
646740aab6 | ||
|
|
a458836e6e | ||
|
|
5fe7df8ea8 | ||
|
|
fda3743f86 | ||
|
|
9e4170b3a8 | ||
|
|
43338aaf02 | ||
|
|
274b7fcedc | ||
|
|
01a5660984 | ||
|
|
a45118f785 | ||
|
|
b6631c3db0 | ||
|
|
84d164c3af | ||
|
|
32815ce25a | ||
|
|
7c2b3f8c8d | ||
|
|
309d9d6ed6 | ||
|
|
91ab21c5f2 | ||
|
|
b8369c4f0a | ||
|
|
a068a32303 | ||
|
|
b6fe0da581 | ||
|
|
6e7e2697b5 | ||
|
|
22084072b0 | ||
|
|
d5457373f4 | ||
|
|
01ae299cde | ||
|
|
fbcd4c21cf | ||
|
|
cf9dc0a9a5 | ||
|
|
1b4f41af46 | ||
|
|
7f3425519b | ||
|
|
434c92deb6 | ||
|
|
e5eae1feb4 | ||
|
|
37e37d7d20 | ||
|
|
062568dc96 | ||
|
|
d60fdf9050 | ||
|
|
9912a88a13 | ||
|
|
8a8f2cd5d7 | ||
|
|
e77e16d9c2 | ||
|
|
1616d0ec33 | ||
|
|
374453775f | ||
|
|
6f8fd6a030 | ||
|
|
c7166975b3 | ||
|
|
2ae5e5a969 | ||
|
|
85cdc995c5 | ||
|
|
b051c8a059 | ||
|
|
508b7dc7a1 | ||
|
|
f203566b81 | ||
|
|
8360c1725f |
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.h linguist-language=C++
|
||||
*.cpp linguist-language=C++
|
||||
*.lua linguist-language=Xmake
|
||||
387
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
tags:
|
||||
- "*"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
# Linux amd64
|
||||
build-linux-amd64:
|
||||
name: Build on Ubuntu 22.04 amd64
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: crossdesk/ubuntu20.04:latest
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Extract version number
|
||||
id: version
|
||||
run: |
|
||||
VERSION="${GITHUB_REF##*/}"
|
||||
VERSION_NUM="${VERSION#v}"
|
||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set legal Debian version
|
||||
shell: bash
|
||||
id: set_deb_version
|
||||
run: |
|
||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
|
||||
LEGAL_VERSION="0.0.0-${VERSION_NUM}-${SHORT_SHA}"
|
||||
else
|
||||
LEGAL_VERSION="${VERSION_NUM}-${SHORT_SHA}"
|
||||
fi
|
||||
echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build CrossDesk
|
||||
env:
|
||||
CUDA_PATH: /usr/local/cuda
|
||||
XMAKE_GLOBALDIR: /data
|
||||
run: |
|
||||
ls -la $XMAKE_GLOBALDIR
|
||||
xmake f --CROSSDESK_VERSION=${LEGAL_VERSION} --root -y
|
||||
xmake b -vy --root crossdesk
|
||||
|
||||
- name: Decode and save certificate
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p certs
|
||||
echo "${{ secrets.CROSSDESK_CERT_BASE64 }}" | base64 --decode > certs/crossdesk.cn_root.crt
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
chmod +x ./scripts/linux/pkg_amd64.sh
|
||||
./scripts/linux/pkg_amd64.sh ${LEGAL_VERSION}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: crossdesk-linux-amd64-${{ env.LEGAL_VERSION }}
|
||||
path: ${{ github.workspace }}/crossdesk-linux-amd64-${{ env.LEGAL_VERSION }}.deb
|
||||
|
||||
# Linux arm64
|
||||
build-linux-arm64:
|
||||
name: Build on Ubuntu 22.04 arm64
|
||||
runs-on: ubuntu-22.04-arm
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: arm64
|
||||
image: crossdesk/ubuntu20.04-arm64v8:latest
|
||||
package_script: ./scripts/linux/pkg_arm64.sh
|
||||
container:
|
||||
image: ${{ matrix.image }}
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Extract version number
|
||||
id: version
|
||||
run: |
|
||||
VERSION="${GITHUB_REF##*/}"
|
||||
VERSION_NUM="${VERSION#v}"
|
||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set legal Debian version
|
||||
shell: bash
|
||||
id: set_deb_version
|
||||
run: |
|
||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
|
||||
LEGAL_VERSION="0.0.0-${VERSION_NUM}-${SHORT_SHA}"
|
||||
else
|
||||
LEGAL_VERSION="${VERSION_NUM}-${SHORT_SHA}"
|
||||
fi
|
||||
echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build CrossDesk
|
||||
env:
|
||||
CUDA_PATH: /usr/local/cuda
|
||||
XMAKE_GLOBALDIR: /data
|
||||
run: |
|
||||
xmake f --CROSSDESK_VERSION=${LEGAL_VERSION} --root -y
|
||||
xmake b -vy --root crossdesk
|
||||
|
||||
- name: Decode and save certificate
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p certs
|
||||
echo "${{ secrets.CROSSDESK_CERT_BASE64 }}" | base64 --decode > certs/crossdesk.cn_root.crt
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
chmod +x ${{ matrix.package_script }}
|
||||
${{ matrix.package_script }} ${LEGAL_VERSION}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: crossdesk-linux-${{ matrix.arch }}-${{ env.LEGAL_VERSION }}
|
||||
path: ${{ github.workspace }}/crossdesk-linux-${{ matrix.arch }}-${{ env.LEGAL_VERSION }}.deb
|
||||
|
||||
# macOS
|
||||
build-macos:
|
||||
name: Build on macOS
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x64
|
||||
runner: macos-15-intel
|
||||
cache-key: intel
|
||||
out-dir: ./build/macosx/x86_64/release/crossdesk
|
||||
package_script: ./scripts/macosx/pkg_x64.sh
|
||||
- arch: arm64
|
||||
runner: macos-14
|
||||
cache-key: arm
|
||||
out-dir: ./build/macosx/arm64/release/crossdesk
|
||||
package_script: ./scripts/macosx/pkg_arm64.sh
|
||||
|
||||
steps:
|
||||
- name: Extract version number
|
||||
id: version
|
||||
run: |
|
||||
VERSION="${GITHUB_REF##*/}"
|
||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||
VERSION_NUM="${VERSION#v}-${SHORT_SHA}"
|
||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV
|
||||
echo "VERSION_NUM=${VERSION_NUM}"
|
||||
|
||||
- name: Cache xmake dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.xmake/packages
|
||||
key: ${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}-${{ hashFiles('**/xmake.lua') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}-
|
||||
|
||||
- name: Install xmake
|
||||
run: brew install xmake
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Build CrossDesk
|
||||
run: |
|
||||
xmake f --CROSSDESK_VERSION=${VERSION_NUM} -y
|
||||
xmake b -vy crossdesk
|
||||
|
||||
- name: Decode and save certificate
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p certs
|
||||
echo "${{ secrets.CROSSDESK_CERT_BASE64 }}" | base64 --decode > certs/crossdesk.cn_root.crt
|
||||
|
||||
- name: Package CrossDesk app
|
||||
run: |
|
||||
chmod +x ${{ matrix.package_script }}
|
||||
${{ matrix.package_script }} ${VERSION_NUM}
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: crossdesk-macos-${{ matrix.arch }}-${{ env.VERSION_NUM }}
|
||||
path: crossdesk-macos-${{ matrix.arch }}-${{ env.VERSION_NUM }}.pkg
|
||||
|
||||
- name: Move files to release dir
|
||||
run: |
|
||||
mkdir -p release
|
||||
cp crossdesk-macos-${{ matrix.arch }}-${{ env.VERSION_NUM }}.pkg release/
|
||||
|
||||
# Windows
|
||||
build-windows-x64:
|
||||
name: Build on Windows x64
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
XMAKE_GLOBALDIR: D:\xmake_global
|
||||
steps:
|
||||
- name: Extract version number
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ref = $env:GITHUB_REF
|
||||
$version = $ref -replace '^refs/(tags|heads)/', ''
|
||||
$version = $version -replace '^v', ''
|
||||
$version = $version -replace '/', '-'
|
||||
$SHORT_SHA = $env:GITHUB_SHA.Substring(0,7)
|
||||
echo "VERSION_NUM=$version-$SHORT_SHA" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Cache xmake dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: D:\xmake_global\.xmake\packages
|
||||
key: ${{ runner.os }}-xmake-deps-intel-${{ hashFiles('**/xmake.lua') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-xmake-deps-intel-
|
||||
|
||||
- name: Install xmake
|
||||
run: |
|
||||
Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.ps1' -UseBasicParsing).Content
|
||||
echo "C:\Users\runneradmin\xmake" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
xmake create cuda
|
||||
Set-Location cuda
|
||||
xmake g --theme=plain
|
||||
$cudaPath = ""
|
||||
$packagesPath = "D:\xmake_global\.xmake\packages"
|
||||
|
||||
if (Test-Path $packagesPath) {
|
||||
Write-Host "Packages directory exists: $packagesPath"
|
||||
try {
|
||||
$info = xmake require --info "cuda 12.6.3" 2>$null
|
||||
if ($null -ne $info -and $info -ne "") {
|
||||
$cudaPath = (($info | Select-String installdir).ToString() -replace '.*installdir:\s*','').Trim()
|
||||
}
|
||||
} catch {}
|
||||
} else {
|
||||
Write-Host "Packages directory not found: $packagesPath"
|
||||
Write-Host "Installing CUDA package..."
|
||||
xmake require -vy "cuda 12.6.3"
|
||||
$info = xmake require --info "cuda 12.6.3"
|
||||
$cudaPath = (($info | Select-String installdir).ToString() -replace '.*installdir:\s*','').Trim()
|
||||
}
|
||||
|
||||
echo "CUDA_PATH=$cudaPath" >> $env:GITHUB_ENV
|
||||
Write-Host "Resolved CUDA_PATH = $cudaPath"
|
||||
Pop-Location
|
||||
|
||||
- name: Download INetC plugin
|
||||
shell: powershell
|
||||
run: |
|
||||
$url = "https://github.com/DigitalMediaServer/NSIS-INetC-plugin/releases/download/v1.0.5.7/InetC.zip"
|
||||
$out = "$env:RUNNER_TEMP\InetC.zip"
|
||||
Invoke-WebRequest -Uri $url -OutFile $out
|
||||
Expand-Archive $out -DestinationPath "$env:RUNNER_TEMP\InetC" -Force
|
||||
|
||||
$source = "$env:RUNNER_TEMP\InetC\x86-unicode\INetC.dll"
|
||||
$target = "C:\Program Files (x86)\NSIS\Plugins\x86-unicode\INetC.dll"
|
||||
|
||||
Write-Host "Copying $source to $target"
|
||||
Copy-Item $source $target -Force
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Copy nsProcess plugin to NSIS folder
|
||||
run: |
|
||||
$nsisPluginDir = "C:\Program Files (x86)\NSIS\Plugins\x86-unicode"
|
||||
copy "${{ github.workspace }}\scripts\windows\nsProcess.dll" $nsisPluginDir
|
||||
|
||||
- name: Build CrossDesk
|
||||
run: |
|
||||
xmake f --CROSSDESK_VERSION=${{ env.VERSION_NUM }} -y
|
||||
xmake b -vy crossdesk
|
||||
|
||||
- name: Decode and save certificate
|
||||
shell: powershell
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path certs
|
||||
[System.IO.File]::WriteAllBytes('certs\crossdesk.cn_root.crt', [Convert]::FromBase64String('${{ secrets.CROSSDESK_CERT_BASE64 }}'))
|
||||
|
||||
- name: Package
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd "${{ github.workspace }}\scripts\windows"
|
||||
makensis /DVERSION=$env:VERSION_NUM nsis_script.nsi
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: crossdesk-win-x64-${{ env.VERSION_NUM }}
|
||||
path: ${{ github.workspace }}/scripts/windows/crossdesk-win-x64-${{ env.VERSION_NUM }}.exe
|
||||
|
||||
release:
|
||||
name: Publish Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs:
|
||||
[build-linux-amd64, build-linux-arm64, build-macos, build-windows-x64]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Extract version number
|
||||
id: version
|
||||
run: |
|
||||
VERSION="${GITHUB_REF##*/}"
|
||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||
VERSION_NUM="${VERSION#v}-${SHORT_SHA}"
|
||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Rename artifacts
|
||||
run: |
|
||||
mkdir -p release
|
||||
cp artifacts/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_NUM }}.pkg
|
||||
cp artifacts/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_NUM }}.pkg
|
||||
cp artifacts/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_NUM }}.deb
|
||||
cp artifacts/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_NUM }}.deb
|
||||
cp artifacts/crossdesk-win-x64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-win-x64-${{ steps.version.outputs.VERSION_NUM }}.exe
|
||||
|
||||
- name: List release files
|
||||
run: ls -lh release/
|
||||
|
||||
- name: Upload to Versioned GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: v${{ steps.version.outputs.VERSION_NUM }}
|
||||
name: Release v${{ steps.version.outputs.VERSION_NUM }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
files: release/*
|
||||
generate_release_notes: false
|
||||
body: |
|
||||
Binary release only. Source code is not included.
|
||||
|
||||
- name: Create or update 'latest' tag
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag -f latest
|
||||
git push origin latest --force
|
||||
|
||||
- name: Upload to GitHub Release (latest)
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: latest
|
||||
name: Latest Release
|
||||
draft: false
|
||||
prerelease: false
|
||||
files: release/*
|
||||
generate_release_notes: false
|
||||
|
||||
- name: Upload artifacts to server
|
||||
uses: burnett01/rsync-deployments@5.2
|
||||
with:
|
||||
switches: -avzr --progress --delete
|
||||
path: release/*
|
||||
remote_path: /var/www/html/downloads/
|
||||
remote_host: ${{ secrets.SERVER_HOST }}
|
||||
remote_user: ${{ secrets.SERVER_USER }}
|
||||
remote_key: ${{ secrets.SERVER_KEY }}
|
||||
65
.github/workflows/close-issue.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Close Inactive Issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run every day at midnight
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
close_inactive_issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check inactive issues and close them
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const { data: issues } = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const now = new Date().getTime();
|
||||
const inactivePeriod = 7 * 24 * 60 * 60 * 1000; // 7 days
|
||||
|
||||
for (const issue of issues) {
|
||||
// skip pull requests (they are also returned by listForRepo)
|
||||
if (issue.pull_request) continue;
|
||||
|
||||
// skip labeled issues
|
||||
if (issue.labels.length > 0) {
|
||||
console.log(`Skipping issue #${issue.number} (Has labels).`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// fetch comments for this issue
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
// determine the "last activity" time
|
||||
let lastActivityTime;
|
||||
if (comments.length > 0) {
|
||||
const lastComment = comments[comments.length - 1];
|
||||
lastActivityTime = new Date(lastComment.updated_at).getTime();
|
||||
} else {
|
||||
lastActivityTime = new Date(issue.created_at).getTime();
|
||||
}
|
||||
|
||||
// check inactivity
|
||||
if (now - lastActivityTime > inactivePeriod) {
|
||||
console.log(`Closing inactive issue: #${issue.number} (No recent replies for 7 days)`);
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed',
|
||||
});
|
||||
} else {
|
||||
console.log(`Skipping issue #${issue.number} (Active within 7 days).`);
|
||||
}
|
||||
}
|
||||
186
.github/workflows/release.yaml
vendored
@@ -1,186 +0,0 @@
|
||||
name: Build and Release CrossDesk
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [release]
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
# Linux
|
||||
build-linux:
|
||||
name: Build on Ubuntu
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: crossdesk/ubuntu22.04:latest
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Build CrossDesk
|
||||
env:
|
||||
CUDA_PATH: /usr/local/cuda
|
||||
XMAKE_GLOBALDIR: /data
|
||||
run: |
|
||||
ls -la $XMAKE_GLOBALDIR
|
||||
xmake b -vy --root crossdesk
|
||||
|
||||
- name: Decode and save certificate
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p certs
|
||||
echo "${{ secrets.CROSSDESK_CERT_BASE64 }}" | base64 --decode > certs/crossdesk.cn_root.crt
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
chmod +x ./scripts/linux/pkg_x86_64.sh
|
||||
./scripts/linux/pkg_x86_64.sh
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: crossdesk-linux-x86_64
|
||||
path: ${{ github.workspace }}/CrossDesk-0.0.1.deb
|
||||
|
||||
# macOS
|
||||
build-macos:
|
||||
name: Build on macOS
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
runner: macos-13
|
||||
cache-key: intel
|
||||
out-dir: ./build/macosx/x86_64/release/crossdesk
|
||||
artifact-name: crossdesk-macos-x86_64
|
||||
package_script: ./scripts/macosx/pkg_x86_64.sh
|
||||
- arch: arm64
|
||||
runner: macos-14
|
||||
cache-key: arm
|
||||
out-dir: ./build/macosx/arm64/release/crossdesk
|
||||
artifact-name: crossdesk-macos-arm64
|
||||
package_script: ./scripts/macosx/pkg_arm64.sh
|
||||
steps:
|
||||
- name: Cache xmake dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.xmake/packages
|
||||
key: ${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}-${{ hashFiles('**/xmake.lua') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}-
|
||||
|
||||
- name: Install xmake
|
||||
run: brew install xmake
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Build CrossDesk
|
||||
run: xmake b -vy crossdesk
|
||||
|
||||
- name: Decode and save certificate
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p certs
|
||||
echo "${{ secrets.CROSSDESK_CERT_BASE64 }}" | base64 --decode > certs/crossdesk.cn_root.crt
|
||||
|
||||
- name: Package CrossDesk app
|
||||
run: |
|
||||
chmod +x ${{ matrix.package_script }}
|
||||
${{ matrix.package_script }}
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.artifact-name }}
|
||||
path: crossdesk-macos-${{ matrix.arch }}-v0.0.1.pkg
|
||||
|
||||
- name: Move files to release dir
|
||||
run: |
|
||||
mkdir -p release
|
||||
cp crossdesk-macos-${{ matrix.arch }}-v0.0.1.pkg release/
|
||||
|
||||
# Windows
|
||||
build-windows:
|
||||
name: Build on Windows
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
XMAKE_GLOBALDIR: D:\xmake_global
|
||||
steps:
|
||||
- name: Cache xmake dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: D:\xmake_global\.xmake\packages
|
||||
key: ${{ runner.os }}-xmake-deps-intel-${{ hashFiles('**/xmake.lua') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-xmake-deps-intel-
|
||||
|
||||
- name: Install xmake
|
||||
run: |
|
||||
Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.ps1' -UseBasicParsing).Content
|
||||
echo "C:\Users\runneradmin\xmake" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
xmake create cuda
|
||||
Set-Location cuda
|
||||
xmake g --theme=plain
|
||||
$cudaPath = ""
|
||||
$packagesPath = "D:\xmake_global\.xmake\packages"
|
||||
|
||||
if (Test-Path $packagesPath) {
|
||||
Write-Host "Packages directory exists: $packagesPath"
|
||||
try {
|
||||
$info = xmake require --info "cuda 12.6.3" 2>$null
|
||||
if ($null -ne $info -and $info -ne "") {
|
||||
$cudaPath = (($info | Select-String installdir).ToString() -replace '.*installdir:\s*','').Trim()
|
||||
}
|
||||
} catch {}
|
||||
} else {
|
||||
Write-Host "Packages directory not found: $packagesPath"
|
||||
Write-Host "Installing CUDA package..."
|
||||
xmake require -vy "cuda 12.6.3"
|
||||
$info = xmake require --info "cuda 12.6.3"
|
||||
$cudaPath = (($info | Select-String installdir).ToString() -replace '.*installdir:\s*','').Trim()
|
||||
}
|
||||
|
||||
echo "CUDA_PATH=$cudaPath" >> $env:GITHUB_ENV
|
||||
Write-Host "Resolved CUDA_PATH = $cudaPath"
|
||||
Pop-Location
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Build CrossDesk
|
||||
run: xmake b -vy crossdesk
|
||||
|
||||
- name: Decode and save certificate
|
||||
shell: powershell
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path certs
|
||||
[System.IO.File]::WriteAllBytes('certs\crossdesk.cn_root.crt', [Convert]::FromBase64String('${{ secrets.CROSSDESK_CERT_BASE64 }}'))
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
cd ./scripts/windows
|
||||
makensis nsis_script.nsi
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: crossdesk-win-x86_64
|
||||
path: ${{ github.workspace }}/scripts/windows/CrossDesk-0.0.1.exe
|
||||
49
.github/workflows/update-pages.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: Update GitHub Pages Downloads
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
update-pages:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout CrossDesk repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set version number
|
||||
id: version
|
||||
run: |
|
||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||
VERSION_NUM="${GITHUB_REF##*/}"
|
||||
VERSION_NUM="${VERSION_NUM#v}-${SHORT_SHA}"
|
||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV
|
||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Checkout Pages repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: kunkundi/kunkundi.github.io
|
||||
token: ${{ secrets.GH_PAGES_PAT }}
|
||||
path: pages
|
||||
|
||||
- name: Update download links
|
||||
run: |
|
||||
cd pages
|
||||
sed -E -i "s/crossdesk-win-x64-[0-9]+\.[0-9]+\.[0-9]+\.exe/crossdesk-win-x64-${VERSION_NUM}.exe/g" index.html
|
||||
sed -E -i "s/crossdesk-macos-x64-[0-9]+\.[0-9]+\.[0-9]+\.pkg/crossdesk-macos-x64-${VERSION_NUM}.pkg/g" index.html
|
||||
sed -E -i "s/crossdesk-macos-arm64-[0-9]+\.[0-9]+\.[0-9]+\.pkg/crossdesk-macos-arm64-${VERSION_NUM}.pkg/g" index.html
|
||||
sed -E -i "s/crossdesk-linux-amd64-[0-9]+\.[0-9]+\.[0-9]+\.deb/crossdesk-linux-amd64-${VERSION_NUM}.deb/g" index.html
|
||||
sed -E -i "s/crossdesk-linux-arm64-[0-9]+\.[0-9]+\.[0-9]+\.deb/crossdesk-linux-arm64-${VERSION_NUM}.deb/g" index.html
|
||||
|
||||
- name: Commit & Push changes
|
||||
run: |
|
||||
cd pages
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add index.html
|
||||
git commit -m "Update download links to v${VERSION_NUM}" || echo "No changes to commit"
|
||||
git push origin main
|
||||
env:
|
||||
VERSION_NUM: ${{ env.VERSION_NUM }}
|
||||
39
Info.plist
@@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- 应用的Bundle identifier,通常使用反向域名标记 -->
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.yourcompany.yourappname</string>
|
||||
|
||||
<!-- 应用的显示名称 -->
|
||||
<key>CFBundleName</key>
|
||||
<string>Your App Name</string>
|
||||
|
||||
<!-- 应用的版本号 -->
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
|
||||
<!-- 应用的构建版本号 -->
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
|
||||
<!-- 请求麦克风访问权限 -->
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>App requires access to the microphone for audio recording.</string>
|
||||
|
||||
<!-- 请求相机访问权限 -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>App requires access to the camera for video recording.</string>
|
||||
|
||||
<!-- 请求使用连续相机设备 -->
|
||||
<key>NSCameraUseContinuityCameraDeviceType</key>
|
||||
<string>Your usage description here</string>
|
||||
|
||||
<!-- High DPI -->>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
|
||||
<!-- 其他权限和配置可以在这里添加 -->
|
||||
</dict>
|
||||
</plist>
|
||||
177
LICENSE
@@ -1,20 +1,165 @@
|
||||
The MIT License (MIT)
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (c) 2023 The Continuous Desk Authors
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
299
README.md
@@ -1,67 +1,300 @@
|
||||
# CrossDesk
|
||||
|
||||
#### More than remote desktop
|
||||
[]()
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
||||
[](https://github.com/kunkundi/crossdesk/actions)
|
||||
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
----
|
||||
[中文](README_CN.md) / [English](README.md)
|
||||
[ [English](README_EN.md) / 中文 ]
|
||||
|
||||

|
||||

|
||||
|
||||
# Intro
|
||||
## 简介
|
||||
|
||||
CrossDesk is a lightweight cross-platform remote desktop. It allows multiple users to remotely control the same computer at the same time. In addition to desktop image transmission, it also supports end-to-end voice transmission, providing collaboration capabilities on the basis of remote desktop.
|
||||
CrossDesk 是一个轻量级的跨平台远程桌面软件。
|
||||
|
||||
CrossDesk is an experimental application of [Projectx](https://github.com/dijunkun/projectx) real-time communications library. Projectx is a lightweight cross-platform real-time communications library. It has basic capabilities such as network traversal ([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)), video software/hardware encoding/decoding (H264), audio encoding/decoding ([Opus](https://github.com/xiph/opus)), signaling interaction, and network congestion control ([TCP over UDP](https://libnice.freedesktop.org/)).
|
||||
CrossDesk 是 [MiniRTC](https://github.com/kunkundi/minirtc.git) 实时音视频传输库的实验性应用。MiniRTC 是一个轻量级的跨平台实时音视频传输库。它具有网络透传([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)),视频软硬编解码(H264/AV1),音频编解码([Opus](https://github.com/xiph/opus)),信令交互,网络拥塞控制,传输加密([SRTP](https://tools.ietf.org/html/rfc3711))等基础能力。
|
||||
|
||||
## Usage
|
||||
|
||||
Enter the remote desktop ID in the 'REMOTE ID' field on the menu bar, and click 'Connect' button to initiate the remote connection.
|
||||
## 使用
|
||||
|
||||

|
||||
在菜单栏“对端ID”处输入远端桌面的ID,点击“→”即可发起远程连接。
|
||||
|
||||
If the remote desktop is set with a connection password, the local end needs to enter the correct password to initiate the remote connection. If the password is incorrect, an "Incorrect password" alert will appear in the status bar.
|
||||

|
||||
|
||||

|
||||
如果远端桌面设置了连接密码,则本端需填写正确的连接密码才能成功发起远程连接。
|
||||
|
||||
After connection successfully established, the status bar will display the message "ClientConnected."
|
||||

|
||||
|
||||

|
||||
发起连接前,可在设置中自定义配置项,如语言、视频编码格式等。
|
||||

|
||||
|
||||
## How to build
|
||||
## 如何编译
|
||||
|
||||
Requirements:
|
||||
依赖:
|
||||
- [xmake](https://xmake.io/#/guide/installation)
|
||||
- [cmake](https://cmake.org/download/)
|
||||
- [vcpkg](https://vcpkg.io/en/getting-started)
|
||||
|
||||
Following packages need to be installed on Linux:
|
||||
Linux环境下需安装以下包:
|
||||
|
||||
```
|
||||
sudo apt-get install -y nvidia-cuda-toolkit libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxcb-shm0-dev libxv-dev libasound2-dev libsndio-dev libasound2-dev libpulse-dev
|
||||
sudo apt-get install -y software-properties-common git curl unzip build-essential libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxv-dev libxtst-dev libasound2-dev libsndio-dev libxcb-shm0-dev libasound2-dev libpulse-dev
|
||||
```
|
||||
|
||||
Commands:
|
||||
编译
|
||||
```
|
||||
git clone https://github.com/dijunkun/continuous-desk
|
||||
git clone https://github.com/kunkundi/crossdesk.git
|
||||
|
||||
cd continuous-desk
|
||||
cd crossdesk
|
||||
|
||||
git submodule init
|
||||
|
||||
git submodule update
|
||||
|
||||
xmake b remote_desk
|
||||
```
|
||||
Run:
|
||||
```
|
||||
# Windows/MacOS
|
||||
xmake r remote_desk
|
||||
|
||||
# root privileges are required on Linux
|
||||
./remote_desk
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
运行
|
||||
```
|
||||
xmake r crossdesk
|
||||
```
|
||||
|
||||
Continuous Desk is licenced under MIT, and some third-party libraries are distributed under their licenses.
|
||||
### 无 CUDA 环境下的开发支持
|
||||
|
||||
对于**未安装 CUDA 环境的 Linux 开发者**,这里提供了预配置的 [Ubuntu 22.04 Docker 镜像](https://hub.docker.com/r/crossdesk/ubuntu22.04)。该镜像内置必要的构建依赖,可在容器中开箱即用,无需额外配置即可直接编译项目。
|
||||
|
||||
进入容器,下载工程后执行:
|
||||
```
|
||||
export CUDA_PATH=/usr/local/cuda
|
||||
export XMAKE_GLOBALDIR=/data
|
||||
|
||||
xmake b --root -vy crossdesk
|
||||
```
|
||||
|
||||
对于**未安装 CUDA 环境的 Windows 开发者**,执行下面的命令安装 CUDA 编译环境:
|
||||
```
|
||||
xmake require -vy "cuda 12.6.3"
|
||||
```
|
||||
安装完成后执行:
|
||||
```
|
||||
xmake require --info "cuda 12.6.3"
|
||||
```
|
||||
输出如下:
|
||||
|
||||
<img width="860" height="226" alt="Image" src="https://github.com/user-attachments/assets/999ac365-581a-4b9a-806e-05eb3e4cf44d" />
|
||||
|
||||
根据上述输出获取到 CUDA 的安装目录,即 installdir 指向的位置。将 CUDA_PATH 加入系统环境变量,或在终端中输入:
|
||||
```
|
||||
set CUDA_PATH=path_to_cuda_installdir
|
||||
```
|
||||
重新执行:
|
||||
```
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
#### 注意
|
||||
运行时如果客户端状态栏显示 **未连接服务器**,请先在 [CrossDesk 官方网站](https://www.crossdesk.cn/) 安装客户端,以便在环境中安装所需的证书文件。
|
||||
|
||||
<img width="256" height="120" alt="image" src="https://github.com/user-attachments/assets/1812f7d6-516b-4b4f-8a3d-98bee505cc5a" />
|
||||
|
||||
## 关于 Xmake
|
||||
|
||||
#### 安装 Xmake
|
||||
使用 curl:
|
||||
```
|
||||
curl -fsSL https://xmake.io/shget.text | bash
|
||||
```
|
||||
使用 wget:
|
||||
```
|
||||
wget https://xmake.io/shget.text -O - | bash
|
||||
```
|
||||
使用 powershell:
|
||||
```
|
||||
irm https://xmake.io/psget.text | iex
|
||||
```
|
||||
|
||||
#### 编译选项
|
||||
```
|
||||
# 切换编译模式
|
||||
xmake f -m debug/release
|
||||
|
||||
# 可选编译参数
|
||||
-r :重新构建目标
|
||||
-v :显示详细的构建日志
|
||||
-y :自动确认提示
|
||||
|
||||
# 示例
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
#### 运行选项
|
||||
```
|
||||
# 使用调试模式运行
|
||||
xmake r -d crossdesk
|
||||
```
|
||||
更多使用方法可参考 [Xmake官方文档](https://xmake.io/guide/quick-start.html) 。
|
||||
|
||||
## 自托管服务器
|
||||
推荐使用Docker部署CrossDesk Server。
|
||||
```
|
||||
sudo docker run -d \
|
||||
--name crossdesk_server \
|
||||
--network host \
|
||||
-e EXTERNAL_IP=xxx.xxx.xxx.xxx \
|
||||
-e INTERNAL_IP=xxx.xxx.xxx.xxx \
|
||||
-e CROSSDESK_SERVER_PORT=9099 \
|
||||
-v /path/to/your/certs:/crossdesk-server/certs \
|
||||
-v /path/to/your/db:/crossdesk-server/db \
|
||||
-v /path/to/your/logs:/crossdesk-server/logs \
|
||||
crossdesk/crossdesk-server:latest
|
||||
```
|
||||
|
||||
上述命令中,用户需注意的参数如下:
|
||||
|
||||
- EXTERNAL_IP:服务器公网 IP , 对应 CrossDesk 客户端**自托管服务器配置**中填写的**服务器地址**
|
||||
|
||||
- INTERNAL_IP:服务器内网 IP
|
||||
|
||||
- CROSSDESK_SERVER_PORT:自托管服务使用的端口,对应 CrossDesk 客户端**自托管服务器配置**中填写的**服务器端口**
|
||||
|
||||
- /path/to/your/certs:证书文件目录
|
||||
|
||||
- /path/to/your/db:CrossDesk Server 设备管理数据库
|
||||
|
||||
- /path/to/your/logs:日志目录
|
||||
|
||||
**注意**:
|
||||
- **/path/to/your/ 是示例路径,请替换为你自己的实际路径。挂载的目录必须事先创建好,否则容器会报错。**
|
||||
- **服务器需开放端口:3478/udp,3478/tcp,30000-60000/udp,CROSSDESK_SERVER_PORT/tcp,443/tcp。**
|
||||
|
||||
## 证书文件
|
||||
客户端需加载根证书文件,服务端需加载服务器私钥和服务器证书文件。
|
||||
|
||||
如果已有SSL证书的用户,可以忽略下面的证书生成步骤。
|
||||
|
||||
对于无证书的用户,可使用下面的脚本自行生成证书文件:
|
||||
```
|
||||
# 创建证书生成脚本
|
||||
vim generate_certs.sh
|
||||
```
|
||||
拷贝到脚本中
|
||||
```
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# 检查参数
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <SERVER_IP>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SERVER_IP="$1"
|
||||
|
||||
# 文件名
|
||||
ROOT_KEY="crossdesk.cn_root.key"
|
||||
ROOT_CERT="crossdesk.cn_root.crt"
|
||||
SERVER_KEY="crossdesk.cn.key"
|
||||
SERVER_CSR="crossdesk.cn.csr"
|
||||
SERVER_CERT="crossdesk.cn_bundle.crt"
|
||||
FULLCHAIN_CERT="crossdesk.cn_fullchain.crt"
|
||||
|
||||
# 证书主题
|
||||
SUBJ="/C=CN/ST=Zhejiang/L=Hangzhou/O=CrossDesk/OU=CrossDesk/CN=$SERVER_IP"
|
||||
|
||||
# 1. 生成根证书
|
||||
echo "Generating root private key..."
|
||||
openssl genrsa -out "$ROOT_KEY" 4096
|
||||
|
||||
echo "Generating self-signed root certificate..."
|
||||
openssl req -x509 -new -nodes -key "$ROOT_KEY" -sha256 -days 3650 -out "$ROOT_CERT" -subj "$SUBJ"
|
||||
|
||||
# 2. 生成服务器私钥
|
||||
echo "Generating server private key..."
|
||||
openssl genrsa -out "$SERVER_KEY" 2048
|
||||
|
||||
# 3. 生成服务器 CSR
|
||||
echo "Generating server CSR..."
|
||||
openssl req -new -key "$SERVER_KEY" -out "$SERVER_CSR" -subj "$SUBJ"
|
||||
|
||||
# 4. 生成临时 OpenSSL 配置文件,加入 SAN
|
||||
SAN_CONF="san.cnf"
|
||||
cat > $SAN_CONF <<EOL
|
||||
[ req ]
|
||||
default_bits = 2048
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = req_ext
|
||||
prompt = no
|
||||
|
||||
[ req_distinguished_name ]
|
||||
C = CN
|
||||
ST = Zhejiang
|
||||
L = Hangzhou
|
||||
O = CrossDesk
|
||||
OU = CrossDesk
|
||||
CN = $SERVER_IP
|
||||
|
||||
[ req_ext ]
|
||||
subjectAltName = IP:$SERVER_IP
|
||||
EOL
|
||||
|
||||
# 5. 用根证书签发服务器证书(包含 SAN)
|
||||
echo "Signing server certificate with root certificate..."
|
||||
openssl x509 -req -in "$SERVER_CSR" -CA "$ROOT_CERT" -CAkey "$ROOT_KEY" -CAcreateserial \
|
||||
-out "$SERVER_CERT" -days 3650 -sha256 -extfile "$SAN_CONF" -extensions req_ext
|
||||
|
||||
# 6. 生成完整链证书
|
||||
cat "$SERVER_CERT" "$ROOT_CERT" > "$FULLCHAIN_CERT"
|
||||
|
||||
# 7. 清理中间文件
|
||||
rm -f "$ROOT_CERT.srl" "$SAN_CONF" "$ROOT_KEY" "$SERVER_CSR" "FULLCHAIN_CERT"
|
||||
|
||||
echo "Generation complete. Deployment files:"
|
||||
echo " Client root certificate: $ROOT_CERT"
|
||||
echo " Server private key: $SERVER_KEY"
|
||||
echo " Server certificate: $SERVER_CERT"
|
||||
```
|
||||
执行
|
||||
```
|
||||
chmod +x generate_certs.sh
|
||||
./generate_certs.sh 服务器公网IP
|
||||
|
||||
# 例如 ./generate_certs.sh 111.111.111.111
|
||||
```
|
||||
输出如下:
|
||||
```
|
||||
Generating root private key...
|
||||
Generating self-signed root certificate...
|
||||
Generating server private key...
|
||||
Generating server CSR...
|
||||
Signing server certificate with root certificate...
|
||||
Certificate request self-signature ok
|
||||
subject=C = CN, ST = Zhejiang, L = Hangzhou, O = CrossDesk, OU = CrossDesk, CN = xxx.xxx.xxx.xxx
|
||||
cleaning up intermediate files...
|
||||
Generation complete. Deployment files::
|
||||
Client root certificate:: crossdesk.cn_root.crt
|
||||
Server private key: crossdesk.cn.key
|
||||
Server certificate: crossdesk.cn_bundle.crt
|
||||
```
|
||||
|
||||
#### 服务端
|
||||
将 **crossdesk.cn.key** 和 **crossdesk.cn_bundle.crt** 放置到 **/path/to/your/certs** 目录下。
|
||||
|
||||
#### 客户端
|
||||
1. 点击右上角设置进入设置页面。<br>
|
||||
<img width="600" height="210" alt="image" src="https://github.com/user-attachments/assets/6431131d-b32a-4726-8783-6788f47baa3b" /><br><br>
|
||||
|
||||
3. 点击点击**自托管服务器配置**。<br><br>
|
||||
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/24c761a3-1985-4d7e-84be-787383c2afb8" /><br><br>
|
||||
|
||||
5. 在**证书文件路径**选择框中找到 **crossdesk.cn_root.crt** 的存放路径,选中 **crossdesk.cn_root.crt**,点击确认。<br><br>
|
||||
<img width="600" height="220" alt="image" src="https://github.com/user-attachments/assets/4af7cd3a-c72e-44fb-b032-30e050019c2a" /><br><br>
|
||||
|
||||
7. 勾选使用**自托管服务器配置**,点击确认配置生效。<br><br>
|
||||
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/1e455dc3-4087-4f37-a544-1ff9f8789383" /><br><br>
|
||||
|
||||
# 常见问题
|
||||
见 [常见问题](https://github.com/kunkundi/crossdesk/blob/self-hosted-server/docs/FAQ.md) 。
|
||||
67
README_CN.md
@@ -1,67 +0,0 @@
|
||||
# Continuous Desk
|
||||
|
||||
#### 不止远程桌面
|
||||
|
||||
----
|
||||
[English](README.md) / [中文](README_CN.md)
|
||||
|
||||

|
||||
|
||||
## 简介
|
||||
|
||||
Continuous Desk 是一个轻量级的跨平台远程桌面软件。它允许多个用户在同一时间远程操控同一台电脑。除桌面图像传输外,它还支持端到端的语音传输,在远程桌面基础上提供额外的协作能力。
|
||||
|
||||
Continuous Desk 是 [Projectx](https://github.com/dijunkun/projectx) 实时音视频传输库的实验性应用。Projectx 是一个轻量级的跨平台实时音视频传输库。它具有网络透传([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)),视频软硬编解码(H264),音频编解码([Opus](https://github.com/xiph/opus)),信令交互,网络拥塞控制([TCP over UDP](https://libnice.freedesktop.org/))等基础能力。
|
||||
|
||||
|
||||
## 使用
|
||||
|
||||
在菜单栏“REMOTE ID”处输入远端桌面的ID,点击“Connect”即可发起远程连接。
|
||||
|
||||

|
||||
|
||||
如果远端桌面设置了连接密码,则本端需填写正确的连接密码才能成功发起远程连接。密码错误时,状态栏会出现“Incorrect password”告警提示。
|
||||
|
||||

|
||||
|
||||
连接成功建立后,状态栏会有“ClientConnected”相关字样。
|
||||
|
||||

|
||||
|
||||
## 编译
|
||||
|
||||
依赖:
|
||||
- [xmake](https://xmake.io/#/guide/installation)
|
||||
- [cmake](https://cmake.org/download/)
|
||||
- [vcpkg](https://vcpkg.io/en/getting-started)
|
||||
|
||||
Linux环境下需安装以下包:
|
||||
|
||||
```
|
||||
sudo apt-get install -y nvidia-cuda-toolkit libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxcb-shm0-dev libxv-dev libasound2-dev libsndio-dev libasound2-dev libpulse-dev
|
||||
```
|
||||
|
||||
编译命令
|
||||
```
|
||||
git clone https://github.com/dijunkun/continuous-desk
|
||||
|
||||
cd continuous-desk
|
||||
|
||||
git submodule init
|
||||
|
||||
git submodule update
|
||||
|
||||
xmake b remote_desk
|
||||
```
|
||||
运行
|
||||
```
|
||||
# Windows/MacOS
|
||||
xmake r remote_desk
|
||||
|
||||
# Linux下需使用root权限运行
|
||||
./remote_desk
|
||||
```
|
||||
|
||||
## 许可证
|
||||
|
||||
Continuous Desk 使用 MIT 许可证,其中使用到的第三方库根据自身许可证进行分发。
|
||||
305
README_EN.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# CrossDesk
|
||||
|
||||
[]()
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
||||
[](https://github.com/kunkundi/crossdesk/actions)
|
||||
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
|
||||
[ [中文](README.md) / English ]
|
||||
|
||||

|
||||
|
||||
# Intro
|
||||
|
||||
CrossDesk is a lightweight cross-platform remote desktop software.
|
||||
|
||||
CrossDesk is an experimental application of [MiniRTC](https://github.com/kunkundi/minirtc.git), a lightweight cross-platform real-time audio and video transmission library. MiniRTC provides fundamental capabilities including network traversal ([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)), video software/hardware encoding and decoding (H264/AV1), audio encoding/decoding ([Opus](https://github.com/xiph/opus)), signaling interaction, network congestion control, and transmission encryption ([SRTP](https://tools.ietf.org/html/rfc3711)).
|
||||
|
||||
## Usage
|
||||
|
||||
Enter the remote desktop ID in the menu bar’s “Remote ID” field and click “→” to initiate a remote connection.
|
||||
|
||||

|
||||
|
||||
If the remote desktop requires a connection password, you must enter the correct password on your side to successfully establish the connection.
|
||||
|
||||

|
||||
|
||||
Before connecting, you can customize configuration options in the settings, such as language and video encoding format.
|
||||
|
||||

|
||||
|
||||
## How to build
|
||||
|
||||
Requirements:
|
||||
- [xmake](https://xmake.io/#/guide/installation)
|
||||
- [cmake](https://cmake.org/download/)
|
||||
|
||||
Following packages need to be installed on Linux:
|
||||
|
||||
```
|
||||
sudo apt-get install -y software-properties-common git curl unzip build-essential libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxv-dev libxtst-dev libasound2-dev libsndio-dev libxcb-shm0-dev libasound2-dev libpulse-dev
|
||||
```
|
||||
|
||||
Build:
|
||||
```
|
||||
git clone https://github.com/kunkundi/crossdesk.git
|
||||
|
||||
cd crossdesk
|
||||
|
||||
git submodule init
|
||||
|
||||
git submodule update
|
||||
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
Run:
|
||||
```
|
||||
xmake r crossdesk
|
||||
```
|
||||
|
||||
#### Development Without CUDA Environment
|
||||
|
||||
For **Linux developers who do not have a CUDA environment** installed, a preconfigured [Ubuntu 22.04 Docker image](https://hub.docker.com/r/crossdesk/ubuntu22.04) is provided.
|
||||
This image comes with all required build dependencies and allows you to build the project directly inside the container without any additional setup.
|
||||
|
||||
After entering the container, download the project and run:
|
||||
```
|
||||
export CUDA_PATH=/usr/local/cuda
|
||||
export XMAKE_GLOBALDIR=/data
|
||||
|
||||
xmake b --root -vy crossdesk
|
||||
```
|
||||
|
||||
For **Windows developers without a CUDA environment** installed, run the following command to install the CUDA build environment:
|
||||
```
|
||||
xmake require -vy "cuda 12.6.3"
|
||||
```
|
||||
After the installation is complete, execute:
|
||||
```
|
||||
xmake require --info "cuda 12.6.3"
|
||||
```
|
||||
The output will look like this:
|
||||
|
||||
<img width="860" height="226" alt="Image" src="https://github.com/user-attachments/assets/999ac365-581a-4b9a-806e-05eb3e4cf44d" />
|
||||
|
||||
From the output above, locate the CUDA installation directory — this is the path pointed to by installdir.
|
||||
Add this path to your system environment variable CUDA_PATH, or set it in the terminal using:
|
||||
```
|
||||
set CUDA_PATH=path_to_cuda_installdir:
|
||||
```
|
||||
Then re-run:
|
||||
```
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
#### Notice
|
||||
If the client status bar shows **Disconnected** during runtime, please first install the client from the [CrossDesk official website](https://www.crossdesk.cn/) to ensure the required certificate files are available in the environment.
|
||||
|
||||
<img width="256" height="120" alt="image" src="https://github.com/user-attachments/assets/1812f7d6-516b-4b4f-8a3d-98bee505cc5a" />
|
||||
|
||||
## About Xmake
|
||||
#### Installing Xmake
|
||||
|
||||
You can install Xmake using one of the following methods:
|
||||
|
||||
Using curl:
|
||||
```
|
||||
curl -fsSL https://xmake.io/shget.text | bash
|
||||
```
|
||||
Using wget:
|
||||
```
|
||||
wget https://xmake.io/shget.text -O - | bash
|
||||
```
|
||||
Using powershell:
|
||||
```
|
||||
irm https://xmake.io/psget.text | iex
|
||||
```
|
||||
|
||||
#### Build Options
|
||||
```
|
||||
# Switch build mode
|
||||
xmake f -m debug/release
|
||||
|
||||
# Optional build parameters
|
||||
-r : Rebuild the target
|
||||
-v : Show detailed build logs
|
||||
-y : Automatically confirm prompts
|
||||
|
||||
# Example
|
||||
xmake b -vy crossdesk
|
||||
```
|
||||
|
||||
#### Run Options
|
||||
```
|
||||
# Run in debug mode
|
||||
xmake r -d crossdesk
|
||||
```
|
||||
|
||||
For more information, please refer to the [official Xmake documentation](https://xmake.io/guide/quick-start.html) .
|
||||
|
||||
## Self-Hosted Server
|
||||
It is recommended to deploy CrossDesk Server using Docker.
|
||||
```
|
||||
sudo docker run -d \
|
||||
--name crossdesk_server \
|
||||
--network host \
|
||||
-e EXTERNAL_IP=150.158.81.30 \
|
||||
-e INTERNAL_IP=10.0.4.3 \
|
||||
-e CROSSDESK_SERVER_PORT=9099 \
|
||||
-v /path/to/your/certs:/crossdesk-server/certs \
|
||||
-v /path/to/your/db:/crossdesk-server/db \
|
||||
-v /path/to/your/logs:/crossdesk-server/logs \
|
||||
crossdesk/crossdesk-server:latest
|
||||
```
|
||||
|
||||
The parameters you need to pay attention to are as follows:
|
||||
|
||||
- **EXTERNAL_IP**: The server's public IP, corresponding to the **Server Address** in the CrossDesk client **Self-Hosted Server Configuration**.
|
||||
|
||||
- **INTERNAL_IP**: The server's internal IP.
|
||||
|
||||
- **CROSSDESK_SERVER_PORT**: The port used by the self-hosted server, corresponding to the **Server Port** in the CrossDesk client **Self-Hosted Server Configuration**.
|
||||
|
||||
- **/path/to/your/certs**: Directory for certificate files.
|
||||
|
||||
- **/path/to/your/db**: CrossDesk Server device management database.
|
||||
|
||||
- **/path/to/your/logs**: Log directory.
|
||||
|
||||
**Note**:
|
||||
- **/path/to/your/ is an example path; please replace it with your actual path. The mounted directories must be created in advance, otherwise the container will fail.**
|
||||
- **The server must open the following ports: 3478/udp, 3478/tcp, 30000-60000/udp, CROSSDESK_SERVER_PORT/tcp, 443/tcp.**
|
||||
|
||||
## Certificate Files
|
||||
The client needs to load the root certificate, and the server needs to load the server private key and server certificate.
|
||||
|
||||
If you already have an SSL certificate, you can skip the following certificate generation steps.
|
||||
|
||||
For users without a certificate, you can use the script below to generate the certificate files:
|
||||
```
|
||||
# Create certificate generation script
|
||||
vim generate_certs.sh
|
||||
```
|
||||
Copy the following into the script:
|
||||
```
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Check arguments
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <SERVER_IP>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SERVER_IP="$1"
|
||||
|
||||
# Filenames
|
||||
ROOT_KEY="crossdesk.cn_root.key"
|
||||
ROOT_CERT="crossdesk.cn_root.crt"
|
||||
SERVER_KEY="crossdesk.cn.key"
|
||||
SERVER_CSR="crossdesk.cn.csr"
|
||||
SERVER_CERT="crossdesk.cn_bundle.crt"
|
||||
FULLCHAIN_CERT="crossdesk.cn_fullchain.crt"
|
||||
|
||||
# Certificate subject
|
||||
SUBJ="/C=CN/ST=Zhejiang/L=Hangzhou/O=CrossDesk/OU=CrossDesk/CN=$SERVER_IP"
|
||||
|
||||
# 1. Generate root certificate
|
||||
echo "Generating root private key..."
|
||||
openssl genrsa -out "$ROOT_KEY" 4096
|
||||
|
||||
echo "Generating self-signed root certificate..."
|
||||
openssl req -x509 -new -nodes -key "$ROOT_KEY" -sha256 -days 3650 -out "$ROOT_CERT" -subj "$SUBJ"
|
||||
|
||||
# 2. Generate server private key
|
||||
echo "Generating server private key..."
|
||||
openssl genrsa -out "$SERVER_KEY" 2048
|
||||
|
||||
# 3. Generate server CSR
|
||||
echo "Generating server CSR..."
|
||||
openssl req -new -key "$SERVER_KEY" -out "$SERVER_CSR" -subj "$SUBJ"
|
||||
|
||||
# 4. Create temporary OpenSSL config file with SAN
|
||||
SAN_CONF="san.cnf"
|
||||
cat > $SAN_CONF <<EOL
|
||||
[ req ]
|
||||
default_bits = 2048
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = req_ext
|
||||
prompt = no
|
||||
|
||||
[ req_distinguished_name ]
|
||||
C = CN
|
||||
ST = Zhejiang
|
||||
L = Hangzhou
|
||||
O = CrossDesk
|
||||
OU = CrossDesk
|
||||
CN = $SERVER_IP
|
||||
|
||||
[ req_ext ]
|
||||
subjectAltName = IP:$SERVER_IP
|
||||
EOL
|
||||
|
||||
# 5. Sign server certificate with root certificate (including SAN)
|
||||
echo "Signing server certificate with root certificate..."
|
||||
openssl x509 -req -in "$SERVER_CSR" -CA "$ROOT_CERT" -CAkey "$ROOT_KEY" -CAcreateserial \
|
||||
-out "$SERVER_CERT" -days 3650 -sha256 -extfile "$SAN_CONF" -extensions req_ext
|
||||
|
||||
# 6. Generate full chain certificate
|
||||
cat "$SERVER_CERT" "$ROOT_CERT" > "$FULLCHAIN_CERT"
|
||||
|
||||
# 7. Clean up intermediate files
|
||||
rm -f "$ROOT_CERT.srl" "$SAN_CONF" "$ROOT_KEY" "$SERVER_CSR" "FULLCHAIN_CERT"
|
||||
|
||||
echo "Generation complete. Deployment files:"
|
||||
echo " Client root certificate: $ROOT_CERT"
|
||||
echo " Server private key: $SERVER_KEY"
|
||||
echo " Server certificate: $SERVER_CERT"
|
||||
```
|
||||
Execute:
|
||||
```
|
||||
chmod +x generate_certs.sh
|
||||
./generate_certs.sh EXTERNAL_IP
|
||||
|
||||
# example ./generate_certs.sh 111.111.111.111
|
||||
```
|
||||
Expected output:
|
||||
```
|
||||
Generating root private key...
|
||||
Generating self-signed root certificate...
|
||||
Generating server private key...
|
||||
Generating server CSR...
|
||||
Signing server certificate with root certificate...
|
||||
Certificate request self-signature ok
|
||||
subject=C = CN, ST = Zhejiang, L = Hangzhou, O = CrossDesk, OU = CrossDesk, CN = xxx.xxx.xxx.xxx
|
||||
cleaning up intermediate files...
|
||||
Generation complete. Deployment files::
|
||||
Client root certificate:: crossdesk.cn_root.crt
|
||||
Server private key: crossdesk.cn.key
|
||||
Server certificate: crossdesk.cn_bundle.crt
|
||||
```
|
||||
|
||||
#### Server Side
|
||||
Place **crossdesk.cn.key** and **crossdesk.cn_bundle.crt** into the **/path/to/your/certs** directory.
|
||||
|
||||
#### Client Side
|
||||
1. Click the settings icon in the top-right corner to enter the settings page.<br>
|
||||
<img width="600" height="210" alt="image" src="https://github.com/user-attachments/assets/6431131d-b32a-4726-8783-6788f47baa3b" /><br><br>
|
||||
|
||||
2. Click **Self-Hosted Server Configuration**.<br><br>
|
||||
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/24c761a3-1985-4d7e-84be-787383c2afb8" /><br><br>
|
||||
|
||||
3. In the **Certificate File Path** selection, locate and select the **crossdesk.cn_root.crt** file.<br><br>
|
||||
<img width="600" height="220" alt="image" src="https://github.com/user-attachments/assets/4af7cd3a-c72e-44fb-b032-30e050019c2a" /><br><br>
|
||||
|
||||
4. Check the option to use **Self-Hosted Server Configuration**.<br><br>
|
||||
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/1e455dc3-4087-4f37-a544-1ff9f8789383" /><br><br>
|
||||
|
||||
# FAQ
|
||||
See [FAQ](https://github.com/kunkundi/crosssesk/blob/self-hosted-server/docs/FAQ.md) .
|
||||
33
docs/FAQ.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# 常见问题(FAQ)
|
||||
|
||||
欢迎来到 **CrossDesk 常见问题** 页面!
|
||||
这里整理了用户和开发者最常见的一些疑问。如果你没有找到答案,欢迎在 [Issues](https://github.com/kunkundi/crossdesk/issues) 中反馈。
|
||||
|
||||
---
|
||||
|
||||
### Q1. 对等连接失败
|
||||
**A:**
|
||||
打开设置,勾选 **启用中继服务** 选项,尝试重新发起连接。
|
||||
|
||||
<img width="396" height="306" alt="Image" src="https://github.com/user-attachments/assets/fd8db148-c782-4f4d-b874-8f1b2a7ec7d6" />
|
||||
|
||||
由于公共中继服务器带宽较小,连接的清晰度流畅度可能会下降,建议自建服务器。 [Issue #8](https://github.com/kunkundi/crossdesk/issues/8)
|
||||
|
||||
### Q2. Windows 无 CUDA 环境下编译
|
||||
**A:**
|
||||
运行下面的命令安装 CUDA 编译环境。
|
||||
```
|
||||
xmake require -vy "cuda 12.6.3"
|
||||
```
|
||||
安装完成后执行
|
||||
```
|
||||
xmake require --info "cuda 12.6.3"
|
||||
```
|
||||
输出如下
|
||||
|
||||
<img width="860" height="226" alt="Image" src="https://github.com/user-attachments/assets/999ac365-581a-4b9a-806e-05eb3e4cf44d" />
|
||||
|
||||
根据上述输出获取到 CUDA 的安装目录,即 installdir 指向的位置。将 CUDA_PATH 加入系统环境变量,或在终端中输入 set CUDA_PATH=path_to_cuda_installdir,重新执行 xmake b -vy crossdesk 即可。
|
||||
[Issue #6](https://github.com/kunkundi/crossdesk/issues/6)
|
||||
|
||||
---
|
||||
@@ -1 +0,0 @@
|
||||
IDI_ICON1 ICON "app_icon.ico"
|
||||
|
Before Width: | Height: | Size: 687 B |
|
Before Width: | Height: | Size: 687 B |
|
Before Width: | Height: | Size: 840 B |
BIN
icons/linux/crossdesk_128x128.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
icons/linux/crossdesk_16x16.png
Normal file
|
After Width: | Height: | Size: 746 B |
BIN
icons/linux/crossdesk_24x24.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
icons/linux/crossdesk_256x256.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
icons/linux/crossdesk_32x32.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
icons/linux/crossdesk_48x48.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
icons/linux/crossdesk_512x512.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
icons/linux/crossdesk_64x64.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
icons/linux/crossdesk_96x96.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
icons/macos/crossdesk.icns
Normal file
BIN
icons/windows/crossdesk.ico
Normal file
|
After Width: | Height: | Size: 84 KiB |
120
scripts/linux/pkg_amd64.sh
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
PKG_NAME="crossdesk"
|
||||
APP_NAME="CrossDesk"
|
||||
|
||||
APP_VERSION="$1"
|
||||
ARCHITECTURE="amd64"
|
||||
MAINTAINER="Junkun Di <junkun.di@hotmail.com>"
|
||||
DESCRIPTION="A simple cross-platform remote desktop client."
|
||||
|
||||
DEB_DIR="${PKG_NAME}-${APP_VERSION}"
|
||||
DEBIAN_DIR="$DEB_DIR/DEBIAN"
|
||||
BIN_DIR="$DEB_DIR/usr/bin"
|
||||
CERT_SRC_DIR="$DEB_DIR/opt/$PKG_NAME/certs"
|
||||
ICON_BASE_DIR="$DEB_DIR/usr/share/icons/hicolor"
|
||||
DESKTOP_DIR="$DEB_DIR/usr/share/applications"
|
||||
|
||||
rm -rf "$DEB_DIR"
|
||||
|
||||
mkdir -p "$DEBIAN_DIR" "$BIN_DIR" "$CERT_SRC_DIR" "$DESKTOP_DIR"
|
||||
|
||||
cp build/linux/x86_64/release/crossdesk "$BIN_DIR/$PKG_NAME"
|
||||
chmod +x "$BIN_DIR/$PKG_NAME"
|
||||
|
||||
ln -s "$PKG_NAME" "$BIN_DIR/$APP_NAME"
|
||||
|
||||
cp certs/crossdesk.cn_root.crt "$CERT_SRC_DIR/crossdesk.cn_root.crt"
|
||||
|
||||
for size in 16 24 32 48 64 96 128 256; do
|
||||
mkdir -p "$ICON_BASE_DIR/${size}x${size}/apps"
|
||||
cp "icons/linux/crossdesk_${size}x${size}.png" \
|
||||
"$ICON_BASE_DIR/${size}x${size}/apps/${PKG_NAME}.png"
|
||||
done
|
||||
|
||||
cat > "$DEBIAN_DIR/control" << EOF
|
||||
Package: $PKG_NAME
|
||||
Version: $APP_VERSION
|
||||
Architecture: $ARCHITECTURE
|
||||
Maintainer: $MAINTAINER
|
||||
Description: $DESCRIPTION
|
||||
Depends: libc6 (>= 2.29), libstdc++6 (>= 9), libx11-6, libxcb1,
|
||||
libxcb-randr0, libxcb-xtest0, libxcb-xinerama0, libxcb-shape0,
|
||||
libxcb-xkb1, libxcb-xfixes0, libxv1, libxtst6, libasound2,
|
||||
libsndio7.0, libxcb-shm0, libpulse0
|
||||
Recommends: nvidia-cuda-toolkit
|
||||
Priority: optional
|
||||
Section: utils
|
||||
EOF
|
||||
|
||||
cat > "$DESKTOP_DIR/$PKG_NAME.desktop" << EOF
|
||||
[Desktop Entry]
|
||||
Version=$APP_VERSION
|
||||
Name=$APP_NAME
|
||||
Comment=$DESCRIPTION
|
||||
Exec=/usr/bin/$PKG_NAME
|
||||
Icon=$PKG_NAME
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;
|
||||
EOF
|
||||
|
||||
cat > "$DEBIAN_DIR/postrm" << EOF
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [ "\$1" = "remove" ] || [ "\$1" = "purge" ]; then
|
||||
rm -f /usr/bin/$PKG_NAME || true
|
||||
rm -f /usr/bin/$APP_NAME || true
|
||||
rm -f /usr/share/applications/$PKG_NAME.desktop || true
|
||||
rm -rf /opt/$PKG_NAME/certs || true
|
||||
for size in 16 24 32 48 64 96 128 256; do
|
||||
rm -f /usr/share/icons/hicolor/\${size}x\${size}/apps/$PKG_NAME.png || true
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$DEBIAN_DIR/postrm"
|
||||
|
||||
cat > "$DEBIAN_DIR/postinst" << 'EOF'
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CERT_SRC="/opt/crossdesk/certs"
|
||||
CERT_FILE="crossdesk.cn_root.crt"
|
||||
|
||||
for user_home in /home/*; do
|
||||
[ -d "$user_home" ] || continue
|
||||
username=$(basename "$user_home")
|
||||
config_dir="$user_home/.config/CrossDesk/certs"
|
||||
target="$config_dir/$CERT_FILE"
|
||||
|
||||
if [ ! -f "$target" ]; then
|
||||
mkdir -p "$config_dir" || true
|
||||
cp "$CERT_SRC/$CERT_FILE" "$target" || true
|
||||
chown -R "$username:$username" "$user_home/.config/CrossDesk" || true
|
||||
echo "✔ Installed cert for $username at $target"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -d "/root" ]; then
|
||||
config_dir="/root/.config/CrossDesk/certs"
|
||||
mkdir -p "$config_dir" || true
|
||||
cp "$CERT_SRC/$CERT_FILE" "$config_dir/$CERT_FILE" || true
|
||||
chown -R root:root /root/.config/CrossDesk || true
|
||||
fi
|
||||
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$DEBIAN_DIR/postinst"
|
||||
|
||||
dpkg-deb --build "$DEB_DIR"
|
||||
|
||||
OUTPUT_FILE="${PKG_NAME}-linux-${ARCHITECTURE}-${APP_VERSION}.deb"
|
||||
mv "$DEB_DIR.deb" "$OUTPUT_FILE"
|
||||
|
||||
rm -rf "$DEB_DIR"
|
||||
|
||||
echo "✅ Deb package created: $OUTPUT_FILE"
|
||||
120
scripts/linux/pkg_arm64.sh
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
PKG_NAME="crossdesk"
|
||||
APP_NAME="CrossDesk"
|
||||
|
||||
APP_VERSION="$1"
|
||||
ARCHITECTURE="arm64"
|
||||
MAINTAINER="Junkun Di <junkun.di@hotmail.com>"
|
||||
DESCRIPTION="A simple cross-platform remote desktop client."
|
||||
|
||||
DEB_DIR="${PKG_NAME}-${APP_VERSION}"
|
||||
DEBIAN_DIR="$DEB_DIR/DEBIAN"
|
||||
BIN_DIR="$DEB_DIR/usr/bin"
|
||||
CERT_SRC_DIR="$DEB_DIR/opt/$PKG_NAME/certs"
|
||||
ICON_BASE_DIR="$DEB_DIR/usr/share/icons/hicolor"
|
||||
DESKTOP_DIR="$DEB_DIR/usr/share/applications"
|
||||
|
||||
rm -rf "$DEB_DIR"
|
||||
|
||||
mkdir -p "$DEBIAN_DIR" "$BIN_DIR" "$CERT_SRC_DIR" "$DESKTOP_DIR"
|
||||
|
||||
cp build/linux/arm64/release/crossdesk "$BIN_DIR"
|
||||
chmod +x "$BIN_DIR/$PKG_NAME"
|
||||
|
||||
ln -s "$PKG_NAME" "$BIN_DIR/$APP_NAME"
|
||||
|
||||
cp certs/crossdesk.cn_root.crt "$CERT_SRC_DIR/crossdesk.cn_root.crt"
|
||||
|
||||
for size in 16 24 32 48 64 96 128 256; do
|
||||
mkdir -p "$ICON_BASE_DIR/${size}x${size}/apps"
|
||||
cp "icons/linux/crossdesk_${size}x${size}.png" \
|
||||
"$ICON_BASE_DIR/${size}x${size}/apps/${PKG_NAME}.png"
|
||||
done
|
||||
|
||||
cat > "$DEBIAN_DIR/control" << EOF
|
||||
Package: $PKG_NAME
|
||||
Version: $APP_VERSION
|
||||
Architecture: $ARCHITECTURE
|
||||
Maintainer: $MAINTAINER
|
||||
Description: $DESCRIPTION
|
||||
Depends: libc6 (>= 2.29), libstdc++6 (>= 9), libx11-6, libxcb1,
|
||||
libxcb-randr0, libxcb-xtest0, libxcb-xinerama0, libxcb-shape0,
|
||||
libxcb-xkb1, libxcb-xfixes0, libxv1, libxtst6, libasound2,
|
||||
libsndio7.0, libxcb-shm0, libpulse0
|
||||
Priority: optional
|
||||
Section: utils
|
||||
EOF
|
||||
|
||||
cat > "$DESKTOP_DIR/$PKG_NAME.desktop" << EOF
|
||||
[Desktop Entry]
|
||||
Version=$APP_VERSION
|
||||
Name=$APP_NAME
|
||||
Comment=$DESCRIPTION
|
||||
Exec=/usr/bin/$PKG_NAME
|
||||
Icon=$PKG_NAME
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;
|
||||
EOF
|
||||
|
||||
cat > "$DEBIAN_DIR/postrm" << EOF
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [ "\$1" = "remove" ] || [ "\$1" = "purge" ]; then
|
||||
rm -f /usr/bin/$PKG_NAME || true
|
||||
rm -f /usr/bin/$APP_NAME || true
|
||||
rm -f /usr/share/applications/$PKG_NAME.desktop || true
|
||||
rm -rf /opt/$PKG_NAME/certs || true
|
||||
for size in 16 24 32 48 64 96 128 256; do
|
||||
rm -f /usr/share/icons/hicolor/\${size}x\${size}/apps/$PKG_NAME.png || true
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$DEBIAN_DIR/postrm"
|
||||
|
||||
cat > "$DEBIAN_DIR/postinst" << 'EOF'
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CERT_SRC="/opt/crossdesk/certs"
|
||||
CERT_FILE="crossdesk.cn_root.crt"
|
||||
|
||||
for user_home in /home/*; do
|
||||
[ -d "$user_home" ] || continue
|
||||
username=$(basename "$user_home")
|
||||
config_dir="$user_home/.config/CrossDesk/certs"
|
||||
target="$config_dir/$CERT_FILE"
|
||||
|
||||
if [ ! -f "$target" ]; then
|
||||
mkdir -p "$config_dir" || true
|
||||
cp "$CERT_SRC/$CERT_FILE" "$target" || true
|
||||
chown -R "$username:$username" "$user_home/.config/CrossDesk" || true
|
||||
echo "✔ Installed cert for $username at $target"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -d "/root" ]; then
|
||||
config_dir="/root/.config/CrossDesk/certs"
|
||||
mkdir -p "$config_dir" || true
|
||||
cp "$CERT_SRC/$CERT_FILE" "$config_dir/$CERT_FILE" || true
|
||||
chown -R root:root /root/.config/CrossDesk || true
|
||||
fi
|
||||
|
||||
exit 0
|
||||
EOF
|
||||
|
||||
chmod +x "$DEBIAN_DIR/postinst"
|
||||
|
||||
dpkg-deb --build "$DEB_DIR"
|
||||
|
||||
OUTPUT_FILE="crossdesk-linux-arm64-$APP_VERSION.deb"
|
||||
mv "$DEB_DIR.deb" "$OUTPUT_FILE"
|
||||
|
||||
rm -rf "$DEB_DIR"
|
||||
|
||||
echo "✅ Deb package created: $OUTPUT_FILE"
|
||||
@@ -1,126 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APP_NAME="CrossDesk"
|
||||
APP_VERSION="0.0.1"
|
||||
ARCHITECTURE="amd64"
|
||||
MAINTAINER="Junkun Di <junkun.di@hotmail.com>"
|
||||
DESCRIPTION="A simple cross-platform remote desktop client."
|
||||
|
||||
# 目录结构
|
||||
DEB_DIR="$APP_NAME-$APP_VERSION"
|
||||
DEBIAN_DIR="$DEB_DIR/DEBIAN"
|
||||
BIN_DIR="$DEB_DIR/usr/local/bin"
|
||||
CERT_SRC_DIR="$DEB_DIR/opt/$APP_NAME/certs" # 用于中转安装时分发
|
||||
ICON_DIR="$DEB_DIR/usr/share/icons/hicolor/256x256/apps"
|
||||
DESKTOP_DIR="$DEB_DIR/usr/share/applications"
|
||||
|
||||
# 清理已有的打包文件夹
|
||||
rm -rf "$DEB_DIR"
|
||||
|
||||
# 创建目录结构
|
||||
mkdir -p "$DEBIAN_DIR" "$BIN_DIR" "$CERT_SRC_DIR" "$ICON_DIR" "$DESKTOP_DIR"
|
||||
|
||||
# 复制二进制文件
|
||||
cp build/linux/x86_64/release/crossdesk "$BIN_DIR"
|
||||
|
||||
# 复制证书文件(将来通过 postinst 拷贝到每个用户 XDG_CONFIG_HOME)
|
||||
cp certs/crossdesk.cn_root.crt "$CERT_SRC_DIR/crossdesk.cn_root.crt"
|
||||
|
||||
# 复制图标文件
|
||||
cp icons/crossdesk.png "$ICON_DIR/crossdesk.png"
|
||||
|
||||
# 设置可执行权限
|
||||
chmod +x "$BIN_DIR/crossdesk"
|
||||
|
||||
# 创建 control 文件
|
||||
cat > "$DEBIAN_DIR/control" << EOF
|
||||
Package: $APP_NAME
|
||||
Version: $APP_VERSION
|
||||
Architecture: $ARCHITECTURE
|
||||
Maintainer: $MAINTAINER
|
||||
Description: $DESCRIPTION
|
||||
Depends: libc6 (>= 2.29), libstdc++6 (>= 9), libx11-6, libxcb1,
|
||||
libxcb-randr0, libxcb-xtest0, libxcb-xinerama0, libxcb-shape0,
|
||||
libxcb-xkb1, libxcb-xfixes0, libxv1, libxtst6, libasound2,
|
||||
libsndio7.0, libxcb-shm0, libpulse0, nvidia-cuda-toolkit
|
||||
Priority: optional
|
||||
Section: utils
|
||||
EOF
|
||||
|
||||
# 创建 desktop 文件
|
||||
cat > "$DESKTOP_DIR/$APP_NAME.desktop" << EOF
|
||||
[Desktop Entry]
|
||||
Version=$APP_VERSION
|
||||
Name=$APP_NAME
|
||||
Comment=$DESCRIPTION
|
||||
Exec=/usr/local/bin/crossdesk
|
||||
Icon=crossdesk
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;
|
||||
EOF
|
||||
|
||||
# 创建卸载脚本 postrm
|
||||
cat > "$DEBIAN_DIR/postrm" << EOF
|
||||
#!/bin/bash
|
||||
# post-removal script for $APP_NAME
|
||||
|
||||
set -e
|
||||
|
||||
if [ "\$1" = "remove" ] || [ "\$1" = "purge" ]; then
|
||||
rm -f /usr/local/bin/crossdesk
|
||||
rm -f /usr/share/icons/hicolor/256x256/apps/crossdesk.png
|
||||
rm -f /usr/share/applications/$APP_NAME.desktop
|
||||
rm -rf /opt/$APP_NAME
|
||||
fi
|
||||
|
||||
exit 0
|
||||
EOF
|
||||
|
||||
chmod +x "$DEBIAN_DIR/postrm"
|
||||
|
||||
# 创建安装后脚本 postinst(拷贝证书到每个用户 XDG_CONFIG_HOME)
|
||||
cat > "$DEBIAN_DIR/postinst" << 'EOF'
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CERT_SRC="/opt/CrossDesk/certs"
|
||||
CERT_FILE="crossdesk.cn_root.crt"
|
||||
|
||||
# 处理每个普通用户的配置目录
|
||||
for user_home in /home/*; do
|
||||
[ -d "$user_home" ] || continue
|
||||
username=$(basename "$user_home")
|
||||
config_dir="$user_home/.config/CrossDesk/certs"
|
||||
target="$config_dir/$CERT_FILE"
|
||||
|
||||
if [ ! -f "$target" ]; then
|
||||
mkdir -p "$config_dir"
|
||||
cp "$CERT_SRC/$CERT_FILE" "$target"
|
||||
chown -R "$username:$username" "$user_home/.config/CrossDesk"
|
||||
echo "✔ Installed cert for $username at $target"
|
||||
fi
|
||||
done
|
||||
|
||||
# 处理 root 用户(可选)
|
||||
if [ -d "/root" ]; then
|
||||
config_dir="/root/.config/CrossDesk/certs"
|
||||
mkdir -p "$config_dir"
|
||||
cp "$CERT_SRC/$CERT_FILE" "$config_dir/$CERT_FILE"
|
||||
chown -R root:root /root/.config/CrossDesk
|
||||
fi
|
||||
|
||||
exit 0
|
||||
EOF
|
||||
|
||||
chmod +x "$DEBIAN_DIR/postinst"
|
||||
|
||||
# 构建 .deb 包
|
||||
dpkg-deb --build "$DEB_DIR"
|
||||
|
||||
# 清理构建目录
|
||||
rm -rf "$DEB_DIR"
|
||||
|
||||
echo "✅ Deb package for $APP_NAME created successfully."
|
||||
@@ -1,64 +1,54 @@
|
||||
#!/bin/bash
|
||||
set -e # 遇错退出
|
||||
set -e
|
||||
|
||||
# === 配置变量 ===
|
||||
APP_NAME="crossdesk"
|
||||
APP_NAME_UPPER="CrossDesk" # 这个变量用来指定大写的应用名
|
||||
EXECUTABLE_PATH="./build/macosx/arm64/release/crossdesk" # 可执行文件路径
|
||||
APP_VERSION="0.0.1"
|
||||
APP_NAME_UPPER="CrossDesk"
|
||||
EXECUTABLE_PATH="./build/macosx/arm64/release/crossdesk"
|
||||
APP_VERSION="$1"
|
||||
PLATFORM="macos"
|
||||
ARCH="arm64"
|
||||
IDENTIFIER="cn.crossdesk.app"
|
||||
ICON_PATH="./icons/crossdesk.icns" # .icns 图标路径
|
||||
ICON_PATH="icons/macos/crossdesk.icns"
|
||||
MACOS_MIN_VERSION="10.12"
|
||||
|
||||
CERTS_SOURCE="./certs" # 你的证书文件目录,里面放所有需要安装的文件
|
||||
CERTS_SOURCE="certs"
|
||||
CERT_NAME="crossdesk.cn_root.crt"
|
||||
|
||||
APP_BUNDLE="${APP_NAME_UPPER}.app" # 使用大写的应用名称
|
||||
APP_BUNDLE="${APP_NAME_UPPER}.app"
|
||||
CONTENTS_DIR="${APP_BUNDLE}/Contents"
|
||||
MACOS_DIR="${CONTENTS_DIR}/MacOS"
|
||||
RESOURCES_DIR="${CONTENTS_DIR}/Resources"
|
||||
|
||||
PKG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-v${APP_VERSION}.pkg" # 保持安装包名称小写
|
||||
DMG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-v${APP_VERSION}.dmg"
|
||||
PKG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-${APP_VERSION}.pkg"
|
||||
DMG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-${APP_VERSION}.dmg"
|
||||
VOL_NAME="Install ${APP_NAME_UPPER}"
|
||||
|
||||
# === 清理旧文件 ===
|
||||
echo "🧹 清理旧文件..."
|
||||
echo "delete old files"
|
||||
rm -rf "${APP_BUNDLE}" "${PKG_NAME}" "${DMG_NAME}" build_pkg_temp CrossDesk_dmg_temp
|
||||
|
||||
mkdir -p build_pkg_temp
|
||||
|
||||
# === 创建 .app 结构 ===
|
||||
echo "📦 创建 ${APP_BUNDLE}..."
|
||||
mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}"
|
||||
|
||||
echo "🚚 拷贝可执行文件..."
|
||||
cp "${EXECUTABLE_PATH}" "${MACOS_DIR}/${APP_NAME_UPPER}" # 拷贝时使用大写的应用名称
|
||||
cp "${EXECUTABLE_PATH}" "${MACOS_DIR}/${APP_NAME_UPPER}"
|
||||
chmod +x "${MACOS_DIR}/${APP_NAME_UPPER}"
|
||||
|
||||
# === 图标 ===
|
||||
if [ -f "${ICON_PATH}" ]; then
|
||||
cp "${ICON_PATH}" "${RESOURCES_DIR}/crossedesk.icns"
|
||||
ICON_KEY="<key>CFBundleIconFile</key><string>crossedesk.icns</string>"
|
||||
echo "🎨 图标添加完成"
|
||||
else
|
||||
ICON_KEY=""
|
||||
echo "⚠️ 未找到图标文件,跳过图标设置"
|
||||
fi
|
||||
|
||||
# === 生成 Info.plist ===
|
||||
echo "📝 生成 Info.plist..."
|
||||
echo "generate Info.plist"
|
||||
cat > "${CONTENTS_DIR}/Info.plist" <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>${APP_NAME_UPPER}</string> <!-- 使用大写名称 -->
|
||||
<string>${APP_NAME_UPPER}</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${APP_NAME_UPPER}</string> <!-- 使用大写名称 -->
|
||||
<string>${APP_NAME_UPPER}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${IDENTIFIER}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@@ -66,7 +56,7 @@ cat > "${CONTENTS_DIR}/Info.plist" <<EOF
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${APP_VERSION}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${APP_NAME_UPPER}</string> <!-- 使用大写名称 -->
|
||||
<string>${APP_NAME_UPPER}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
${ICON_KEY}
|
||||
@@ -86,10 +76,9 @@ cat > "${CONTENTS_DIR}/Info.plist" <<EOF
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
echo "✅ .app 创建完成"
|
||||
echo ".app created successfully."
|
||||
|
||||
# === 构建应用组件包 ===
|
||||
echo "📦 构建应用组件包..."
|
||||
echo "building pkg..."
|
||||
pkgbuild \
|
||||
--identifier "${IDENTIFIER}" \
|
||||
--version "${APP_VERSION}" \
|
||||
@@ -97,8 +86,6 @@ pkgbuild \
|
||||
--component "${APP_BUNDLE}" \
|
||||
build_pkg_temp/${APP_NAME}-component.pkg
|
||||
|
||||
# === 构建 certs 组件包 ===
|
||||
# 先创建脚本目录和脚本文件
|
||||
mkdir -p scripts
|
||||
|
||||
cat > scripts/postinstall <<'EOF'
|
||||
@@ -116,7 +103,6 @@ EOF
|
||||
|
||||
chmod +x scripts/postinstall
|
||||
|
||||
# 构建 certs 组件包,增加 --scripts 参数指定 postinstall
|
||||
pkgbuild \
|
||||
--root "${CERTS_SOURCE}" \
|
||||
--identifier "${IDENTIFIER}.certs" \
|
||||
@@ -125,28 +111,15 @@ pkgbuild \
|
||||
--scripts scripts \
|
||||
build_pkg_temp/${APP_NAME}-certs.pkg
|
||||
|
||||
# === 组合产品包 ===
|
||||
echo "🏗️ 组合最终安装包..."
|
||||
productbuild \
|
||||
--package build_pkg_temp/${APP_NAME}-component.pkg \
|
||||
--package build_pkg_temp/${APP_NAME}-certs.pkg \
|
||||
"${PKG_NAME}"
|
||||
|
||||
echo "✅ 生成安装包完成:${PKG_NAME}"
|
||||
echo "PKG package created: ${PKG_NAME}"
|
||||
|
||||
# === 可选:打包成 DMG ===
|
||||
echo "📦 可选打包成 DMG..."
|
||||
mkdir -p CrossDesk_dmg_temp
|
||||
cp "${PKG_NAME}" CrossDesk_dmg_temp/
|
||||
ln -s /Applications CrossDesk_dmg_temp/Applications
|
||||
rm -rf build_pkg_temp scripts ${APP_BUNDLE}
|
||||
|
||||
hdiutil create -volname "${VOL_NAME}" \
|
||||
-srcfolder CrossDesk_dmg_temp \
|
||||
-ov -format UDZO "${DMG_NAME}"
|
||||
|
||||
rm -rf CrossDesk_dmg_temp build_pkg_temp scripts ${APP_BUNDLE} ${DMG_NAME}
|
||||
|
||||
echo "🎉 所有打包完成:"
|
||||
echo " ✔️ 应用:${APP_BUNDLE}"
|
||||
echo " ✔️ 安装包:${PKG_NAME}"
|
||||
echo " ✔️ 镜像包(可选):${DMG_NAME}"
|
||||
echo "PKG package created successfully."
|
||||
echo "package ${APP_BUNDLE}"
|
||||
echo "installer ${PKG_NAME}"
|
||||
@@ -1,64 +1,54 @@
|
||||
#!/bin/bash
|
||||
set -e # 遇错退出
|
||||
set -e
|
||||
|
||||
# === 配置变量 ===
|
||||
APP_NAME="crossdesk"
|
||||
APP_NAME_UPPER="CrossDesk" # 这个变量用来指定大写的应用名
|
||||
EXECUTABLE_PATH="build/macosx/x86_64/release/crossdesk" # 可执行文件路径
|
||||
APP_VERSION="0.0.1"
|
||||
APP_NAME_UPPER="CrossDesk"
|
||||
EXECUTABLE_PATH="build/macosx/x86_64/release/crossdesk"
|
||||
APP_VERSION="$1"
|
||||
PLATFORM="macos"
|
||||
ARCH="x86_64"
|
||||
ARCH="x64"
|
||||
IDENTIFIER="cn.crossdesk.app"
|
||||
ICON_PATH="icons/crossdesk.icns" # .icns 图标路径
|
||||
ICON_PATH="icons/macos/crossdesk.icns"
|
||||
MACOS_MIN_VERSION="10.12"
|
||||
|
||||
CERTS_SOURCE="certs" # 你的证书文件目录,里面放所有需要安装的文件
|
||||
CERTS_SOURCE="certs"
|
||||
CERT_NAME="crossdesk.cn_root.crt"
|
||||
|
||||
APP_BUNDLE="${APP_NAME_UPPER}.app" # 使用大写的应用名称
|
||||
APP_BUNDLE="${APP_NAME_UPPER}.app"
|
||||
CONTENTS_DIR="${APP_BUNDLE}/Contents"
|
||||
MACOS_DIR="${CONTENTS_DIR}/MacOS"
|
||||
RESOURCES_DIR="${CONTENTS_DIR}/Resources"
|
||||
|
||||
PKG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-v${APP_VERSION}.pkg" # 保持安装包名称小写
|
||||
DMG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-v${APP_VERSION}.dmg"
|
||||
PKG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-${APP_VERSION}.pkg"
|
||||
DMG_NAME="${APP_NAME}-${PLATFORM}-${ARCH}-${APP_VERSION}.dmg"
|
||||
VOL_NAME="Install ${APP_NAME_UPPER}"
|
||||
|
||||
# === 清理旧文件 ===
|
||||
echo "🧹 清理旧文件..."
|
||||
echo "delete old files"
|
||||
rm -rf "${APP_BUNDLE}" "${PKG_NAME}" "${DMG_NAME}" build_pkg_temp CrossDesk_dmg_temp
|
||||
|
||||
mkdir -p build_pkg_temp
|
||||
|
||||
# === 创建 .app 结构 ===
|
||||
echo "📦 创建 ${APP_BUNDLE}..."
|
||||
mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}"
|
||||
|
||||
echo "🚚 拷贝可执行文件..."
|
||||
cp "${EXECUTABLE_PATH}" "${MACOS_DIR}/${APP_NAME_UPPER}" # 拷贝时使用大写的应用名称
|
||||
cp "${EXECUTABLE_PATH}" "${MACOS_DIR}/${APP_NAME_UPPER}"
|
||||
chmod +x "${MACOS_DIR}/${APP_NAME_UPPER}"
|
||||
|
||||
# === 图标 ===
|
||||
if [ -f "${ICON_PATH}" ]; then
|
||||
cp "${ICON_PATH}" "${RESOURCES_DIR}/crossedesk.icns"
|
||||
ICON_KEY="<key>CFBundleIconFile</key><string>crossedesk.icns</string>"
|
||||
echo "🎨 图标添加完成"
|
||||
else
|
||||
ICON_KEY=""
|
||||
echo "⚠️ 未找到图标文件,跳过图标设置"
|
||||
fi
|
||||
|
||||
# === 生成 Info.plist ===
|
||||
echo "📝 生成 Info.plist..."
|
||||
echo "generate Info.plist"
|
||||
cat > "${CONTENTS_DIR}/Info.plist" <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>${APP_NAME_UPPER}</string> <!-- 使用大写名称 -->
|
||||
<string>${APP_NAME_UPPER}</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${APP_NAME_UPPER}</string> <!-- 使用大写名称 -->
|
||||
<string>${APP_NAME_UPPER}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${IDENTIFIER}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@@ -66,7 +56,7 @@ cat > "${CONTENTS_DIR}/Info.plist" <<EOF
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${APP_VERSION}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${APP_NAME_UPPER}</string> <!-- 使用大写名称 -->
|
||||
<string>${APP_NAME_UPPER}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
${ICON_KEY}
|
||||
@@ -86,10 +76,9 @@ cat > "${CONTENTS_DIR}/Info.plist" <<EOF
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
echo "✅ .app 创建完成"
|
||||
echo ".app created successfully."
|
||||
|
||||
# === 构建应用组件包 ===
|
||||
echo "📦 构建应用组件包..."
|
||||
echo "building pkg..."
|
||||
pkgbuild \
|
||||
--identifier "${IDENTIFIER}" \
|
||||
--version "${APP_VERSION}" \
|
||||
@@ -97,8 +86,6 @@ pkgbuild \
|
||||
--component "${APP_BUNDLE}" \
|
||||
build_pkg_temp/${APP_NAME}-component.pkg
|
||||
|
||||
# === 构建 certs 组件包 ===
|
||||
# 先创建脚本目录和脚本文件
|
||||
mkdir -p scripts
|
||||
|
||||
cat > scripts/postinstall <<'EOF'
|
||||
@@ -116,7 +103,6 @@ EOF
|
||||
|
||||
chmod +x scripts/postinstall
|
||||
|
||||
# 构建 certs 组件包,增加 --scripts 参数指定 postinstall
|
||||
pkgbuild \
|
||||
--root "${CERTS_SOURCE}" \
|
||||
--identifier "${IDENTIFIER}.certs" \
|
||||
@@ -125,28 +111,15 @@ pkgbuild \
|
||||
--scripts scripts \
|
||||
build_pkg_temp/${APP_NAME}-certs.pkg
|
||||
|
||||
# === 组合产品包 ===
|
||||
echo "🏗️ 组合最终安装包..."
|
||||
productbuild \
|
||||
--package build_pkg_temp/${APP_NAME}-component.pkg \
|
||||
--package build_pkg_temp/${APP_NAME}-certs.pkg \
|
||||
"${PKG_NAME}"
|
||||
|
||||
echo "✅ 生成安装包完成:${PKG_NAME}"
|
||||
echo "PKG package created: ${PKG_NAME}"
|
||||
|
||||
# === 可选:打包成 DMG ===
|
||||
echo "📦 可选打包成 DMG..."
|
||||
mkdir -p CrossDesk_dmg_temp
|
||||
cp "${PKG_NAME}" CrossDesk_dmg_temp/
|
||||
ln -s /Applications CrossDesk_dmg_temp/Applications
|
||||
rm -rf build_pkg_temp scripts ${APP_BUNDLE}
|
||||
|
||||
hdiutil create -volname "${VOL_NAME}" \
|
||||
-srcfolder CrossDesk_dmg_temp \
|
||||
-ov -format UDZO "${DMG_NAME}"
|
||||
|
||||
rm -rf CrossDesk_dmg_temp build_pkg_temp scripts ${APP_BUNDLE} ${DMG_NAME}
|
||||
|
||||
echo "🎉 所有打包完成:"
|
||||
echo " ✔️ 应用:${APP_BUNDLE}"
|
||||
echo " ✔️ 安装包:${PKG_NAME}"
|
||||
echo " ✔️ 镜像包(可选):${DMG_NAME}"
|
||||
echo "PKG package created successfully."
|
||||
echo "package ${APP_BUNDLE}"
|
||||
echo "installer ${PKG_NAME}"
|
||||
43
scripts/windows/crossdesk.manifest
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<!-- 应用程序标识 -->
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="*"
|
||||
name="CrossDesk"
|
||||
type="win32" />
|
||||
|
||||
<!-- 描述信息 -->
|
||||
<description>CrossDesk Application</description>
|
||||
|
||||
<!-- 权限:要求管理员运行 -->
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<!-- DPI 感知设置:支持高分屏 -->
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<!-- Windows Vista/7 风格 DPI 感知 -->
|
||||
<dpiAware>true/pm</dpiAware>
|
||||
<!-- Windows 10/11 高级 DPI 感知 -->
|
||||
<dpiAwareness>PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
|
||||
<!-- Windows 兼容性声明 -->
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- 支持 Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- 支持 Windows 11(向下兼容 Win10 GUID) -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
</assembly>
|
||||
BIN
scripts/windows/nsProcess.dll
Normal file
@@ -1,53 +1,86 @@
|
||||
; <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>
|
||||
; Set search path
|
||||
!addincludedir "${__FILEDIR__}"
|
||||
|
||||
; <EFBFBD><EFBFBD>װ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD>峣<EFBFBD><EFBFBD>
|
||||
; Installer initial constants
|
||||
!define PRODUCT_NAME "CrossDesk"
|
||||
!define PRODUCT_VERSION "0.0.1"
|
||||
!define PRODUCT_VERSION "${VERSION}"
|
||||
!define PRODUCT_PUBLISHER "CrossDesk"
|
||||
!define PRODUCT_WEB_SITE "https://www.crossdesk.cn/"
|
||||
!define APP_NAME "CrossDesk"
|
||||
!define UNINSTALL_REG_KEY "CrossDesk"
|
||||
|
||||
; <EFBFBD><EFBFBD><EFBFBD>ð<EFBFBD>װ<EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>
|
||||
!define MUI_ICON "${__FILEDIR__}\..\..\icons\crossdesk.ico"
|
||||
; Installer icon path
|
||||
!define MUI_ICON "${__FILEDIR__}\..\..\icons\windows\crossdesk.ico"
|
||||
|
||||
; <EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>
|
||||
; Certificate path
|
||||
!define CERT_FILE "${__FILEDIR__}\..\..\certs\crossdesk.cn_root.crt"
|
||||
|
||||
; ѹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
; Compression settings
|
||||
SetCompressor /FINAL lzma
|
||||
|
||||
; <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԱȨ<EFBFBD>ޣ<EFBFBD>д<EFBFBD><EFBFBD>HKLM<EFBFBD><EFBFBD>Ҫ<EFBFBD><EFBFBD>
|
||||
; Request admin privileges (needed to write HKLM)
|
||||
RequestExecutionLevel admin
|
||||
|
||||
; ------ MUI <EFBFBD>ִ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>涨<EFBFBD><EFBFBD> ------
|
||||
; ------ MUI Modern UI Definition ------
|
||||
!include "MUI.nsh"
|
||||
!define MUI_ABORTWARNING
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
; Add run-after-install option
|
||||
!define MUI_FINISHPAGE_RUN
|
||||
!define MUI_FINISHPAGE_RUN_TEXT "Run ${PRODUCT_NAME}"
|
||||
!define MUI_FINISHPAGE_RUN_FUNCTION LaunchApp
|
||||
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
!insertmacro MUI_LANGUAGE "SimpChinese"
|
||||
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
|
||||
; ------ MUI <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ------
|
||||
; ------ End of MUI Definition ------
|
||||
|
||||
; Include LogicLib for process handling
|
||||
!include "LogicLib.nsh"
|
||||
|
||||
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
|
||||
OutFile "CrossDesk-${PRODUCT_VERSION}.exe"
|
||||
OutFile "crossdesk-win-x64-${PRODUCT_VERSION}.exe"
|
||||
InstallDir "$PROGRAMFILES\CrossDesk"
|
||||
InstallDirRegKey HKCU "Software\${PRODUCT_NAME}" "InstallDir"
|
||||
ShowInstDetails show
|
||||
|
||||
Section "MainSection"
|
||||
; Check if CrossDesk is running
|
||||
StrCpy $1 "crossdesk.exe"
|
||||
|
||||
nsProcess::_FindProcess "$1"
|
||||
Pop $R0
|
||||
${If} $R0 = 0 ;
|
||||
MessageBox MB_ICONQUESTION|MB_YESNO "CrossDesk is running. Do you want to close it and continue the installation?" IDYES closeApp IDNO cancelInstall
|
||||
${Else}
|
||||
Goto installApp
|
||||
${EndIf}
|
||||
|
||||
closeApp:
|
||||
nsProcess::_KillProcess "$1"
|
||||
Pop $R0
|
||||
Sleep 500
|
||||
Goto installApp
|
||||
|
||||
cancelInstall:
|
||||
SetDetailsPrint both
|
||||
MessageBox MB_ICONEXCLAMATION|MB_OK "Installation has been aborted."
|
||||
Abort
|
||||
|
||||
installApp:
|
||||
SetOutPath "$INSTDIR"
|
||||
SetOverwrite ifnewer
|
||||
|
||||
; <EFBFBD><EFBFBD><EFBFBD>ó<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>·<EFBFBD><EFBFBD>
|
||||
; Main application executable path
|
||||
File /oname=crossdesk.exe "..\..\build\windows\x64\release\crossdesk.exe"
|
||||
|
||||
; ? <20><><EFBFBD><EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD>װĿ¼
|
||||
|
||||
; Copy icon file to installation directory
|
||||
File "${MUI_ICON}"
|
||||
|
||||
; д<EFBFBD><EFBFBD>ж<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ
|
||||
; Write uninstall information
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "DisplayName" "${PRODUCT_NAME}"
|
||||
@@ -55,9 +88,15 @@ Section "MainSection"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "DisplayIcon" "$INSTDIR\crossdesk.ico"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "DisplayIcon" "$INSTDIR\crossdesk.ico"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "NoModify" 1
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "NoRepair" 1
|
||||
WriteRegStr HKCU "Software\${PRODUCT_NAME}" "InstallDir" "$INSTDIR"
|
||||
SectionEnd
|
||||
|
||||
; After installation
|
||||
Section -Post
|
||||
ExecWait '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\mt.exe" -manifest "$INSTDIR\crossdesk.manifest" -outputresource:"$INSTDIR\crossdesk.exe";1'
|
||||
SectionEnd
|
||||
|
||||
Section "Cert"
|
||||
@@ -66,37 +105,60 @@ Section "Cert"
|
||||
SectionEnd
|
||||
|
||||
Section -AdditionalIcons
|
||||
; <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݷ<EFBFBD>ʽ
|
||||
; Desktop shortcut
|
||||
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\crossdesk.exe" "" "$INSTDIR\crossdesk.ico"
|
||||
|
||||
; <EFBFBD><EFBFBD>ʼ<EFBFBD>˵<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݷ<EFBFBD>ʽ
|
||||
; Start menu shortcut
|
||||
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" "$INSTDIR\crossdesk.exe" "" "$INSTDIR\crossdesk.ico"
|
||||
|
||||
; <20><>ҳ<EFBFBD><D2B3><EFBFBD>ݷ<EFBFBD>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD><EFBFBD>棩
|
||||
WriteIniStr "$DESKTOP\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}"
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
; ɾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD>س<EFBFBD><EFBFBD><EFBFBD>
|
||||
; Check if CrossDesk is running
|
||||
StrCpy $1 "crossdesk.exe"
|
||||
|
||||
nsProcess::_FindProcess "$1"
|
||||
Pop $R0
|
||||
${If} $R0 = 0
|
||||
MessageBox MB_ICONQUESTION|MB_YESNO "CrossDesk is running. Do you want to close it and uninstall?" IDYES closeApp IDNO cancelUninstall
|
||||
${Else}
|
||||
Goto uninstallApp
|
||||
${EndIf}
|
||||
|
||||
closeApp:
|
||||
nsProcess::_KillProcess "$1"
|
||||
Pop $R0
|
||||
Sleep 500
|
||||
Goto uninstallApp
|
||||
|
||||
cancelUninstall:
|
||||
SetDetailsPrint both
|
||||
MessageBox MB_ICONEXCLAMATION|MB_OK "Uninstallation has been aborted."
|
||||
Abort
|
||||
|
||||
uninstallApp:
|
||||
; Delete main executable and uninstaller
|
||||
Delete "$INSTDIR\crossdesk.exe"
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
|
||||
; <EFBFBD>ݹ<EFBFBD>ɾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>װĿ¼
|
||||
; Recursively delete installation directory
|
||||
RMDir /r "$INSTDIR"
|
||||
|
||||
; ɾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϳ<EFBFBD>ʼ<EFBFBD>˵<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݷ<EFBFBD>ʽ
|
||||
; Delete desktop and start menu shortcuts
|
||||
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
|
||||
Delete "$DESKTOP\${PRODUCT_NAME}.url"
|
||||
Delete "$SMPROGRAMS\${PRODUCT_NAME}.lnk"
|
||||
|
||||
; ɾ<EFBFBD><EFBFBD>ע<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
; Delete registry uninstall entry
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}"
|
||||
|
||||
; <EFBFBD>ݹ<EFBFBD>ɾ<EFBFBD><EFBFBD><EFBFBD>û<EFBFBD> AppData <20>е<EFBFBD> CrossDesk <20>ļ<EFBFBD><C4BC><EFBFBD>
|
||||
; Delete remembered install dir
|
||||
DeleteRegKey HKCU "Software\${PRODUCT_NAME}"
|
||||
|
||||
; Recursively delete CrossDesk folder in user AppData
|
||||
RMDir /r "$APPDATA\CrossDesk"
|
||||
RMDir /r "$LOCALAPPDATA\CrossDesk"
|
||||
SectionEnd
|
||||
|
||||
|
||||
Section -Post
|
||||
SectionEnd
|
||||
; ------ Functions ------
|
||||
Function LaunchApp
|
||||
Exec "$INSTDIR\crossdesk.exe"
|
||||
FunctionEnd
|
||||
|
||||
3644
src/config_center/SimpleIni.h
Normal file
@@ -1,45 +1,241 @@
|
||||
#include "config_center.h"
|
||||
|
||||
ConfigCenter::ConfigCenter() {}
|
||||
ConfigCenter::ConfigCenter(const std::string& config_path,
|
||||
const std::string& cert_file_path)
|
||||
: config_path_(config_path),
|
||||
cert_file_path_(cert_file_path),
|
||||
cert_file_path_default_(cert_file_path) {
|
||||
ini_.SetUnicode(true);
|
||||
Load();
|
||||
}
|
||||
|
||||
ConfigCenter::~ConfigCenter() {}
|
||||
|
||||
int ConfigCenter::Load() {
|
||||
SI_Error rc = ini_.LoadFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
Save();
|
||||
return -1;
|
||||
}
|
||||
|
||||
language_ = static_cast<LANGUAGE>(
|
||||
ini_.GetLongValue(section_, "language", static_cast<long>(language_)));
|
||||
|
||||
video_quality_ = static_cast<VIDEO_QUALITY>(ini_.GetLongValue(
|
||||
section_, "video_quality", static_cast<long>(video_quality_)));
|
||||
|
||||
video_frame_rate_ = static_cast<VIDEO_FRAME_RATE>(ini_.GetLongValue(
|
||||
section_, "video_frame_rate", static_cast<long>(video_frame_rate_)));
|
||||
|
||||
video_encode_format_ = static_cast<VIDEO_ENCODE_FORMAT>(
|
||||
ini_.GetLongValue(section_, "video_encode_format",
|
||||
static_cast<long>(video_encode_format_)));
|
||||
|
||||
hardware_video_codec_ = ini_.GetBoolValue(section_, "hardware_video_codec",
|
||||
hardware_video_codec_);
|
||||
|
||||
enable_turn_ = ini_.GetBoolValue(section_, "enable_turn", enable_turn_);
|
||||
enable_srtp_ = ini_.GetBoolValue(section_, "enable_srtp", enable_srtp_);
|
||||
server_host_ = ini_.GetValue(section_, "server_host", server_host_.c_str());
|
||||
server_port_ = static_cast<int>(
|
||||
ini_.GetLongValue(section_, "server_port", server_port_));
|
||||
cert_file_path_ =
|
||||
ini_.GetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
||||
enable_self_hosted_ =
|
||||
ini_.GetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||
|
||||
enable_minimize_to_tray_ = ini_.GetBoolValue(
|
||||
section_, "enable_minimize_to_tray", enable_minimize_to_tray_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::Save() {
|
||||
ini_.SetLongValue(section_, "language", static_cast<long>(language_));
|
||||
ini_.SetLongValue(section_, "video_quality",
|
||||
static_cast<long>(video_quality_));
|
||||
ini_.SetLongValue(section_, "video_frame_rate",
|
||||
static_cast<long>(video_frame_rate_));
|
||||
ini_.SetLongValue(section_, "video_encode_format",
|
||||
static_cast<long>(video_encode_format_));
|
||||
ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
|
||||
ini_.SetBoolValue(section_, "enable_turn", enable_turn_);
|
||||
ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
|
||||
ini_.SetValue(section_, "server_host", server_host_.c_str());
|
||||
ini_.SetLongValue(section_, "server_port", static_cast<long>(server_port_));
|
||||
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
||||
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||
ini_.SetBoolValue(section_, "enable_minimize_to_tray",
|
||||
enable_minimize_to_tray_);
|
||||
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// setters
|
||||
|
||||
int ConfigCenter::SetLanguage(LANGUAGE language) {
|
||||
language_ = language;
|
||||
ini_.SetLongValue(section_, "language", static_cast<long>(language_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetVideoQuality(VIDEO_QUALITY video_quality) {
|
||||
video_quality_ = video_quality;
|
||||
ini_.SetLongValue(section_, "video_quality",
|
||||
static_cast<long>(video_quality_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetVideoFrameRate(VIDEO_FRAME_RATE video_frame_rate) {
|
||||
video_frame_rate_ = video_frame_rate;
|
||||
ini_.SetLongValue(section_, "video_frame_rate",
|
||||
static_cast<long>(video_frame_rate_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetVideoEncodeFormat(
|
||||
VIDEO_ENCODE_FORMAT video_encode_format) {
|
||||
video_encode_format_ = video_encode_format;
|
||||
ini_.SetLongValue(section_, "video_encode_format",
|
||||
static_cast<long>(video_encode_format_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetHardwareVideoCodec(bool hardware_video_codec) {
|
||||
hardware_video_codec_ = hardware_video_codec;
|
||||
ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetTurn(bool enable_turn) {
|
||||
enable_turn_ = enable_turn;
|
||||
ini_.SetBoolValue(section_, "enable_turn", enable_turn_);
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() { return language_; }
|
||||
int ConfigCenter::SetSrtp(bool enable_srtp) {
|
||||
enable_srtp_ = enable_srtp;
|
||||
ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() {
|
||||
int ConfigCenter::SetServerHost(const std::string& server_host) {
|
||||
server_host_ = server_host;
|
||||
ini_.SetValue(section_, "server_host", server_host_.c_str());
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetServerPort(int server_port) {
|
||||
server_port_ = server_port;
|
||||
ini_.SetLongValue(section_, "server_port", static_cast<long>(server_port_));
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetCertFilePath(const std::string& cert_file_path) {
|
||||
cert_file_path_ = cert_file_path;
|
||||
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetSelfHosted(bool enable_self_hosted) {
|
||||
enable_self_hosted_ = enable_self_hosted;
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetMinimizeToTray(bool enable_minimize_to_tray) {
|
||||
enable_minimize_to_tray_ = enable_minimize_to_tray;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// getters
|
||||
|
||||
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; }
|
||||
|
||||
ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() const {
|
||||
return video_quality_;
|
||||
}
|
||||
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() {
|
||||
ConfigCenter::VIDEO_FRAME_RATE ConfigCenter::GetVideoFrameRate() const {
|
||||
return video_frame_rate_;
|
||||
}
|
||||
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() const {
|
||||
return video_encode_format_;
|
||||
}
|
||||
|
||||
bool ConfigCenter::IsHardwareVideoCodec() { return hardware_video_codec_; }
|
||||
bool ConfigCenter::IsHardwareVideoCodec() const {
|
||||
return hardware_video_codec_;
|
||||
}
|
||||
|
||||
bool ConfigCenter::IsEnableTurn() { return enable_turn_; }
|
||||
bool ConfigCenter::IsEnableTurn() const { return enable_turn_; }
|
||||
|
||||
bool ConfigCenter::IsEnableSrtp() const { return enable_srtp_; }
|
||||
|
||||
std::string ConfigCenter::GetServerHost() const { return server_host_; }
|
||||
|
||||
int ConfigCenter::GetServerPort() const { return server_port_; }
|
||||
|
||||
std::string ConfigCenter::GetCertFilePath() const { return cert_file_path_; }
|
||||
|
||||
std::string ConfigCenter::GetDefaultServerHost() const {
|
||||
return server_host_default_;
|
||||
}
|
||||
|
||||
int ConfigCenter::GetDefaultServerPort() const { return server_port_default_; }
|
||||
|
||||
std::string ConfigCenter::GetDefaultCertFilePath() const {
|
||||
return cert_file_path_default_;
|
||||
}
|
||||
|
||||
bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }
|
||||
|
||||
bool ConfigCenter::IsMinimizeToTray() const { return enable_minimize_to_tray_; }
|
||||
@@ -7,37 +7,78 @@
|
||||
#ifndef _CONFIG_CENTER_H_
|
||||
#define _CONFIG_CENTER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "SimpleIni.h"
|
||||
|
||||
class ConfigCenter {
|
||||
public:
|
||||
enum class LANGUAGE { CHINESE = 0, ENGLISH = 1 };
|
||||
enum class VIDEO_QUALITY { LOW = 0, MEDIUM = 1, HIGH = 2 };
|
||||
enum class VIDEO_ENCODE_FORMAT { AV1 = 0, H264 = 1 };
|
||||
enum class VIDEO_FRAME_RATE { FPS_30 = 0, FPS_60 = 1 };
|
||||
enum class VIDEO_ENCODE_FORMAT { H264 = 0, AV1 = 1 };
|
||||
|
||||
public:
|
||||
ConfigCenter();
|
||||
explicit ConfigCenter(
|
||||
const std::string& config_path = "config.ini",
|
||||
const std::string& cert_file_path = "crossdesk.cn_root.crt");
|
||||
~ConfigCenter();
|
||||
|
||||
public:
|
||||
// write config
|
||||
int SetLanguage(LANGUAGE language);
|
||||
int SetVideoQuality(VIDEO_QUALITY video_quality);
|
||||
int SetVideoFrameRate(VIDEO_FRAME_RATE video_frame_rate);
|
||||
int SetVideoEncodeFormat(VIDEO_ENCODE_FORMAT video_encode_format);
|
||||
int SetHardwareVideoCodec(bool hardware_video_codec);
|
||||
int SetTurn(bool enable_turn);
|
||||
int SetSrtp(bool enable_srtp);
|
||||
int SetServerHost(const std::string& server_host);
|
||||
int SetServerPort(int server_port);
|
||||
int SetCertFilePath(const std::string& cert_file_path);
|
||||
int SetSelfHosted(bool enable_self_hosted);
|
||||
int SetMinimizeToTray(bool enable_minimize_to_tray);
|
||||
|
||||
public:
|
||||
LANGUAGE GetLanguage();
|
||||
VIDEO_QUALITY GetVideoQuality();
|
||||
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat();
|
||||
bool IsHardwareVideoCodec();
|
||||
bool IsEnableTurn();
|
||||
// read config
|
||||
|
||||
LANGUAGE GetLanguage() const;
|
||||
VIDEO_QUALITY GetVideoQuality() const;
|
||||
VIDEO_FRAME_RATE GetVideoFrameRate() const;
|
||||
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat() const;
|
||||
bool IsHardwareVideoCodec() const;
|
||||
bool IsEnableTurn() const;
|
||||
bool IsEnableSrtp() const;
|
||||
std::string GetServerHost() const;
|
||||
int GetServerPort() const;
|
||||
std::string GetCertFilePath() const;
|
||||
std::string GetDefaultServerHost() const;
|
||||
int GetDefaultServerPort() const;
|
||||
std::string GetDefaultCertFilePath() const;
|
||||
bool IsSelfHosted() const;
|
||||
bool IsMinimizeToTray() const;
|
||||
|
||||
int Load();
|
||||
int Save();
|
||||
|
||||
private:
|
||||
// Default value should be same with parameters in localization.h
|
||||
std::string config_path_;
|
||||
std::string cert_file_path_;
|
||||
CSimpleIniA ini_;
|
||||
const char* section_ = "Settings";
|
||||
|
||||
LANGUAGE language_ = LANGUAGE::CHINESE;
|
||||
VIDEO_QUALITY video_quality_ = VIDEO_QUALITY::MEDIUM;
|
||||
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::AV1;
|
||||
VIDEO_FRAME_RATE video_frame_rate_ = VIDEO_FRAME_RATE::FPS_30;
|
||||
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::H264;
|
||||
bool hardware_video_codec_ = false;
|
||||
bool enable_turn_ = false;
|
||||
bool enable_srtp_ = false;
|
||||
std::string server_host_ = "api.crossdesk.cn";
|
||||
int server_port_ = 9099;
|
||||
std::string server_host_default_ = "api.crossdesk.cn";
|
||||
int server_port_default_ = 9099;
|
||||
std::string cert_file_path_default_ = "";
|
||||
bool enable_self_hosted_ = false;
|
||||
bool enable_minimize_to_tray_ = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -123,12 +123,44 @@ int KeyboardCapturer::Unhook() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool IsFunctionKey(int key_code) {
|
||||
switch (key_code) {
|
||||
case 0x7A:
|
||||
case 0x78:
|
||||
case 0x63:
|
||||
case 0x76:
|
||||
case 0x60:
|
||||
case 0x61:
|
||||
case 0x62:
|
||||
case 0x64:
|
||||
case 0x65:
|
||||
case 0x6D:
|
||||
case 0x67:
|
||||
case 0x6F:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down) {
|
||||
if (vkCodeToCGKeyCode.find(key_code) != vkCodeToCGKeyCode.end()) {
|
||||
CGKeyCode cg_key_code = vkCodeToCGKeyCode[key_code];
|
||||
CGEventRef event = CGEventCreateKeyboardEvent(NULL, cg_key_code, is_down);
|
||||
CGEventRef clearFlags =
|
||||
CGEventCreateKeyboardEvent(NULL, (CGKeyCode)0, true);
|
||||
CGEventSetFlags(clearFlags, 0);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
|
||||
// F1-F12 keys often require the FN key to be pressed on Mac keyboards, so
|
||||
// we simulate the FN key release when an F1-F12 key is released.
|
||||
if (IsFunctionKey(cg_key_code) && !is_down) {
|
||||
CGEventRef fn_release_event =
|
||||
CGEventCreateKeyboardEvent(NULL, fn_key_code_, false);
|
||||
CGEventPost(kCGHIDEventTap, fn_release_event);
|
||||
CFRelease(fn_release_event);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -31,6 +31,7 @@ class KeyboardCapturer : public DeviceController {
|
||||
bool control_flag_ = false;
|
||||
bool option_flag_ = false;
|
||||
bool command_flag_ = false;
|
||||
int fn_key_code_ = 0x3F;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -247,58 +247,494 @@ std::map<int, int> CGKeyCodeToVkCode = {
|
||||
|
||||
// Windows vkCode to X11 KeySym
|
||||
std::map<int, int> vkCodeToX11KeySym = {
|
||||
{0x41, 0x0041}, {0x42, 0x0042}, {0x43, 0x0043}, {0x44, 0x0044},
|
||||
{0x45, 0x0045}, {0x46, 0x0046}, {0x47, 0x0047}, {0x48, 0x0048},
|
||||
{0x49, 0x0049}, {0x4A, 0x004A}, {0x4B, 0x004B}, {0x4C, 0x004C},
|
||||
{0x4D, 0x004D}, {0x4E, 0x004E}, {0x4F, 0x004F}, {0x50, 0x0050},
|
||||
{0x51, 0x0051}, {0x52, 0x0052}, {0x53, 0x0053}, {0x54, 0x0054},
|
||||
{0x55, 0x0055}, {0x56, 0x0056}, {0x57, 0x0057}, {0x58, 0x0058},
|
||||
{0x59, 0x0059}, {0x5A, 0x005A}, {0x30, 0x0030}, {0x31, 0x0031},
|
||||
{0x32, 0x0032}, {0x33, 0x0033}, {0x34, 0x0034}, {0x35, 0x0035},
|
||||
{0x36, 0x0036}, {0x37, 0x0037}, {0x38, 0x0038}, {0x39, 0x0039},
|
||||
{0x1B, 0xFF1B}, {0x0D, 0xFF0D}, {0x20, 0x0020}, {0x08, 0xFF08},
|
||||
{0x09, 0xFF09}, {0x25, 0xFF51}, {0x27, 0xFF53}, {0x26, 0xFF52},
|
||||
{0x28, 0xFF54}, {0x70, 0xFFBE}, {0x71, 0xFFBF}, {0x72, 0xFFC0},
|
||||
{0x73, 0xFFC1}, {0x74, 0xFFC2}, {0x75, 0xFFC3}, {0x76, 0xFFC4},
|
||||
{0x77, 0xFFC5}, {0x78, 0xFFC6}, {0x79, 0xFFC7}, {0x7A, 0xFFC8},
|
||||
{0x7B, 0xFFC9},
|
||||
// A-Z
|
||||
{0x41, 0x0041}, // A
|
||||
{0x42, 0x0042}, // B
|
||||
{0x43, 0x0043}, // C
|
||||
{0x44, 0x0044}, // D
|
||||
{0x45, 0x0045}, // E
|
||||
{0x46, 0x0046}, // F
|
||||
{0x47, 0x0047}, // G
|
||||
{0x48, 0x0048}, // H
|
||||
{0x49, 0x0049}, // I
|
||||
{0x4A, 0x004A}, // J
|
||||
{0x4B, 0x004B}, // K
|
||||
{0x4C, 0x004C}, // L
|
||||
{0x4D, 0x004D}, // M
|
||||
{0x4E, 0x004E}, // N
|
||||
{0x4F, 0x004F}, // O
|
||||
{0x50, 0x0050}, // P
|
||||
{0x51, 0x0051}, // Q
|
||||
{0x52, 0x0052}, // R
|
||||
{0x53, 0x0053}, // S
|
||||
{0x54, 0x0054}, // T
|
||||
{0x55, 0x0055}, // U
|
||||
{0x56, 0x0056}, // V
|
||||
{0x57, 0x0057}, // W
|
||||
{0x58, 0x0058}, // X
|
||||
{0x59, 0x0059}, // Y
|
||||
{0x5A, 0x005A}, // Z
|
||||
|
||||
// 0-9
|
||||
{0x30, 0x0030}, // 0
|
||||
{0x31, 0x0031}, // 1
|
||||
{0x32, 0x0032}, // 2
|
||||
{0x33, 0x0033}, // 3
|
||||
{0x34, 0x0034}, // 4
|
||||
{0x35, 0x0035}, // 5
|
||||
{0x36, 0x0036}, // 6
|
||||
{0x37, 0x0037}, // 7
|
||||
{0x38, 0x0038}, // 8
|
||||
{0x39, 0x0039}, // 9
|
||||
|
||||
// F1-F12
|
||||
{0x70, 0xFFBE}, // F1
|
||||
{0x71, 0xFFBF}, // F2
|
||||
{0x72, 0xFFC0}, // F3
|
||||
{0x73, 0xFFC1}, // F4
|
||||
{0x74, 0xFFC2}, // F5
|
||||
{0x75, 0xFFC3}, // F6
|
||||
{0x76, 0xFFC4}, // F7
|
||||
{0x77, 0xFFC5}, // F8
|
||||
{0x78, 0xFFC6}, // F9
|
||||
{0x79, 0xFFC7}, // F10
|
||||
{0x7A, 0xFFC8}, // F11
|
||||
{0x7B, 0xFFC9}, // F12
|
||||
|
||||
// control keys
|
||||
{0x1B, 0xFF1B}, // Escape
|
||||
{0x0D, 0xFF0D}, // Enter
|
||||
{0x20, 0x0020}, // Space
|
||||
{0x08, 0xFF08}, // Backspace
|
||||
{0x09, 0xFF09}, // Tab
|
||||
{0x2C, 0xFF15}, // Print Screen
|
||||
{0x91, 0xFF14}, // Scroll Lock
|
||||
{0x13, 0xFF13}, // Pause/Break
|
||||
{0x2D, 0xFF63}, // Insert
|
||||
{0x2E, 0xFFFF}, // Delete
|
||||
{0x24, 0xFF50}, // Home
|
||||
{0x23, 0xFF57}, // End
|
||||
{0x21, 0xFF55}, // Page Up
|
||||
{0x22, 0xFF56}, // Page Down
|
||||
|
||||
// arrow keys
|
||||
{0x25, 0xFF51}, // Left Arrow
|
||||
{0x27, 0xFF53}, // Right Arrow
|
||||
{0x26, 0xFF52}, // Up Arrow
|
||||
{0x28, 0xFF54}, // Down Arrow
|
||||
|
||||
// numpad
|
||||
{0x60, 0x0030}, // Numpad 0
|
||||
{0x61, 0x0031}, // Numpad 1
|
||||
{0x62, 0x0032}, // Numpad 2
|
||||
{0x63, 0x0033}, // Numpad 3
|
||||
{0x64, 0x0034}, // Numpad 4
|
||||
{0x65, 0x0035}, // Numpad 5
|
||||
{0x66, 0x0036}, // Numpad 6
|
||||
{0x67, 0x0037}, // Numpad 7
|
||||
{0x68, 0x0038}, // Numpad 8
|
||||
{0x69, 0x0039}, // Numpad 9
|
||||
{0x6E, 0x003A}, // Numpad .
|
||||
{0x6F, 0x002F}, // Numpad /
|
||||
{0x6A, 0x002A}, // Numpad *
|
||||
{0x6D, 0x002D}, // Numpad -
|
||||
{0x6B, 0x002B}, // Numpad +
|
||||
|
||||
// symbol keys
|
||||
{0xBA, 0x003B}, // ; (Semicolon)
|
||||
{0xDE, 0x0027}, // ' (Quote)
|
||||
{0xC0, 0x007E}, // ` (Grave)
|
||||
{0xBC, 0x002C}, // , (Comma)
|
||||
{0xBE, 0x002E}, // . (Period)
|
||||
{0xBF, 0x002F}, // / (Slash)
|
||||
{0xDC, 0x005C}, // \ (Backslash)
|
||||
{0xDB, 0x005B}, // [ (Left Bracket)
|
||||
{0xDD, 0x005D}, // ] (Right Bracket)
|
||||
{0xBD, 0x002D}, // - (Minus)
|
||||
{0xBB, 0x003D}, // = (Equals)
|
||||
|
||||
// modifier keys
|
||||
{0x14, 0xFFE5}, // Caps Lock
|
||||
{0xA0, 0xFFE1}, // Shift (Left)
|
||||
{0xA1, 0xFFE2}, // Shift (Right)
|
||||
{0xA2, 0xFFE3}, // Ctrl (Left)
|
||||
{0xA3, 0xFFE4}, // Ctrl (Right)
|
||||
{0xA4, 0xFFE9}, // Alt (Left)
|
||||
{0xA5, 0xFFEA}, // Alt (Right)
|
||||
{0x5B, 0xFFEB}, // Left Command (Windows key)
|
||||
{0x5C, 0xFFEC}, // Right Command
|
||||
};
|
||||
|
||||
// X11 KeySym to Windows vkCode
|
||||
std::map<int, int> x11KeySymToVkCode = []() {
|
||||
std::map<int, int> result;
|
||||
for (const auto& pair : vkCodeToX11KeySym) {
|
||||
result[pair.second] = pair.first;
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
// std::map<int, int> x11KeySymToVkCode = []() {
|
||||
// std::map<int, int> result;
|
||||
// for (const auto& pair : vkCodeToX11KeySym) {
|
||||
// result[pair.second] = pair.first;
|
||||
// }
|
||||
// return result;
|
||||
// }();
|
||||
|
||||
std::map<int, int> x11KeySymToVkCode = {
|
||||
// A-Z
|
||||
{0x0041, 0x41}, // A
|
||||
{0x0042, 0x42}, // B
|
||||
{0x0043, 0x43}, // C
|
||||
{0x0044, 0x44}, // D
|
||||
{0x0045, 0x45}, // E
|
||||
{0x0046, 0x46}, // F
|
||||
{0x0047, 0x47}, // G
|
||||
{0x0048, 0x48}, // H
|
||||
{0x0049, 0x49}, // I
|
||||
{0x004A, 0x4A}, // J
|
||||
{0x004B, 0x4B}, // K
|
||||
{0x004C, 0x4C}, // L
|
||||
{0x004D, 0x4D}, // M
|
||||
{0x004E, 0x4E}, // N
|
||||
{0x004F, 0x4F}, // O
|
||||
{0x0050, 0x50}, // P
|
||||
{0x0051, 0x51}, // Q
|
||||
{0x0052, 0x52}, // R
|
||||
{0x0053, 0x53}, // S
|
||||
{0x0054, 0x54}, // T
|
||||
{0x0055, 0x55}, // U
|
||||
{0x0056, 0x56}, // V
|
||||
{0x0057, 0x57}, // W
|
||||
{0x0058, 0x58}, // X
|
||||
{0x0059, 0x59}, // Y
|
||||
{0x005A, 0x5A}, // Z
|
||||
|
||||
// 0-9
|
||||
{0x0030, 0x30}, // 0
|
||||
{0x0031, 0x31}, // 1
|
||||
{0x0032, 0x32}, // 2
|
||||
{0x0033, 0x33}, // 3
|
||||
{0x0034, 0x34}, // 4
|
||||
{0x0035, 0x35}, // 5
|
||||
{0x0036, 0x36}, // 6
|
||||
{0x0037, 0x37}, // 7
|
||||
{0x0038, 0x38}, // 8
|
||||
{0x0039, 0x39}, // 9
|
||||
|
||||
// F1-F12
|
||||
{0xFFBE, 0x70}, // F1
|
||||
{0xFFBF, 0x71}, // F2
|
||||
{0xFFC0, 0x72}, // F3
|
||||
{0xFFC1, 0x73}, // F4
|
||||
{0xFFC2, 0x74}, // F5
|
||||
{0xFFC3, 0x75}, // F6
|
||||
{0xFFC4, 0x76}, // F7
|
||||
{0xFFC5, 0x77}, // F8
|
||||
{0xFFC6, 0x78}, // F9
|
||||
{0xFFC7, 0x79}, // F10
|
||||
{0xFFC8, 0x7A}, // F11
|
||||
{0xFFC9, 0x7B}, // F12
|
||||
|
||||
// control keys
|
||||
{0xFF1B, 0x1B}, // Escape
|
||||
{0xFF0D, 0x0D}, // Enter
|
||||
{0x0020, 0x20}, // Space
|
||||
{0xFF08, 0x08}, // Backspace
|
||||
{0xFF09, 0x09}, // Tab
|
||||
{0xFF15, 0x2C}, // Print Screen
|
||||
{0xFF14, 0x91}, // Scroll Lock
|
||||
{0xFF13, 0x13}, // Pause/Break
|
||||
{0xFF63, 0x2D}, // Insert
|
||||
{0xFFFF, 0x2E}, // Delete
|
||||
{0xFF50, 0x24}, // Home
|
||||
{0xFF57, 0x23}, // End
|
||||
{0xFF55, 0x21}, // Page Up
|
||||
{0xFF56, 0x22}, // Page Down
|
||||
|
||||
// arrow keys
|
||||
{0xFF51, 0x25}, // Left Arrow
|
||||
{0xFF53, 0x27}, // Right Arrow
|
||||
{0xFF52, 0x26}, // Up Arrow
|
||||
{0xFF54, 0x28}, // Down Arrow
|
||||
|
||||
// numpad
|
||||
{0x0030, 0x60}, // Numpad 0
|
||||
{0x0031, 0x61}, // Numpad 1
|
||||
{0x0032, 0x62}, // Numpad 2
|
||||
{0x0033, 0x63}, // Numpad 3
|
||||
{0x0034, 0x64}, // Numpad 4
|
||||
{0x0035, 0x65}, // Numpad 5
|
||||
{0x0036, 0x66}, // Numpad 6
|
||||
{0x0037, 0x67}, // Numpad 7
|
||||
{0x0038, 0x68}, // Numpad 8
|
||||
{0x0039, 0x69}, // Numpad 9
|
||||
{0x003A, 0x6E}, // Numpad .
|
||||
{0x002F, 0x6F}, // Numpad /
|
||||
{0x002A, 0x6A}, // Numpad *
|
||||
{0x002D, 0x6D}, // Numpad -
|
||||
{0x002B, 0x6B}, // Numpad +
|
||||
|
||||
// symbol keys
|
||||
{0x003B, 0xBA}, // ; (Semicolon)
|
||||
{0x0027, 0xDE}, // ' (Quote)
|
||||
{0x007E, 0xC0}, // ` (Grave)
|
||||
{0x002C, 0xBC}, // , (Comma)
|
||||
{0x002E, 0xBE}, // . (Period)
|
||||
{0x002F, 0xBF}, // / (Slash)
|
||||
{0x005C, 0xDC}, // \ (Backslash)
|
||||
{0x005B, 0xDB}, // [ (Left Bracket)
|
||||
{0x005D, 0xDD}, // ] (Right Bracket)
|
||||
{0x002D, 0xBD}, // - (Minus)
|
||||
{0x003D, 0xBB}, // = (Equals)
|
||||
|
||||
// modifier keys
|
||||
{0xFFE5, 0x14}, // Caps Lock
|
||||
{0xFFE1, 0xA0}, // Shift (Left)
|
||||
{0xFFE2, 0xA1}, // Shift (Right)
|
||||
{0xFFE3, 0xA2}, // Ctrl (Left)
|
||||
{0xFFE4, 0xA3}, // Ctrl (Right)
|
||||
{0xFFE9, 0xA4}, // Alt (Left)
|
||||
{0xFFEA, 0xA5}, // Alt (Right)
|
||||
{0xFFEB, 0x5B}, // Left Command (Windows key)
|
||||
{0xFFEC, 0x5C}, // Right Command
|
||||
};
|
||||
|
||||
// macOS CGKeyCode to X11 KeySym
|
||||
std::map<int, int> cgKeyCodeToX11KeySym = {
|
||||
{0x00, 0x0041}, {0x0B, 0x0042}, {0x08, 0x0043}, {0x02, 0x0044},
|
||||
{0x0E, 0x0045}, {0x03, 0x0046}, {0x05, 0x0047}, {0x04, 0x0048},
|
||||
{0x22, 0x0049}, {0x26, 0x004A}, {0x28, 0x004B}, {0x25, 0x004C},
|
||||
{0x2E, 0x004D}, {0x2D, 0x004E}, {0x1F, 0x004F}, {0x23, 0x0050},
|
||||
{0x0C, 0x0051}, {0x0F, 0x0052}, {0x01, 0x0053}, {0x11, 0x0054},
|
||||
{0x20, 0x0055}, {0x09, 0x0056}, {0x0D, 0x0057}, {0x07, 0x0058},
|
||||
{0x10, 0x0059}, {0x06, 0x005A}, {0x12, 0x0031}, {0x13, 0x0032},
|
||||
{0x14, 0x0033}, {0x15, 0x0034}, {0x17, 0x0035}, {0x16, 0x0036},
|
||||
{0x1A, 0x0037}, {0x1C, 0x0038}, {0x19, 0x0039}, {0x1D, 0x0030},
|
||||
{0x35, 0xFF1B}, {0x24, 0xFF0D}, {0x31, 0x0020}, {0x33, 0xFF08},
|
||||
{0x30, 0xFF09}, {0x7B, 0xFF51}, {0x7C, 0xFF53}, {0x7E, 0xFF52},
|
||||
{0x7D, 0xFF54}, {0x7A, 0xFFBE}, {0x78, 0xFFBF}, {0x63, 0xFFC0},
|
||||
{0x76, 0xFFC1}, {0x60, 0xFFC2}, {0x61, 0xFFC3}, {0x62, 0xFFC4},
|
||||
{0x64, 0xFFC5}, {0x65, 0xFFC6}, {0x6D, 0xFFC7}, {0x67, 0xFFC8},
|
||||
{0x6F, 0xFFC9},
|
||||
// A-Z
|
||||
{0x00, 0x0041}, // A
|
||||
{0x0B, 0x0042}, // B
|
||||
{0x08, 0x0043}, // C
|
||||
{0x02, 0x0044}, // D
|
||||
{0x0E, 0x0045}, // E
|
||||
{0x03, 0x0046}, // F
|
||||
{0x05, 0x0047}, // G
|
||||
{0x04, 0x0048}, // H
|
||||
{0x22, 0x0049}, // I
|
||||
{0x26, 0x004A}, // J
|
||||
{0x28, 0x004B}, // K
|
||||
{0x25, 0x004C}, // L
|
||||
{0x2E, 0x004D}, // M
|
||||
{0x2D, 0x004E}, // N
|
||||
{0x1F, 0x004F}, // O
|
||||
{0x23, 0x0050}, // P
|
||||
{0x0C, 0x0051}, // Q
|
||||
{0x0F, 0x0052}, // R
|
||||
{0x01, 0x0053}, // S
|
||||
{0x11, 0x0054}, // T
|
||||
{0x20, 0x0055}, // U
|
||||
{0x09, 0x0056}, // V
|
||||
{0x0D, 0x0057}, // W
|
||||
{0x07, 0x0058}, // X
|
||||
{0x10, 0x0059}, // Y
|
||||
{0x06, 0x005A}, // Z
|
||||
|
||||
// 0-9
|
||||
{0x1D, 0x0030}, // 0
|
||||
{0x12, 0x0031}, // 1
|
||||
{0x13, 0x0032}, // 2
|
||||
{0x14, 0x0033}, // 3
|
||||
{0x15, 0x0034}, // 4
|
||||
{0x17, 0x0035}, // 5
|
||||
{0x16, 0x0036}, // 6
|
||||
{0x1A, 0x0037}, // 7
|
||||
{0x1C, 0x0038}, // 8
|
||||
{0x19, 0x0039}, // 9
|
||||
|
||||
// F1-F12
|
||||
{0x7A, 0xFFBE}, // F1
|
||||
{0x78, 0xFFBF}, // F2
|
||||
{0x63, 0xFFC0}, // F3
|
||||
{0x76, 0xFFC1}, // F4
|
||||
{0x60, 0xFFC2}, // F5
|
||||
{0x61, 0xFFC3}, // F6
|
||||
{0x62, 0xFFC4}, // F7
|
||||
{0x64, 0xFFC5}, // F8
|
||||
{0x65, 0xFFC6}, // F9
|
||||
{0x6D, 0xFFC7}, // F10
|
||||
{0x67, 0xFFC8}, // F11
|
||||
{0x6F, 0xFFC9}, // F12
|
||||
|
||||
// control keys
|
||||
{0x35, 0xFF1B}, // Escape
|
||||
{0x24, 0xFF0D}, // Enter
|
||||
{0x31, 0x0020}, // Space
|
||||
{0x33, 0xFF08}, // Backspace
|
||||
{0x30, 0xFF09}, // Tab
|
||||
{0x74, 0xFF15}, // Print Screen
|
||||
{0x72, 0xFF63}, // Insert
|
||||
{0x75, 0xFFFF}, // Delete
|
||||
{0x73, 0xFF50}, // Home
|
||||
{0x77, 0xFF57}, // End
|
||||
{0x79, 0xFF55}, // Page Up
|
||||
{0x7A, 0xFF56}, // Page Down
|
||||
|
||||
// arrow keys
|
||||
{0x7B, 0xFF51}, // Left Arrow
|
||||
{0x7C, 0xFF53}, // Right Arrow
|
||||
{0x7E, 0xFF52}, // Up Arrow
|
||||
{0x7D, 0xFF54}, // Down Arrow
|
||||
|
||||
// numpad
|
||||
{0x52, 0x0030}, // Numpad 0
|
||||
{0x53, 0x0031}, // Numpad 1
|
||||
{0x54, 0x0032}, // Numpad 2
|
||||
{0x55, 0x0033}, // Numpad 3
|
||||
{0x56, 0x0034}, // Numpad 4
|
||||
{0x57, 0x0035}, // Numpad 5
|
||||
{0x58, 0x0036}, // Numpad 6
|
||||
{0x59, 0x0037}, // Numpad 7
|
||||
{0x5B, 0x0038}, // Numpad 8
|
||||
{0x5C, 0x0039}, // Numpad 9
|
||||
{0x41, 0x003A}, // Numpad .
|
||||
{0x4B, 0x002F}, // Numpad /
|
||||
{0x43, 0x002A}, // Numpad *
|
||||
{0x4E, 0x002D}, // Numpad -
|
||||
{0x45, 0x002B}, // Numpad +
|
||||
|
||||
// symbol keys
|
||||
{0x29, 0x003B}, // ; (Semicolon)
|
||||
{0x27, 0x0027}, // ' (Quote)
|
||||
{0x32, 0x007E}, // ` (Backtick)
|
||||
{0x2B, 0x002C}, // , (Comma)
|
||||
{0x2F, 0x002E}, // . (Period)
|
||||
{0x2C, 0x002F}, // / (Slash)
|
||||
{0x2A, 0x005C}, // \ (Backslash)
|
||||
{0x21, 0x005B}, // [ (Left Bracket)
|
||||
{0x1E, 0x005D}, // ] (Right Bracket)
|
||||
{0x1B, 0x002D}, // - (Minus)
|
||||
{0x18, 0x003D}, // = (Equals)
|
||||
|
||||
// modifier keys
|
||||
{0x39, 0xFFE5}, // Caps Lock
|
||||
{0x38, 0xFFE1}, // Shift (Left)
|
||||
{0x3C, 0xFFE2}, // Shift (Right)
|
||||
{0x3B, 0xFFE3}, // Control (Left)
|
||||
{0x3E, 0xFFE4}, // Control (Right)
|
||||
{0x3A, 0xFFE9}, // Alt (Left)
|
||||
{0x3D, 0xFFEA}, // Alt (Right)
|
||||
{0x37, 0xFFEB}, // Left Command (Windows key)
|
||||
{0x36, 0xFFEC}, // Right Command
|
||||
};
|
||||
|
||||
// X11 KeySym to macOS CGKeyCode
|
||||
std::map<int, int> x11KeySymToCgKeyCode = []() {
|
||||
std::map<int, int> result;
|
||||
for (const auto& pair : cgKeyCodeToX11KeySym) {
|
||||
result[pair.second] = pair.first;
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
// std::map<int, int> x11KeySymToCgKeyCode = []() {
|
||||
// std::map<int, int> result;
|
||||
// for (const auto& pair : cgKeyCodeToX11KeySym) {
|
||||
// result[pair.second] = pair.first;
|
||||
// }
|
||||
// return result;
|
||||
// }();
|
||||
|
||||
std::map<int, int> x11KeySymToCgKeyCode = {
|
||||
// A-Z
|
||||
{0x0041, 0x00}, // A
|
||||
{0x0042, 0x0B}, // B
|
||||
{0x0043, 0x08}, // C
|
||||
{0x0044, 0x02}, // D
|
||||
{0x0045, 0x0E}, // E
|
||||
{0x0046, 0x03}, // F
|
||||
{0x0047, 0x05}, // G
|
||||
{0x0048, 0x04}, // H
|
||||
{0x0049, 0x22}, // I
|
||||
{0x004A, 0x26}, // J
|
||||
{0x004B, 0x28}, // K
|
||||
{0x004C, 0x25}, // L
|
||||
{0x004D, 0x2E}, // M
|
||||
{0x004E, 0x2D}, // N
|
||||
{0x004F, 0x1F}, // O
|
||||
{0x0050, 0x23}, // P
|
||||
{0x0051, 0x0C}, // Q
|
||||
{0x0052, 0x0F}, // R
|
||||
{0x0053, 0x01}, // S
|
||||
{0x0054, 0x11}, // T
|
||||
{0x0055, 0x20}, // U
|
||||
{0x0056, 0x09}, // V
|
||||
{0x0057, 0x0D}, // W
|
||||
{0x0058, 0x07}, // X
|
||||
{0x0059, 0x10}, // Y
|
||||
{0x005A, 0x06}, // Z
|
||||
|
||||
// 0-9
|
||||
{0x0030, 0x1D}, // 0
|
||||
{0x0031, 0x12}, // 1
|
||||
{0x0032, 0x13}, // 2
|
||||
{0x0033, 0x14}, // 3
|
||||
{0x0034, 0x15}, // 4
|
||||
{0x0035, 0x17}, // 5
|
||||
{0x0036, 0x16}, // 6
|
||||
{0x0037, 0x1A}, // 7
|
||||
{0x0038, 0x1C}, // 8
|
||||
{0x0039, 0x19}, // 9
|
||||
|
||||
// F1-F12
|
||||
{0xFFBE, 0x7A}, // F1
|
||||
{0xFFBF, 0x78}, // F2
|
||||
{0xFFC0, 0x63}, // F3
|
||||
{0xFFC1, 0x76}, // F4
|
||||
{0xFFC2, 0x60}, // F5
|
||||
{0xFFC3, 0x61}, // F6
|
||||
{0xFFC4, 0x62}, // F7
|
||||
{0xFFC5, 0x64}, // F8
|
||||
{0xFFC6, 0x65}, // F9
|
||||
{0xFFC7, 0x6D}, // F10
|
||||
{0xFFC8, 0x67}, // F11
|
||||
{0xFFC9, 0x6F}, // F12
|
||||
|
||||
// control keys
|
||||
{0xFF1B, 0x35}, // Escape
|
||||
{0xFF0D, 0x24}, // Enter
|
||||
{0x0020, 0x31}, // Space
|
||||
{0xFF08, 0x33}, // Backspace
|
||||
{0xFF09, 0x30}, // Tab
|
||||
{0xFF15, 0x74}, // Print Screen
|
||||
{0xFF63, 0x72}, // Insert
|
||||
{0xFFFF, 0x75}, // Delete
|
||||
{0xFF50, 0x73}, // Home
|
||||
{0xFF57, 0x77}, // End
|
||||
{0xFF55, 0x79}, // Page Up
|
||||
{0xFF56, 0x7A}, // Page Down
|
||||
|
||||
// arrow keys
|
||||
{0xFF51, 0x7B}, // Left Arrow
|
||||
{0xFF53, 0x7C}, // Right Arrow
|
||||
{0xFF52, 0x7E}, // Up Arrow
|
||||
{0xFF54, 0x7D}, // Down Arrow
|
||||
|
||||
// numpad
|
||||
{0x0030, 0x52}, // Numpad 0
|
||||
{0x0031, 0x53}, // Numpad 1
|
||||
{0x0032, 0x54}, // Numpad 2
|
||||
{0x0033, 0x55}, // Numpad 3
|
||||
{0x0034, 0x56}, // Numpad 4
|
||||
{0x0035, 0x57}, // Numpad 5
|
||||
{0x0036, 0x58}, // Numpad 6
|
||||
{0x0037, 0x59}, // Numpad 7
|
||||
{0x0038, 0x5B}, // Numpad 8
|
||||
{0x0039, 0x5C}, // Numpad 9
|
||||
{0x003A, 0x41}, // Numpad .
|
||||
{0x002F, 0x4B}, // Numpad /
|
||||
{0x002A, 0x43}, // Numpad *
|
||||
{0x002D, 0x4E}, // Numpad -
|
||||
{0x002B, 0x45}, // Numpad +
|
||||
|
||||
// symbol keys
|
||||
{0x003B, 0x29}, // ; (Semicolon)
|
||||
{0x0027, 0x27}, // ' (Quote)
|
||||
{0x007E, 0x32}, // ` (Backtick)
|
||||
{0x002C, 0x2B}, // , (Comma)
|
||||
{0x002E, 0x2F}, // . (Period)
|
||||
{0x002F, 0x2C}, // / (Slash)
|
||||
{0x005C, 0x2A}, // \ (Backslash)
|
||||
{0x005B, 0x21}, // [ (Left Bracket)
|
||||
{0x005D, 0x1E}, // ] (Right Bracket)
|
||||
{0x002D, 0x1B}, // - (Minus)
|
||||
{0x003D, 0x18}, // = (Equals)
|
||||
|
||||
// modifier keys
|
||||
{0xFFE5, 0x39}, // Caps Lock
|
||||
{0xFFE1, 0x38}, // Shift (Left)
|
||||
{0xFFE2, 0x3C}, // Shift (Right)
|
||||
{0xFFE3, 0x3B}, // Control (Left)
|
||||
{0xFFE4, 0x3E}, // Control (Right)
|
||||
{0xFFE9, 0x3A}, // Alt (Left)
|
||||
{0xFFEA, 0x3D}, // Alt (Right)
|
||||
{0xFFEB, 0x37}, // Left Command
|
||||
{0xFFEC, 0x36}, // Right Command
|
||||
};
|
||||
|
||||
#endif
|
||||
2802
src/device_controller/linux_keycode.h
Normal file
861
src/device_controller/macos_keycode.h
Normal file
@@ -0,0 +1,861 @@
|
||||
/*
|
||||
File: HIToolbox/Events.h
|
||||
|
||||
Contains: Event Manager Interfaces.
|
||||
|
||||
Copyright: 锟<> 1985-2008 by Apple Computer, Inc., all rights reserved
|
||||
|
||||
Bugs?: For bug reports, consult the following page on
|
||||
the World Wide Web:
|
||||
|
||||
http://developer.apple.com/bugreporter/
|
||||
|
||||
*/
|
||||
#ifndef __EVENTS__
|
||||
#define __EVENTS__
|
||||
|
||||
#ifndef __APPLICATIONSERVICES__
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
|
||||
#if PRAGMA_ONCE
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 2)
|
||||
|
||||
typedef UInt16 EventKind;
|
||||
typedef UInt16 EventMask;
|
||||
enum {
|
||||
nullEvent = 0,
|
||||
mouseDown = 1,
|
||||
mouseUp = 2,
|
||||
keyDown = 3,
|
||||
keyUp = 4,
|
||||
autoKey = 5,
|
||||
updateEvt = 6,
|
||||
diskEvt = 7, /* Not sent in Carbon. See kEventClassVolume in CarbonEvents.h*/
|
||||
activateEvt = 8,
|
||||
osEvt = 15,
|
||||
kHighLevelEvent = 23
|
||||
};
|
||||
|
||||
enum {
|
||||
mDownMask = 1 << mouseDown, /* mouse button pressed*/
|
||||
mUpMask = 1 << mouseUp, /* mouse button released*/
|
||||
keyDownMask = 1 << keyDown, /* key pressed*/
|
||||
keyUpMask = 1 << keyUp, /* key released*/
|
||||
autoKeyMask = 1 << autoKey, /* key repeatedly held down*/
|
||||
updateMask = 1 << updateEvt, /* window needs updating*/
|
||||
diskMask = 1 << diskEvt, /* disk inserted*/
|
||||
activMask = 1 << activateEvt, /* activate/deactivate window*/
|
||||
highLevelEventMask = 0x0400, /* high-level events (includes AppleEvents)*/
|
||||
osMask = 1 << osEvt, /* operating system events (suspend, resume)*/
|
||||
everyEvent = 0xFFFF /* all of the above*/
|
||||
};
|
||||
|
||||
enum {
|
||||
charCodeMask = 0x000000FF,
|
||||
keyCodeMask = 0x0000FF00,
|
||||
adbAddrMask = 0x00FF0000,
|
||||
osEvtMessageMask = (UInt32)0xFF000000
|
||||
};
|
||||
|
||||
enum {
|
||||
/* OS event messages. Event (sub)code is in the high byte of the message
|
||||
field.*/
|
||||
mouseMovedMessage = 0x00FA,
|
||||
suspendResumeMessage = 0x0001
|
||||
};
|
||||
|
||||
enum {
|
||||
resumeFlag = 1 /* Bit 0 of message indicates resume vs suspend*/
|
||||
};
|
||||
|
||||
#if CALL_NOT_IN_CARBON
|
||||
/* convertClipboardFlag is not ever set under Carbon. This is because scrap
|
||||
* conversion is */
|
||||
/* not tied to suspend/resume events any longer. Your application should
|
||||
* instead use the */
|
||||
/* scrap promise mechanism and fulfill scrap requests only when your promise
|
||||
* keeper proc */
|
||||
/* is called. If you need to know if the scrap has changed, you can cache the
|
||||
* last */
|
||||
/* ScrapRef you received and compare it with the current ScrapRef */
|
||||
enum {
|
||||
convertClipboardFlag =
|
||||
2 /* Bit 1 in resume message indicates clipboard change*/
|
||||
};
|
||||
|
||||
#endif /* CALL_NOT_IN_CARBON */
|
||||
|
||||
/*
|
||||
CARBON ALERT! BATTLESTATIONS!
|
||||
|
||||
The EventModifiers bits defined here are also used in the newer Carbon Event
|
||||
key modifiers parameters. There are two main differences:
|
||||
|
||||
1) The Carbon key modifiers parameter is a UInt32, not a UInt16. Never try
|
||||
to extract the key modifiers parameter from a Carbon Event into an
|
||||
EventModifiers type. You will probably get your stack trashed. 2) The Carbon
|
||||
key modifiers is just that: key modifiers. That parameter will never contain
|
||||
the button state bit.
|
||||
*/
|
||||
typedef UInt16 EventModifiers;
|
||||
enum {
|
||||
/* modifiers */
|
||||
activeFlagBit = 0, /* activate? (activateEvt and mouseDown)*/
|
||||
btnStateBit = 7, /* state of button?*/
|
||||
cmdKeyBit = 8, /* command key down?*/
|
||||
shiftKeyBit = 9, /* shift key down?*/
|
||||
alphaLockBit = 10, /* alpha lock down?*/
|
||||
optionKeyBit = 11, /* option key down?*/
|
||||
controlKeyBit = 12, /* control key down?*/
|
||||
rightShiftKeyBit = 13, /* right shift key down? Not supported on Mac OS X.*/
|
||||
rightOptionKeyBit = 14, /* right Option key down? Not supported on Mac OS X.*/
|
||||
rightControlKeyBit =
|
||||
15 /* right Control key down? Not supported on Mac OS X.*/
|
||||
};
|
||||
|
||||
enum {
|
||||
activeFlag = 1 << activeFlagBit,
|
||||
btnState = 1 << btnStateBit,
|
||||
cmdKey = 1 << cmdKeyBit,
|
||||
shiftKey = 1 << shiftKeyBit,
|
||||
alphaLock = 1 << alphaLockBit,
|
||||
optionKey = 1 << optionKeyBit,
|
||||
controlKey = 1 << controlKeyBit,
|
||||
rightShiftKey = 1 << rightShiftKeyBit, /* Not supported on Mac OS X.*/
|
||||
rightOptionKey = 1 << rightOptionKeyBit, /* Not supported on Mac OS X.*/
|
||||
rightControlKey = 1 << rightControlKeyBit /* Not supported on Mac OS X.*/
|
||||
};
|
||||
|
||||
/* MacRoman character codes*/
|
||||
enum {
|
||||
kNullCharCode = 0,
|
||||
kHomeCharCode = 1,
|
||||
kEnterCharCode = 3,
|
||||
kEndCharCode = 4,
|
||||
kHelpCharCode = 5,
|
||||
kBellCharCode = 7,
|
||||
kBackspaceCharCode = 8,
|
||||
kTabCharCode = 9,
|
||||
kLineFeedCharCode = 10,
|
||||
kVerticalTabCharCode = 11,
|
||||
kPageUpCharCode = 11,
|
||||
kFormFeedCharCode = 12,
|
||||
kPageDownCharCode = 12,
|
||||
kReturnCharCode = 13,
|
||||
kFunctionKeyCharCode = 16,
|
||||
kCommandCharCode = 17, /* glyph available only in system fonts*/
|
||||
kCheckCharCode = 18, /* glyph available only in system fonts*/
|
||||
kDiamondCharCode = 19, /* glyph available only in system fonts*/
|
||||
kAppleLogoCharCode = 20, /* glyph available only in system fonts*/
|
||||
kEscapeCharCode = 27,
|
||||
kClearCharCode = 27,
|
||||
kLeftArrowCharCode = 28,
|
||||
kRightArrowCharCode = 29,
|
||||
kUpArrowCharCode = 30,
|
||||
kDownArrowCharCode = 31,
|
||||
kSpaceCharCode = 32,
|
||||
kDeleteCharCode = 127,
|
||||
kBulletCharCode = 165,
|
||||
kNonBreakingSpaceCharCode = 202
|
||||
};
|
||||
|
||||
/* useful Unicode code points*/
|
||||
enum {
|
||||
kShiftUnicode = 0x21E7, /* Unicode UPWARDS WHITE ARROW*/
|
||||
kControlUnicode = 0x2303, /* Unicode UP ARROWHEAD*/
|
||||
kOptionUnicode = 0x2325, /* Unicode OPTION KEY*/
|
||||
kCommandUnicode = 0x2318, /* Unicode PLACE OF INTEREST SIGN*/
|
||||
kPencilUnicode = 0x270E, /* Unicode LOWER RIGHT PENCIL; actually pointed left
|
||||
until Mac OS X 10.3*/
|
||||
kPencilLeftUnicode = 0xF802, /* Unicode LOWER LEFT PENCIL; available in Mac OS
|
||||
X 10.3 and later*/
|
||||
kCheckUnicode = 0x2713, /* Unicode CHECK MARK*/
|
||||
kDiamondUnicode = 0x25C6, /* Unicode BLACK DIAMOND*/
|
||||
kBulletUnicode = 0x2022, /* Unicode BULLET*/
|
||||
kAppleLogoUnicode = 0xF8FF /* Unicode APPLE LOGO*/
|
||||
};
|
||||
|
||||
/*
|
||||
* Summary:
|
||||
* Virtual keycodes
|
||||
*
|
||||
* Discussion:
|
||||
* These constants are the virtual keycodes defined originally in
|
||||
* Inside Mac Volume V, pg. V-191. They identify physical keys on a
|
||||
* keyboard. Those constants with "ANSI" in the name are labeled
|
||||
* according to the key position on an ANSI-standard US keyboard.
|
||||
* For example, kVK_ANSI_A indicates the virtual keycode for the key
|
||||
* with the letter 'A' in the US keyboard layout. Other keyboard
|
||||
* layouts may have the 'A' key label on a different physical key;
|
||||
* in this case, pressing 'A' will generate a different virtual
|
||||
* keycode.
|
||||
*/
|
||||
enum {
|
||||
kVK_ANSI_A = 0x00,
|
||||
kVK_ANSI_S = 0x01,
|
||||
kVK_ANSI_D = 0x02,
|
||||
kVK_ANSI_F = 0x03,
|
||||
kVK_ANSI_H = 0x04,
|
||||
kVK_ANSI_G = 0x05,
|
||||
kVK_ANSI_Z = 0x06,
|
||||
kVK_ANSI_X = 0x07,
|
||||
kVK_ANSI_C = 0x08,
|
||||
kVK_ANSI_V = 0x09,
|
||||
kVK_ANSI_B = 0x0B,
|
||||
kVK_ANSI_Q = 0x0C,
|
||||
kVK_ANSI_W = 0x0D,
|
||||
kVK_ANSI_E = 0x0E,
|
||||
kVK_ANSI_R = 0x0F,
|
||||
kVK_ANSI_Y = 0x10,
|
||||
kVK_ANSI_T = 0x11,
|
||||
kVK_ANSI_1 = 0x12,
|
||||
kVK_ANSI_2 = 0x13,
|
||||
kVK_ANSI_3 = 0x14,
|
||||
kVK_ANSI_4 = 0x15,
|
||||
kVK_ANSI_6 = 0x16,
|
||||
kVK_ANSI_5 = 0x17,
|
||||
kVK_ANSI_Equal = 0x18,
|
||||
kVK_ANSI_9 = 0x19,
|
||||
kVK_ANSI_7 = 0x1A,
|
||||
kVK_ANSI_Minus = 0x1B,
|
||||
kVK_ANSI_8 = 0x1C,
|
||||
kVK_ANSI_0 = 0x1D,
|
||||
kVK_ANSI_RightBracket = 0x1E,
|
||||
kVK_ANSI_O = 0x1F,
|
||||
kVK_ANSI_U = 0x20,
|
||||
kVK_ANSI_LeftBracket = 0x21,
|
||||
kVK_ANSI_I = 0x22,
|
||||
kVK_ANSI_P = 0x23,
|
||||
kVK_ANSI_L = 0x25,
|
||||
kVK_ANSI_J = 0x26,
|
||||
kVK_ANSI_Quote = 0x27,
|
||||
kVK_ANSI_K = 0x28,
|
||||
kVK_ANSI_Semicolon = 0x29,
|
||||
kVK_ANSI_Backslash = 0x2A,
|
||||
kVK_ANSI_Comma = 0x2B,
|
||||
kVK_ANSI_Slash = 0x2C,
|
||||
kVK_ANSI_N = 0x2D,
|
||||
kVK_ANSI_M = 0x2E,
|
||||
kVK_ANSI_Period = 0x2F,
|
||||
kVK_ANSI_Grave = 0x32,
|
||||
kVK_ANSI_KeypadDecimal = 0x41,
|
||||
kVK_ANSI_KeypadMultiply = 0x43,
|
||||
kVK_ANSI_KeypadPlus = 0x45,
|
||||
kVK_ANSI_KeypadClear = 0x47,
|
||||
kVK_ANSI_KeypadDivide = 0x4B,
|
||||
kVK_ANSI_KeypadEnter = 0x4C,
|
||||
kVK_ANSI_KeypadMinus = 0x4E,
|
||||
kVK_ANSI_KeypadEquals = 0x51,
|
||||
kVK_ANSI_Keypad0 = 0x52,
|
||||
kVK_ANSI_Keypad1 = 0x53,
|
||||
kVK_ANSI_Keypad2 = 0x54,
|
||||
kVK_ANSI_Keypad3 = 0x55,
|
||||
kVK_ANSI_Keypad4 = 0x56,
|
||||
kVK_ANSI_Keypad5 = 0x57,
|
||||
kVK_ANSI_Keypad6 = 0x58,
|
||||
kVK_ANSI_Keypad7 = 0x59,
|
||||
kVK_ANSI_Keypad8 = 0x5B,
|
||||
kVK_ANSI_Keypad9 = 0x5C
|
||||
};
|
||||
|
||||
/* keycodes for keys that are independent of keyboard layout*/
|
||||
enum {
|
||||
kVK_Return = 0x24,
|
||||
kVK_Tab = 0x30,
|
||||
kVK_Space = 0x31,
|
||||
kVK_Delete = 0x33,
|
||||
kVK_Escape = 0x35,
|
||||
kVK_Command = 0x37,
|
||||
kVK_Shift = 0x38,
|
||||
kVK_CapsLock = 0x39,
|
||||
kVK_Option = 0x3A,
|
||||
kVK_Control = 0x3B,
|
||||
kVK_RightCommand = 0x36,
|
||||
kVK_RightShift = 0x3C,
|
||||
kVK_RightOption = 0x3D,
|
||||
kVK_RightControl = 0x3E,
|
||||
kVK_Function = 0x3F,
|
||||
kVK_F17 = 0x40,
|
||||
kVK_VolumeUp = 0x48,
|
||||
kVK_VolumeDown = 0x49,
|
||||
kVK_Mute = 0x4A,
|
||||
kVK_F18 = 0x4F,
|
||||
kVK_F19 = 0x50,
|
||||
kVK_F20 = 0x5A,
|
||||
kVK_F5 = 0x60,
|
||||
kVK_F6 = 0x61,
|
||||
kVK_F7 = 0x62,
|
||||
kVK_F3 = 0x63,
|
||||
kVK_F8 = 0x64,
|
||||
kVK_F9 = 0x65,
|
||||
kVK_F11 = 0x67,
|
||||
kVK_F13 = 0x69,
|
||||
kVK_F16 = 0x6A,
|
||||
kVK_F14 = 0x6B,
|
||||
kVK_F10 = 0x6D,
|
||||
kVK_ContextualMenu = 0x6E,
|
||||
kVK_F12 = 0x6F,
|
||||
kVK_F15 = 0x71,
|
||||
kVK_Help = 0x72,
|
||||
kVK_Home = 0x73,
|
||||
kVK_PageUp = 0x74,
|
||||
kVK_ForwardDelete = 0x75,
|
||||
kVK_F4 = 0x76,
|
||||
kVK_End = 0x77,
|
||||
kVK_F2 = 0x78,
|
||||
kVK_PageDown = 0x79,
|
||||
kVK_F1 = 0x7A,
|
||||
kVK_LeftArrow = 0x7B,
|
||||
kVK_RightArrow = 0x7C,
|
||||
kVK_DownArrow = 0x7D,
|
||||
kVK_UpArrow = 0x7E
|
||||
};
|
||||
|
||||
/* ISO keyboards only*/
|
||||
enum { kVK_ISO_Section = 0x0A };
|
||||
|
||||
/* JIS keyboards only*/
|
||||
enum {
|
||||
kVK_JIS_Yen = 0x5D,
|
||||
kVK_JIS_Underscore = 0x5E,
|
||||
kVK_JIS_KeypadComma = 0x5F,
|
||||
kVK_JIS_Eisu = 0x66,
|
||||
kVK_JIS_Kana = 0x68
|
||||
};
|
||||
|
||||
struct EventRecord {
|
||||
EventKind what;
|
||||
unsigned long message;
|
||||
UInt32 when;
|
||||
Point where;
|
||||
EventModifiers modifiers;
|
||||
};
|
||||
typedef struct EventRecord EventRecord;
|
||||
typedef CALLBACK_API(void, FKEYProcPtr)(void);
|
||||
typedef STACK_UPP_TYPE(FKEYProcPtr) FKEYUPP;
|
||||
/*
|
||||
* NewFKEYUPP()
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: not available
|
||||
* CarbonLib: not available
|
||||
* Non-Carbon CFM: available as macro/inline
|
||||
*/
|
||||
|
||||
/*
|
||||
* DisposeFKEYUPP()
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: not available
|
||||
* CarbonLib: not available
|
||||
* Non-Carbon CFM: available as macro/inline
|
||||
*/
|
||||
|
||||
/*
|
||||
* InvokeFKEYUPP()
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: not available
|
||||
* CarbonLib: not available
|
||||
* Non-Carbon CFM: available as macro/inline
|
||||
*/
|
||||
|
||||
#if !__LP64__
|
||||
/*
|
||||
* GetMouse() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use HIGetMousePosition instead.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] but deprecated in 10.5 CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern void GetMouse(Point *mouseLoc)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5;
|
||||
|
||||
#endif /* !__LP64__ */
|
||||
|
||||
/*
|
||||
* Button() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use GetCurrentButtonState or GetCurrentEventButtonState instead.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework but
|
||||
* deprecated in 10.6 CarbonLib: in CarbonLib 1.0 and later Non-Carbon
|
||||
* CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern Boolean Button(void)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_6;
|
||||
|
||||
#if !__LP64__
|
||||
/*
|
||||
* StillDown()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] CarbonLib: in CarbonLib 1.0 and later Non-Carbon CFM: in
|
||||
* InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern Boolean StillDown(void) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
/*
|
||||
* WaitMouseUp()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] CarbonLib: in CarbonLib 1.0 and later Non-Carbon CFM: in
|
||||
* InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern Boolean WaitMouseUp(void) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
/*
|
||||
* KeyTranslate() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use UCKeyTranslate instead.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] but deprecated in 10.6 CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern UInt32 KeyTranslate(const void *transData, UInt16 keycode, UInt32 *state)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_6;
|
||||
|
||||
/*
|
||||
* GetCaretTime()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] CarbonLib: in CarbonLib 1.0 and later Non-Carbon CFM: in
|
||||
* InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern UInt32 GetCaretTime(void) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
#endif /* !__LP64__ */
|
||||
|
||||
/*
|
||||
QuickTime 3.0 supports GetKeys() on unix and win32
|
||||
But, on little endian machines you will have to be
|
||||
careful about bit numberings and/or use a KeyMapByteArray
|
||||
instead.
|
||||
*/
|
||||
#if TARGET_API_MAC_OS8
|
||||
|
||||
typedef UInt32 KeyMap[4];
|
||||
#else
|
||||
typedef BigEndianUInt32 KeyMap[4];
|
||||
#endif /* TARGET_API_MAC_OS8 */
|
||||
|
||||
typedef UInt8 KeyMapByteArray[16];
|
||||
/*
|
||||
* GetKeys()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework
|
||||
* CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern void GetKeys(KeyMap theKeys) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
/* Obsolete event types & masks */
|
||||
enum {
|
||||
networkEvt = 10,
|
||||
driverEvt = 11,
|
||||
app1Evt = 12,
|
||||
app2Evt = 13,
|
||||
app3Evt = 14,
|
||||
app4Evt = 15,
|
||||
networkMask = 0x0400,
|
||||
driverMask = 0x0800,
|
||||
app1Mask = 0x1000,
|
||||
app2Mask = 0x2000,
|
||||
app3Mask = 0x4000,
|
||||
app4Mask = 0x8000
|
||||
};
|
||||
|
||||
struct EvQEl {
|
||||
QElemPtr qLink;
|
||||
SInt16 qType;
|
||||
EventKind
|
||||
evtQWhat; /* this part is identical to the EventRecord as defined above */
|
||||
unsigned long evtQMessage;
|
||||
UInt32 evtQWhen;
|
||||
Point evtQWhere;
|
||||
EventModifiers evtQModifiers;
|
||||
};
|
||||
typedef struct EvQEl EvQEl;
|
||||
typedef EvQEl *EvQElPtr;
|
||||
typedef CALLBACK_API(void, GetNextEventFilterProcPtr)(EventRecord *theEvent,
|
||||
Boolean *result);
|
||||
typedef STACK_UPP_TYPE(GetNextEventFilterProcPtr) GetNextEventFilterUPP;
|
||||
/*
|
||||
* NewGetNextEventFilterUPP()
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: not available
|
||||
* CarbonLib: not available
|
||||
* Non-Carbon CFM: available as macro/inline
|
||||
*/
|
||||
|
||||
/*
|
||||
* DisposeGetNextEventFilterUPP()
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: not available
|
||||
* CarbonLib: not available
|
||||
* Non-Carbon CFM: available as macro/inline
|
||||
*/
|
||||
|
||||
/*
|
||||
* InvokeGetNextEventFilterUPP()
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: not available
|
||||
* CarbonLib: not available
|
||||
* Non-Carbon CFM: available as macro/inline
|
||||
*/
|
||||
|
||||
typedef GetNextEventFilterUPP GNEFilterUPP;
|
||||
#if !__LP64__
|
||||
/*
|
||||
* GetDblTime()
|
||||
*
|
||||
* Summary:
|
||||
* Returns the maximum time (in units of 1/60th of a second) allowed
|
||||
* between two consecutive mouse-down events in order for the second
|
||||
* click to be considered a double-click.
|
||||
*
|
||||
* Discussion:
|
||||
* In 64-bit applications, you may replace calls to this API with
|
||||
* calls to NXClickTime (declared in
|
||||
* <IOKit/hidsystem/event_status_driver.h>) or with +[NSEvent
|
||||
* doubleClickInterval] (available in Mac OS X 10.6 and later).
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Result:
|
||||
* The maximum time between mouse-downs allowed for a double-click.
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] CarbonLib: in CarbonLib 1.0 and later Non-Carbon CFM: in
|
||||
* InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern UInt32 GetDblTime(void) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
/*
|
||||
* SetEventMask()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] CarbonLib: in CarbonLib 1.0 and later Non-Carbon CFM: in
|
||||
* InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern void SetEventMask(EventMask value)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
/*
|
||||
* GetNextEvent() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use ReceiveNextEvent instead.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] but deprecated in 10.6 CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern Boolean GetNextEvent(EventMask eventMask, EventRecord *theEvent)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_6;
|
||||
|
||||
/*
|
||||
* WaitNextEvent() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use ReceiveNextEvent instead.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] but deprecated in 10.6 CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern Boolean WaitNextEvent(EventMask eventMask, EventRecord *theEvent,
|
||||
UInt32 sleep, RgnHandle mouseRgn) /* can be NULL */
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_6;
|
||||
|
||||
/*
|
||||
* EventAvail() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use FindSpecificEventInQueue instead.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] but deprecated in 10.6 CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern Boolean EventAvail(EventMask eventMask, EventRecord *theEvent)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_6;
|
||||
|
||||
/*
|
||||
* PostEvent() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use PostEventToQueue or CGEventPost instead.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] but deprecated in 10.6 CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern OSErr PostEvent(EventKind eventNum, UInt32 eventMsg)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_6;
|
||||
|
||||
#endif /* !__LP64__ */
|
||||
|
||||
/*
|
||||
* FlushEvents() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use FlushEventsMatchingListFromQueue,
|
||||
* FlushSpecificEventsFromQueue, or FlushEventQueue instead.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework but
|
||||
* deprecated in 10.6 CarbonLib: in CarbonLib 1.0 and later Non-Carbon
|
||||
* CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern void FlushEvents(EventMask whichMask, EventMask stopMask)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_6;
|
||||
|
||||
#if OLDROUTINENAMES
|
||||
#define KeyTrans(transData, keycode, state) \
|
||||
KeyTranslate(transData, keycode, state)
|
||||
#endif /* OLDROUTINENAMES */
|
||||
|
||||
#if !__LP64__
|
||||
/*
|
||||
* KeyScript() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use TISSelectInputSource API for positive verbs (ScriptCode).
|
||||
* Use TSMDocument properties to restrict input sources:
|
||||
* kTSMDocumentEnabledInputSourcesPropertyTag
|
||||
* kTSMDocumentInputSourceOverridePropertyTag
|
||||
*
|
||||
* Summary:
|
||||
* Switch to the specified script's default (last used) input source.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] but deprecated in 10.5 CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern void KeyScript(short code)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5;
|
||||
|
||||
#endif /* !__LP64__ */
|
||||
|
||||
/*
|
||||
* IsCmdChar() *** DEPRECATED ***
|
||||
*
|
||||
* Deprecated:
|
||||
* Use IsUserCancelEventRef or CheckEventQueueForUserCancel instead.
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework but
|
||||
* deprecated in 10.6 CarbonLib: in CarbonLib 1.0 and later Non-Carbon
|
||||
* CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern Boolean IsCmdChar(const EventRecord *event, short test)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_6;
|
||||
|
||||
/*
|
||||
LowMem accessor functions previously in LowMem.h
|
||||
*/
|
||||
/*
|
||||
* LMGetKeyThresh()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework
|
||||
* CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern SInt16 LMGetKeyThresh(void) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
#if !__LP64__
|
||||
/*
|
||||
* LMSetKeyThresh()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] CarbonLib: in CarbonLib 1.0 and later Non-Carbon CFM: in
|
||||
* InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern void LMSetKeyThresh(SInt16 value)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
#endif /* !__LP64__ */
|
||||
|
||||
/*
|
||||
* LMGetKeyRepThresh()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework
|
||||
* CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern SInt16 LMGetKeyRepThresh(void) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
#if !__LP64__
|
||||
/*
|
||||
* LMSetKeyRepThresh()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] CarbonLib: in CarbonLib 1.0 and later Non-Carbon CFM: in
|
||||
* InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern void LMSetKeyRepThresh(SInt16 value)
|
||||
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
#endif /* !__LP64__ */
|
||||
|
||||
/*
|
||||
* LMGetKbdLast()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework
|
||||
* CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern UInt8 LMGetKbdLast(void) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
#if !__LP64__
|
||||
/*
|
||||
* LMSetKbdLast()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] CarbonLib: in CarbonLib 1.0 and later Non-Carbon CFM: in
|
||||
* InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern void LMSetKbdLast(UInt8 value) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
#endif /* !__LP64__ */
|
||||
|
||||
/*
|
||||
* LMGetKbdType()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework
|
||||
* CarbonLib: in CarbonLib 1.0 and later
|
||||
* Non-Carbon CFM: in InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern UInt8 LMGetKbdType(void) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
#if !__LP64__
|
||||
/*
|
||||
* LMSetKbdType()
|
||||
*
|
||||
* Mac OS X threading:
|
||||
* Not thread safe
|
||||
*
|
||||
* Availability:
|
||||
* Mac OS X: in version 10.0 and later in Carbon.framework [32-bit
|
||||
* only] CarbonLib: in CarbonLib 1.0 and later Non-Carbon CFM: in
|
||||
* InterfaceLib 7.1 and later
|
||||
*/
|
||||
extern void LMSetKbdType(UInt8 value) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
|
||||
|
||||
#endif /* !__LP64__ */
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __EVENTS__ */
|
||||
206
src/device_controller/windows_keycode.h
Normal file
@@ -0,0 +1,206 @@
|
||||
// virtual_key_codes.h
|
||||
#ifndef VIRTUAL_KEY_CODES_H
|
||||
#define VIRTUAL_KEY_CODES_H
|
||||
|
||||
#define VK_LBUTTON 0x01 // Left mouse button
|
||||
#define VK_RBUTTON 0x02 // Right mouse button
|
||||
#define VK_CANCEL 0x03 // Control-break processing
|
||||
#define VK_MBUTTON 0x04 // Middle mouse button
|
||||
#define VK_XBUTTON1 0x05 // X1 mouse button
|
||||
#define VK_XBUTTON2 0x06 // X2 mouse button
|
||||
// 0x07 Reserved
|
||||
#define VK_BACK 0x08 // Backspace key
|
||||
#define VK_TAB 0x09 // Tab key
|
||||
// 0x0A-0B Reserved
|
||||
#define VK_CLEAR 0x0C // Clear key
|
||||
#define VK_RETURN 0x0D // Enter key
|
||||
// 0x0E-0F Unassigned
|
||||
#define VK_SHIFT 0x10 // Shift key
|
||||
#define VK_CONTROL 0x11 // Ctrl key
|
||||
#define VK_MENU 0x12 // Alt key
|
||||
#define VK_PAUSE 0x13 // Pause key
|
||||
#define VK_CAPITAL 0x14 // Caps lock key
|
||||
#define VK_KANA 0x15 // IME Kana mode
|
||||
#define VK_HANGUL 0x15 // IME Hangul mode
|
||||
#define VK_IME_ON 0x16 // IME On
|
||||
#define VK_JUNJA 0x17 // IME Junja mode
|
||||
#define VK_FINAL 0x18 // IME final mode
|
||||
#define VK_HANJA 0x19 // IME Hanja mode
|
||||
#define VK_KANJI 0x19 // IME Kanji mode
|
||||
#define VK_IME_OFF 0x1A // IME Off
|
||||
#define VK_ESCAPE 0x1B // Esc key
|
||||
#define VK_CONVERT 0x1C // IME convert
|
||||
#define VK_NONCONVERT 0x1D // IME nonconvert
|
||||
#define VK_ACCEPT 0x1E // IME accept
|
||||
#define VK_MODECHANGE 0x1F // IME mode change request
|
||||
#define VK_SPACE 0x20 // Spacebar key
|
||||
#define VK_PRIOR 0x21 // Page up key
|
||||
#define VK_NEXT 0x22 // Page down key
|
||||
#define VK_END 0x23 // End key
|
||||
#define VK_HOME 0x24 // Home key
|
||||
#define VK_LEFT 0x25 // Left arrow key
|
||||
#define VK_UP 0x26 // Up arrow key
|
||||
#define VK_RIGHT 0x27 // Right arrow key
|
||||
#define VK_DOWN 0x28 // Down arrow key
|
||||
#define VK_SELECT 0x29 // Select key
|
||||
#define VK_PRINT 0x2A // Print key
|
||||
#define VK_EXECUTE 0x2B // Execute key
|
||||
#define VK_SNAPSHOT 0x2C // Print screen key
|
||||
#define VK_INSERT 0x2D // Insert key
|
||||
#define VK_DELETE 0x2E // Delete key
|
||||
#define VK_HELP 0x2F // Help key
|
||||
|
||||
#define VK_0 0x30 // 0 key
|
||||
#define VK_1 0x31 // 1 key
|
||||
#define VK_2 0x32 // 2 key
|
||||
#define VK_3 0x33 // 3 key
|
||||
#define VK_4 0x34 // 4 key
|
||||
#define VK_5 0x35 // 5 key
|
||||
#define VK_6 0x36 // 6 key
|
||||
#define VK_7 0x37 // 7 key
|
||||
#define VK_8 0x38 // 8 key
|
||||
#define VK_9 0x39 // 9 key
|
||||
// 0x3A-40 Undefined
|
||||
|
||||
#define VK_A 0x41 // A key
|
||||
#define VK_B 0x42 // B key
|
||||
#define VK_C 0x43 // C key
|
||||
#define VK_D 0x44 // D key
|
||||
#define VK_E 0x45 // E key
|
||||
#define VK_F 0x46 // F key
|
||||
#define VK_G 0x47 // G key
|
||||
#define VK_H 0x48 // H key
|
||||
#define VK_I 0x49 // I key
|
||||
#define VK_J 0x4A // J key
|
||||
#define VK_K 0x4B // K key
|
||||
#define VK_L 0x4C // L key
|
||||
#define VK_M 0x4D // M key
|
||||
#define VK_N 0x4E // N key
|
||||
#define VK_O 0x4F // O key
|
||||
#define VK_P 0x50 // P key
|
||||
#define VK_Q 0x51 // Q key
|
||||
#define VK_R 0x52 // R key
|
||||
#define VK_S 0x53 // S key
|
||||
#define VK_T 0x54 // T key
|
||||
#define VK_U 0x55 // U key
|
||||
#define VK_V 0x56 // V key
|
||||
#define VK_W 0x57 // W key
|
||||
#define VK_X 0x58 // X key
|
||||
#define VK_Y 0x59 // Y key
|
||||
#define VK_Z 0x5A // Z key
|
||||
|
||||
#define VK_LWIN 0x5B // Left Windows logo key
|
||||
#define VK_RWIN 0x5C // Right Windows logo key
|
||||
#define VK_APPS 0x5D // Application key
|
||||
// 0x5E Reserved
|
||||
#define VK_SLEEP 0x5F // Computer Sleep key
|
||||
|
||||
#define VK_NUMPAD0 0x60 // Numeric keypad 0 key
|
||||
#define VK_NUMPAD1 0x61 // Numeric keypad 1 key
|
||||
#define VK_NUMPAD2 0x62 // Numeric keypad 2 key
|
||||
#define VK_NUMPAD3 0x63 // Numeric keypad 3 key
|
||||
#define VK_NUMPAD4 0x64 // Numeric keypad 4 key
|
||||
#define VK_NUMPAD5 0x65 // Numeric keypad 5 key
|
||||
#define VK_NUMPAD6 0x66 // Numeric keypad 6 key
|
||||
#define VK_NUMPAD7 0x67 // Numeric keypad 7 key
|
||||
#define VK_NUMPAD8 0x68 // Numeric keypad 8 key
|
||||
#define VK_NUMPAD9 0x69 // Numeric keypad 9 key
|
||||
#define VK_MULTIPLY 0x6A // Multiply key
|
||||
#define VK_ADD 0x6B // Add key
|
||||
#define VK_SEPARATOR 0x6C // Separator key
|
||||
#define VK_SUBTRACT 0x6D // Subtract key
|
||||
#define VK_DECIMAL 0x6E // Decimal key
|
||||
#define VK_DIVIDE 0x6F // Divide key
|
||||
|
||||
#define VK_F1 0x70 // F1 key
|
||||
#define VK_F2 0x71 // F2 key
|
||||
#define VK_F3 0x72 // F3 key
|
||||
#define VK_F4 0x73 // F4 key
|
||||
#define VK_F5 0x74 // F5 key
|
||||
#define VK_F6 0x75 // F6 key
|
||||
#define VK_F7 0x76 // F7 key
|
||||
#define VK_F8 0x77 // F8 key
|
||||
#define VK_F9 0x78 // F9 key
|
||||
#define VK_F10 0x79 // F10 key
|
||||
#define VK_F11 0x7A // F11 key
|
||||
#define VK_F12 0x7B // F12 key
|
||||
#define VK_F13 0x7C // F13 key
|
||||
#define VK_F14 0x7D // F14 key
|
||||
#define VK_F15 0x7E // F15 key
|
||||
#define VK_F16 0x7F // F16 key
|
||||
#define VK_F17 0x80 // F17 key
|
||||
#define VK_F18 0x81 // F18 key
|
||||
#define VK_F19 0x82 // F19 key
|
||||
#define VK_F20 0x83 // F20 key
|
||||
#define VK_F21 0x84 // F21 key
|
||||
#define VK_F22 0x85 // F22 key
|
||||
#define VK_F23 0x86 // F23 key
|
||||
#define VK_F24 0x87 // F24 key
|
||||
// 0x88–0x8F Reserved
|
||||
|
||||
#define VK_NUMLOCK 0x90 // Num lock key
|
||||
#define VK_SCROLL 0x91 // Scroll lock key
|
||||
// 0x92–0x96 OEM specific
|
||||
// 0x97–0x9F Unassigned
|
||||
|
||||
#define VK_LSHIFT 0xA0 // Left Shift key
|
||||
#define VK_RSHIFT 0xA1 // Right Shift key
|
||||
#define VK_LCONTROL 0xA2 // Left Ctrl key
|
||||
#define VK_RCONTROL 0xA3 // Right Ctrl key
|
||||
#define VK_LMENU 0xA4 // Left Alt key
|
||||
#define VK_RMENU 0xA5 // Right Alt key
|
||||
|
||||
#define VK_BROWSER_BACK 0xA6 // Browser Back key
|
||||
#define VK_BROWSER_FORWARD 0xA7 // Browser Forward key
|
||||
#define VK_BROWSER_REFRESH 0xA8 // Browser Refresh key
|
||||
#define VK_BROWSER_STOP 0xA9 // Browser Stop key
|
||||
#define VK_BROWSER_SEARCH 0xAA // Browser Search key
|
||||
#define VK_BROWSER_FAVORITES 0xAB // Browser Favorites key
|
||||
#define VK_BROWSER_HOME 0xAC // Browser Start and Home key
|
||||
#define VK_VOLUME_MUTE 0xAD // Volume Mute key
|
||||
#define VK_VOLUME_DOWN 0xAE // Volume Down key
|
||||
#define VK_VOLUME_UP 0xAF // Volume Up key
|
||||
|
||||
#define VK_MEDIA_NEXT_TRACK 0xB0 // Next Track key
|
||||
#define VK_MEDIA_PREV_TRACK 0xB1 // Previous Track key
|
||||
#define VK_MEDIA_STOP 0xB2 // Stop Media key
|
||||
#define VK_MEDIA_PLAY_PAUSE 0xB3 // Play/Pause Media key
|
||||
#define VK_LAUNCH_MAIL 0xB4 // Start Mail key
|
||||
#define VK_LAUNCH_MEDIA_SELECT 0xB5 // Select Media key
|
||||
#define VK_LAUNCH_APP1 0xB6 // Start Application 1 key
|
||||
#define VK_LAUNCH_APP2 0xB7 // Start Application 2 key
|
||||
// 0xB8–0xB9 Reserved
|
||||
|
||||
#define VK_OEM_1 0xBA // For US: Semicolon/Colon key
|
||||
#define VK_OEM_PLUS 0xBB // Equals/Plus key
|
||||
#define VK_OEM_COMMA 0xBC // Comma/Less Than key
|
||||
#define VK_OEM_MINUS 0xBD // Dash/Underscore key
|
||||
#define VK_OEM_PERIOD 0xBE // Period/Greater Than key
|
||||
#define VK_OEM_2 0xBF // Slash/Question Mark key
|
||||
#define VK_OEM_3 0xC0 // Grave Accent/Tilde key
|
||||
// 0xC1–0xDA Reserved
|
||||
|
||||
#define VK_OEM_4 0xDB // Left Brace key
|
||||
#define VK_OEM_5 0xDC // Backslash/Pipe key
|
||||
#define VK_OEM_6 0xDD // Right Brace key
|
||||
#define VK_OEM_7 0xDE // Apostrophe/Quote key
|
||||
#define VK_OEM_8 0xDF // (Canadian CSA: Right Ctrl key)
|
||||
// 0xE0 Reserved
|
||||
#define VK_OEM_102 0xE2 // (European ISO: Backslash/Pipe key)
|
||||
// 0xE3–E4 OEM specific
|
||||
#define VK_PROCESSKEY 0xE5 // IME PROCESS key
|
||||
// 0xE6 OEM specific
|
||||
#define VK_PACKET 0xE7 // Unicode characters as keystrokes
|
||||
// 0xE8 Unassigned
|
||||
// 0xE9–F5 OEM specific
|
||||
#define VK_ATTN 0xF6 // Attn key
|
||||
#define VK_CRSEL 0xF7 // CrSel key
|
||||
#define VK_EXSEL 0xF8 // ExSel key
|
||||
#define VK_EREOF 0xF9 // Erase EOF key
|
||||
#define VK_PLAY 0xFA // Play key
|
||||
#define VK_ZOOM 0xFB // Zoom key
|
||||
#define VK_NONAME 0xFC // Reserved
|
||||
#define VK_PA1 0xFD // PA1 key
|
||||
#define VK_OEM_CLEAR 0xFE // Clear key
|
||||
|
||||
#endif // VIRTUAL_KEY_CODES_H
|
||||
2878
src/gui/assets/fonts/OPPOSans_Regular.h
Normal file
63
src/gui/assets/layouts/layout.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-06-14
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LAYOUT_STYLE_H_
|
||||
#define _LAYOUT_STYLE_H_
|
||||
|
||||
#define MENU_WINDOW_WIDTH_CN 300
|
||||
#define MENU_WINDOW_HEIGHT_CN 280
|
||||
#define LOCAL_WINDOW_WIDTH_CN 300
|
||||
#define LOCAL_WINDOW_HEIGHT_CN 280
|
||||
#define REMOTE_WINDOW_WIDTH_CN 300
|
||||
#define REMOTE_WINDOW_HEIGHT_CN 280
|
||||
#define MENU_WINDOW_WIDTH_EN 190
|
||||
#define MENU_WINDOW_HEIGHT_EN 245
|
||||
#define IPUT_WINDOW_WIDTH 160
|
||||
#define INPUT_WINDOW_PADDING_CN 66
|
||||
#define INPUT_WINDOW_PADDING_EN 96
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 202
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 248
|
||||
#if _WIN32
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 345
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 345
|
||||
#else
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 315
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 315
|
||||
#endif
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN 165
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN 165
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 120
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 120
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN 120
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 120
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 167
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_CN 171
|
||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_EN 218
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN 90
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137
|
||||
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN 90
|
||||
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN 137
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 73
|
||||
#define SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH 130
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 65
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 83
|
||||
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN 78
|
||||
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN 91
|
||||
|
||||
#endif
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
namespace localization {
|
||||
|
||||
static std::vector<std::string> local_desktop = {
|
||||
@@ -71,6 +75,9 @@ static std::vector<std::string> language_en = {
|
||||
reinterpret_cast<const char*>(u8"英文"), "English"};
|
||||
static std::vector<std::string> video_quality = {
|
||||
reinterpret_cast<const char*>(u8"视频质量:"), "Video Quality:"};
|
||||
static std::vector<std::string> video_frame_rate = {
|
||||
reinterpret_cast<const char*>(u8"画面采集帧率:"),
|
||||
"Video Capture Frame Rate:"};
|
||||
static std::vector<std::string> video_quality_high = {
|
||||
reinterpret_cast<const char*>(u8"高"), "High"};
|
||||
static std::vector<std::string> video_quality_medium = {
|
||||
@@ -88,7 +95,22 @@ static std::vector<std::string> enable_hardware_video_codec = {
|
||||
"Enable Hardware Video Codec:"};
|
||||
static std::vector<std::string> enable_turn = {
|
||||
reinterpret_cast<const char*>(u8"启用中继服务:"), "Enable TURN Service:"};
|
||||
|
||||
static std::vector<std::string> enable_srtp = {
|
||||
reinterpret_cast<const char*>(u8"启用SRTP:"), "Enable SRTP:"};
|
||||
static std::vector<std::string> self_hosted_server_config = {
|
||||
reinterpret_cast<const char*>(u8"自托管服务器配置"),
|
||||
"Self-Hosted Server Config"};
|
||||
static std::vector<std::string> self_hosted_server_settings = {
|
||||
reinterpret_cast<const char*>(u8"自托管服务器设置"),
|
||||
"Self-Hosted Server Settings"};
|
||||
static std::vector<std::string> self_hosted_server_address = {
|
||||
reinterpret_cast<const char*>(u8"服务器地址:"), "Server Address:"};
|
||||
static std::vector<std::string> self_hosted_server_port = {
|
||||
reinterpret_cast<const char*>(u8"服务器端口:"), "Server Port:"};
|
||||
static std::vector<std::string> self_hosted_server_certificate_path = {
|
||||
reinterpret_cast<const char*>(u8"证书文件路径:"), "Certificate File Path:"};
|
||||
static std::vector<std::string> select_a_file = {
|
||||
reinterpret_cast<const char*>(u8"请选择文件"), "Please select a file"};
|
||||
static std::vector<std::string> ok = {reinterpret_cast<const char*>(u8"确认"),
|
||||
"OK"};
|
||||
static std::vector<std::string> cancel = {
|
||||
@@ -137,6 +159,12 @@ static std::vector<std::string> version = {
|
||||
static std::vector<std::string> confirm_delete_connection = {
|
||||
reinterpret_cast<const char*>(u8"确认删除此连接"),
|
||||
"Confirm to delete this connection"};
|
||||
} // namespace localization
|
||||
#if _WIN32
|
||||
|
||||
static std::vector<std::string> minimize_to_tray = {
|
||||
reinterpret_cast<const char*>(u8"退出时最小化到系统托盘:"),
|
||||
"Minimize to system tray when exit:"};
|
||||
static std::vector<LPCWSTR> exit_program = {L"退出", L"Exit"};
|
||||
#endif
|
||||
} // namespace localization
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <random>
|
||||
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -74,7 +74,7 @@ int Render::LocalWindow() {
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(ICON_FA_COPY, ImVec2(35, 38))) {
|
||||
if (ImGui::Button(ICON_FA_COPY, ImVec2(22, 38))) {
|
||||
local_id_copied_ = true;
|
||||
ImGui::SetClipboardText(client_id_);
|
||||
copy_start_time_ = ImGui::GetTime();
|
||||
@@ -84,7 +84,7 @@ int Render::LocalWindow() {
|
||||
|
||||
double time_duration = ImGui::GetTime() - copy_start_time_;
|
||||
if (local_id_copied_ && time_duration < 1.0f) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
notification_window_width_) /
|
||||
@@ -159,7 +159,7 @@ int Render::LocalWindow() {
|
||||
}
|
||||
|
||||
if (!show_password_) {
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 12.5f),
|
||||
ImVec2(l_x + 20.3f, l_y + 26.5f),
|
||||
IM_COL32(239, 240, 242, 255), 2.0f);
|
||||
@@ -170,19 +170,6 @@ int Render::LocalWindow() {
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(
|
||||
regenerate_password_ ? ICON_FA_SPINNER : ICON_FA_ARROWS_ROTATE,
|
||||
ImVec2(22, 38))) {
|
||||
regenerate_password_ = true;
|
||||
regenerate_password_start_time_ = ImGui::GetTime();
|
||||
LeaveConnection(peer_, client_id_);
|
||||
}
|
||||
if (ImGui::GetTime() - regenerate_password_start_time_ > 0.3f) {
|
||||
regenerate_password_ = false;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(ICON_FA_PEN, ImVec2(22, 38))) {
|
||||
show_reset_password_window_ = true;
|
||||
}
|
||||
@@ -190,7 +177,7 @@ int Render::LocalWindow() {
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (show_reset_password_window_) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
@@ -209,8 +209,8 @@ int Render::ShowRecentConnections() {
|
||||
ImVec2 line_end = ImVec2(
|
||||
image_screen_pos.x + recent_connection_image_width_ + 20.0f,
|
||||
image_screen_pos.y + recent_connection_image_height_ + button_height);
|
||||
ImGui::GetForegroundDrawList()->AddLine(line_start, line_end,
|
||||
IM_COL32(0, 0, 0, 122), 1.0f);
|
||||
ImGui::GetWindowDrawList()->AddLine(line_start, line_end,
|
||||
IM_COL32(0, 0, 0, 122), 1.0f);
|
||||
}
|
||||
|
||||
count++;
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -132,6 +132,7 @@ static int InputTextCallback(ImGuiInputTextCallbackData *data) {
|
||||
int Render::ConnectTo(const std::string &remote_id, const char *password,
|
||||
bool remember_password) {
|
||||
LOG_INFO("Connect to [{}]", remote_id);
|
||||
focused_remote_id_ = remote_id;
|
||||
|
||||
if (client_properties_.find(remote_id) == client_properties_.end()) {
|
||||
client_properties_[remote_id] =
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "render.h"
|
||||
|
||||
#include <libyuv.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -9,7 +11,7 @@
|
||||
#include "device_controller_factory.h"
|
||||
#include "fa_regular_400.h"
|
||||
#include "fa_solid_900.h"
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "platform.h"
|
||||
#include "rd_log.h"
|
||||
@@ -19,8 +21,6 @@
|
||||
|
||||
#define MOUSE_GRAB_PADDING 5
|
||||
|
||||
#define STREAM_FRASH (SDL_USEREVENT + 1)
|
||||
|
||||
std::vector<char> Render::SerializeRemoteAction(const RemoteAction& action) {
|
||||
std::vector<char> buffer;
|
||||
buffer.push_back(static_cast<char>(action.type));
|
||||
@@ -171,7 +171,8 @@ Render::~Render() {}
|
||||
|
||||
int Render::SaveSettingsIntoCacheFile() {
|
||||
cd_cache_mutex_.lock();
|
||||
std::ofstream cd_cache_file(cache_path_ + "/cache.cd", std::ios::binary);
|
||||
std::ofstream cd_cache_file(cache_path_ + "/secure_cache.enc",
|
||||
std::ios::binary);
|
||||
if (!cd_cache_file) {
|
||||
cd_cache_mutex_.unlock();
|
||||
return -1;
|
||||
@@ -181,15 +182,6 @@ int Render::SaveSettingsIntoCacheFile() {
|
||||
sizeof(cd_cache_.client_id_with_password));
|
||||
memcpy(cd_cache_.client_id_with_password, client_id_with_password_,
|
||||
sizeof(client_id_with_password_));
|
||||
memcpy(&cd_cache_.language, &language_button_value_,
|
||||
sizeof(language_button_value_));
|
||||
memcpy(&cd_cache_.video_quality, &video_quality_button_value_,
|
||||
sizeof(video_quality_button_value_));
|
||||
memcpy(&cd_cache_.video_encode_format, &video_encode_format_button_value_,
|
||||
sizeof(video_encode_format_button_value_));
|
||||
memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_,
|
||||
sizeof(enable_hardware_video_codec_));
|
||||
memcpy(&cd_cache_.enable_turn, &enable_turn_, sizeof(enable_turn_));
|
||||
memcpy(&cd_cache_.key, &aes128_key_, sizeof(aes128_key_));
|
||||
memcpy(&cd_cache_.iv, &aes128_iv_, sizeof(aes128_iv_));
|
||||
|
||||
@@ -197,42 +189,19 @@ int Render::SaveSettingsIntoCacheFile() {
|
||||
cd_cache_file.close();
|
||||
cd_cache_mutex_.unlock();
|
||||
|
||||
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
|
||||
config_center_.SetVideoQuality(
|
||||
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
|
||||
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
|
||||
config_center_.SetTurn(enable_turn_);
|
||||
|
||||
LOG_INFO("Save settings into cache file success");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::LoadSettingsFromCacheFile() {
|
||||
cd_cache_mutex_.lock();
|
||||
std::ifstream cd_cache_file(cache_path_ + "/cache.cd", std::ios::binary);
|
||||
std::ifstream cd_cache_file(cache_path_ + "/secure_cache.enc",
|
||||
std::ios::binary);
|
||||
if (!cd_cache_file) {
|
||||
cd_cache_mutex_.unlock();
|
||||
|
||||
LOG_INFO("Init cache file by using default settings");
|
||||
memset(password_saved_, 0, sizeof(password_saved_));
|
||||
memset(aes128_key_, 0, sizeof(aes128_key_));
|
||||
memset(aes128_iv_, 0, sizeof(aes128_iv_));
|
||||
language_button_value_ = 0;
|
||||
video_quality_button_value_ = 0;
|
||||
video_encode_format_button_value_ = 1;
|
||||
enable_hardware_video_codec_ = false;
|
||||
enable_turn_ = false;
|
||||
|
||||
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
|
||||
config_center_.SetVideoQuality(
|
||||
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
|
||||
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
|
||||
config_center_.SetTurn(enable_turn_);
|
||||
|
||||
thumbnail_.reset();
|
||||
thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/");
|
||||
@@ -279,25 +248,21 @@ int Render::LoadSettingsFromCacheFile() {
|
||||
thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/",
|
||||
aes128_key_, aes128_iv_);
|
||||
|
||||
language_button_value_ = cd_cache_.language;
|
||||
video_quality_button_value_ = cd_cache_.video_quality;
|
||||
video_encode_format_button_value_ = cd_cache_.video_encode_format;
|
||||
enable_hardware_video_codec_ = cd_cache_.enable_hardware_video_codec;
|
||||
enable_turn_ = cd_cache_.enable_turn;
|
||||
language_button_value_ = (int)config_center_->GetLanguage();
|
||||
video_quality_button_value_ = (int)config_center_->GetVideoQuality();
|
||||
video_frame_rate_button_value_ = (int)config_center_->GetVideoFrameRate();
|
||||
video_encode_format_button_value_ =
|
||||
(int)config_center_->GetVideoEncodeFormat();
|
||||
enable_hardware_video_codec_ = config_center_->IsHardwareVideoCodec();
|
||||
enable_turn_ = config_center_->IsEnableTurn();
|
||||
enable_srtp_ = config_center_->IsEnableSrtp();
|
||||
|
||||
language_button_value_last_ = language_button_value_;
|
||||
video_quality_button_value_last_ = video_quality_button_value_;
|
||||
video_encode_format_button_value_last_ = video_encode_format_button_value_;
|
||||
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
|
||||
enable_turn_last_ = enable_turn_;
|
||||
|
||||
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
|
||||
config_center_.SetVideoQuality(
|
||||
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
|
||||
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
|
||||
config_center_.SetTurn(enable_turn_);
|
||||
enable_srtp_last_ = enable_srtp_;
|
||||
|
||||
LOG_INFO("Load settings from cache file");
|
||||
|
||||
@@ -305,25 +270,28 @@ int Render::LoadSettingsFromCacheFile() {
|
||||
}
|
||||
|
||||
int Render::ScreenCapturerInit() {
|
||||
if (screen_capturer_) {
|
||||
LOG_INFO("Screen capturer already initialized");
|
||||
return 0;
|
||||
if (!screen_capturer_) {
|
||||
screen_capturer_ = (ScreenCapturer*)screen_capturer_factory_->Create();
|
||||
}
|
||||
|
||||
screen_capturer_ = (ScreenCapturer*)screen_capturer_factory_->Create();
|
||||
last_frame_time_ = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
int fps = config_center_->GetVideoFrameRate() ==
|
||||
ConfigCenter::VIDEO_FRAME_RATE::FPS_30
|
||||
? 30
|
||||
: 60;
|
||||
LOG_INFO("Init screen capturer with {} fps", fps);
|
||||
|
||||
int screen_capturer_init_ret = screen_capturer_->Init(
|
||||
60,
|
||||
[this](unsigned char* data, int size, int width, int height,
|
||||
const char* display_name) -> void {
|
||||
fps,
|
||||
[this, fps](unsigned char* data, int size, int width, int height,
|
||||
const char* display_name) -> void {
|
||||
auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
auto duration = now_time - last_frame_time_;
|
||||
if (duration >= 33) {
|
||||
if (duration * fps >= 1000) { // ~60 FPS
|
||||
XVideoFrame frame;
|
||||
frame.data = (const char*)data;
|
||||
frame.size = size;
|
||||
@@ -458,24 +426,53 @@ int Render::StopKeyboardCapturer() {
|
||||
|
||||
int Render::CreateConnectionPeer() {
|
||||
params_.use_cfg_file = false;
|
||||
params_.signal_server_ip = "api.crossdesk.cn";
|
||||
params_.signal_server_port = 9099;
|
||||
params_.stun_server_ip = "150.158.81.30";
|
||||
|
||||
std::string server_ip;
|
||||
int server_port;
|
||||
std::string server_cert_path;
|
||||
|
||||
if (config_center_->IsSelfHosted()) {
|
||||
server_ip = config_center_->GetServerHost();
|
||||
server_port = config_center_->GetServerPort();
|
||||
server_cert_path = config_center_->GetCertFilePath();
|
||||
} else {
|
||||
server_ip = config_center_->GetDefaultServerHost();
|
||||
server_port = config_center_->GetDefaultServerPort();
|
||||
server_cert_path = config_center_->GetDefaultCertFilePath();
|
||||
}
|
||||
|
||||
strncpy((char*)params_.signal_server_ip, server_ip.c_str(),
|
||||
sizeof(params_.signal_server_ip) - 1);
|
||||
params_.signal_server_ip[sizeof(params_.signal_server_ip) - 1] = '\0';
|
||||
params_.signal_server_port = server_port;
|
||||
strncpy((char*)params_.stun_server_ip, server_ip.c_str(),
|
||||
sizeof(params_.stun_server_ip) - 1);
|
||||
params_.stun_server_ip[sizeof(params_.stun_server_ip) - 1] = '\0';
|
||||
params_.stun_server_port = 3478;
|
||||
params_.turn_server_ip = "150.158.81.30";
|
||||
strncpy((char*)params_.turn_server_ip, server_ip.c_str(),
|
||||
sizeof(params_.turn_server_ip) - 1);
|
||||
params_.turn_server_ip[sizeof(params_.turn_server_ip) - 1] = '\0';
|
||||
params_.turn_server_port = 3478;
|
||||
params_.turn_server_username = "dijunkun";
|
||||
params_.turn_server_password = "dijunkunpw";
|
||||
params_.tls_cert_path = std::filesystem::exists(cert_path_)
|
||||
? cert_path_.c_str()
|
||||
: "certs/crossdesk.cn_root.crt";
|
||||
params_.log_path = dll_log_path_.c_str();
|
||||
params_.hardware_acceleration = config_center_.IsHardwareVideoCodec();
|
||||
params_.av1_encoding = config_center_.GetVideoEncodeFormat() ==
|
||||
strncpy((char*)params_.turn_server_username, "dijunkun",
|
||||
sizeof(params_.turn_server_username) - 1);
|
||||
params_.turn_server_username[sizeof(params_.turn_server_username) - 1] = '\0';
|
||||
strncpy((char*)params_.turn_server_password, "dijunkunpw",
|
||||
sizeof(params_.turn_server_password) - 1);
|
||||
params_.turn_server_password[sizeof(params_.turn_server_password) - 1] = '\0';
|
||||
strncpy(params_.tls_cert_path, server_cert_path.c_str(),
|
||||
sizeof(params_.tls_cert_path) - 1);
|
||||
params_.tls_cert_path[sizeof(params_.tls_cert_path) - 1] = '\0';
|
||||
|
||||
strncpy(params_.log_path, dll_log_path_.c_str(),
|
||||
sizeof(params_.log_path) - 1);
|
||||
params_.log_path[sizeof(params_.log_path) - 1] = '\0';
|
||||
params_.hardware_acceleration = config_center_->IsHardwareVideoCodec();
|
||||
params_.av1_encoding = config_center_->GetVideoEncodeFormat() ==
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1
|
||||
? true
|
||||
: false;
|
||||
params_.enable_turn = config_center_.IsEnableTurn();
|
||||
params_.enable_turn = config_center_->IsEnableTurn();
|
||||
params_.enable_srtp = config_center_->IsEnableSrtp();
|
||||
params_.on_receive_video_buffer = nullptr;
|
||||
params_.on_receive_audio_buffer = OnReceiveAudioBufferCb;
|
||||
params_.on_receive_data_buffer = OnReceiveDataBufferCb;
|
||||
@@ -512,49 +509,29 @@ int Render::CreateConnectionPeer() {
|
||||
}
|
||||
|
||||
int Render::AudioDeviceInit() {
|
||||
// Audio
|
||||
SDL_AudioSpec want_in = {};
|
||||
SDL_AudioSpec want_out = {};
|
||||
SDL_zero(want_in);
|
||||
want_in.freq = 48000;
|
||||
want_in.format = AUDIO_S16LSB;
|
||||
want_in.channels = 1;
|
||||
want_in.samples = 480;
|
||||
want_in.callback = SdlCaptureAudioIn;
|
||||
want_in.userdata = this;
|
||||
SDL_AudioSpec desired_out{};
|
||||
desired_out.freq = 48000;
|
||||
desired_out.format = SDL_AUDIO_S16;
|
||||
desired_out.channels = 1;
|
||||
|
||||
// input_dev_ = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0);
|
||||
// if (input_dev_ == 0) {
|
||||
// SDL_Log("Failed to open input: %s", SDL_GetError());
|
||||
// // return 1;
|
||||
// }
|
||||
|
||||
SDL_zero(want_out);
|
||||
want_out.freq = 48000;
|
||||
want_out.format = AUDIO_S16LSB;
|
||||
want_out.channels = 1;
|
||||
// want_out.silence = 0;
|
||||
want_out.samples = 480;
|
||||
want_out.callback = nullptr;
|
||||
want_out.userdata = this;
|
||||
|
||||
output_dev_ = SDL_OpenAudioDevice(nullptr, 0, &want_out, NULL, 0);
|
||||
if (output_dev_ == 0) {
|
||||
SDL_Log("Failed to open input: %s", SDL_GetError());
|
||||
// return 1;
|
||||
output_stream_ = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
|
||||
&desired_out, nullptr, nullptr);
|
||||
if (!output_stream_) {
|
||||
LOG_ERROR("Failed to open output stream: {}", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// SDL_PauseAudioDevice(input_dev_, 0);
|
||||
SDL_PauseAudioDevice(output_dev_, 0);
|
||||
SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(output_stream_));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::AudioDeviceDestroy() {
|
||||
SDL_PauseAudioDevice(output_dev_, 1);
|
||||
SDL_CloseAudioDevice(output_dev_);
|
||||
// SDL_CloseAudioDevice(input_dev_);
|
||||
|
||||
if (output_stream_) {
|
||||
SDL_CloseAudioDevice(SDL_GetAudioStreamDevice(output_stream_));
|
||||
SDL_DestroyAudioStream(output_stream_);
|
||||
output_stream_ = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -595,26 +572,33 @@ int Render::CreateMainWindow() {
|
||||
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
|
||||
SDL_WindowFlags window_flags =
|
||||
(SDL_WindowFlags)(SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_BORDERLESS |
|
||||
SDL_WINDOW_HIDDEN);
|
||||
main_window_ =
|
||||
SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED, (int)main_window_width_default_,
|
||||
(int)main_window_height_default_, window_flags);
|
||||
|
||||
main_renderer_ = SDL_CreateRenderer(
|
||||
main_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
||||
if (main_renderer_ == nullptr) {
|
||||
LOG_ERROR("Error creating SDL_Renderer");
|
||||
return 0;
|
||||
if (!SDL_CreateWindowAndRenderer(
|
||||
"Remote Desk", (int)main_window_width_default_,
|
||||
(int)main_window_height_default_,
|
||||
SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_BORDERLESS |
|
||||
SDL_WINDOW_HIDDEN,
|
||||
&main_window_, &main_renderer_)) {
|
||||
LOG_ERROR("Error creating main_window_ and main_renderer_: {}",
|
||||
SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_SetWindowResizable(main_window_, SDL_FALSE);
|
||||
SDL_SetWindowResizable(main_window_, false);
|
||||
|
||||
// for window region action
|
||||
SDL_SetWindowHitTest(main_window_, HitTestCallback, this);
|
||||
|
||||
#if _WIN32
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(main_window_);
|
||||
HWND main_hwnd = (HWND)SDL_GetPointerProperty(
|
||||
props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
|
||||
|
||||
HICON tray_icon = (HICON)LoadImageW(NULL, L"crossdesk.ico", IMAGE_ICON, 0, 0,
|
||||
LR_LOADFROMFILE | LR_DEFAULTSIZE);
|
||||
tray_ = std::make_unique<WinTray>(main_hwnd, tray_icon, L"CrossDesk",
|
||||
localization_language_index_);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -647,35 +631,27 @@ int Render::CreateStreamWindow() {
|
||||
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
|
||||
SDL_WindowFlags window_flags =
|
||||
(SDL_WindowFlags)(SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_BORDERLESS);
|
||||
stream_window_ =
|
||||
SDL_CreateWindow("Stream window", SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED, stream_window_width_default_,
|
||||
stream_window_height_default_, window_flags);
|
||||
|
||||
stream_renderer_ = SDL_CreateRenderer(
|
||||
stream_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
||||
if (stream_renderer_ == nullptr) {
|
||||
LOG_ERROR("Error creating SDL_Renderer");
|
||||
return 0;
|
||||
if (!SDL_CreateWindowAndRenderer(
|
||||
"Stream window", (int)stream_window_width_default_,
|
||||
(int)stream_window_height_default_,
|
||||
SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_BORDERLESS,
|
||||
&stream_window_, &stream_renderer_)) {
|
||||
LOG_ERROR("Error creating stream_window_ and stream_renderer_: {}",
|
||||
SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream_pixformat_ = SDL_PIXELFORMAT_NV12;
|
||||
// stream_texture_ = SDL_CreateTexture(stream_renderer_, stream_pixformat_,
|
||||
// SDL_TEXTUREACCESS_STREAMING,
|
||||
// texture_width_, texture_height_);
|
||||
|
||||
SDL_SetWindowResizable(stream_window_, SDL_TRUE);
|
||||
SDL_SetWindowResizable(stream_window_, true);
|
||||
|
||||
// for window region action
|
||||
SDL_SetWindowHitTest(stream_window_, HitTestCallback, this);
|
||||
|
||||
// change props->stream_render_rect_
|
||||
SDL_Event event;
|
||||
event.type = SDL_WINDOWEVENT;
|
||||
event.type = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED;
|
||||
event.window.windowID = SDL_GetWindowID(stream_window_);
|
||||
event.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
|
||||
SDL_PushEvent(&event);
|
||||
|
||||
stream_window_created_ = true;
|
||||
@@ -709,14 +685,7 @@ int Render::SetupFontAndStyle() {
|
||||
// Setup Dear ImGui style
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
imgui_cache_path_ = cache_path_ + "/crossdesk.ini";
|
||||
io.IniFilename = imgui_cache_path_.c_str();
|
||||
|
||||
io.ConfigFlags |=
|
||||
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
io.ConfigFlags |=
|
||||
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
||||
io.IniFilename = NULL; // disable imgui.ini
|
||||
|
||||
// Load Fonts
|
||||
ImFontConfig config;
|
||||
@@ -744,25 +713,25 @@ int Render::SetupMainWindow() {
|
||||
|
||||
SetupFontAndStyle();
|
||||
|
||||
SDL_GL_GetDrawableSize(main_window_, &main_window_width_real_,
|
||||
&main_window_height_real_);
|
||||
SDL_GetWindowSizeInPixels(main_window_, &main_window_width_real_,
|
||||
&main_window_height_real_);
|
||||
main_window_dpi_scaling_w_ = main_window_width_real_ / main_window_width_;
|
||||
main_window_dpi_scaling_h_ = main_window_width_real_ / main_window_width_;
|
||||
SDL_RenderSetScale(main_renderer_, main_window_dpi_scaling_w_,
|
||||
SDL_SetRenderScale(main_renderer_, main_window_dpi_scaling_w_,
|
||||
main_window_dpi_scaling_h_);
|
||||
LOG_INFO("Use dpi scaling [{}x{}] for main window",
|
||||
main_window_dpi_scaling_w_, main_window_dpi_scaling_h_);
|
||||
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(main_window_, main_renderer_);
|
||||
ImGui_ImplSDLRenderer2_Init(main_renderer_);
|
||||
ImGui_ImplSDL3_InitForSDLRenderer(main_window_, main_renderer_);
|
||||
ImGui_ImplSDLRenderer3_Init(main_renderer_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::DestroyMainWindowContext() {
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui_ImplSDLRenderer3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext(main_ctx_);
|
||||
|
||||
return 0;
|
||||
@@ -782,21 +751,21 @@ int Render::SetupStreamWindow() {
|
||||
|
||||
SetupFontAndStyle();
|
||||
|
||||
SDL_GL_GetDrawableSize(stream_window_, &stream_window_width_real_,
|
||||
&stream_window_height_real_);
|
||||
SDL_GetWindowSizeInPixels(stream_window_, &stream_window_width_real_,
|
||||
&stream_window_height_real_);
|
||||
|
||||
stream_window_dpi_scaling_w_ =
|
||||
stream_window_width_real_ / stream_window_width_;
|
||||
stream_window_dpi_scaling_h_ =
|
||||
stream_window_width_real_ / stream_window_width_;
|
||||
|
||||
SDL_RenderSetScale(stream_renderer_, stream_window_dpi_scaling_w_,
|
||||
SDL_SetRenderScale(stream_renderer_, stream_window_dpi_scaling_w_,
|
||||
stream_window_dpi_scaling_h_);
|
||||
LOG_INFO("Use dpi scaling [{}x{}] for stream window",
|
||||
stream_window_dpi_scaling_w_, stream_window_dpi_scaling_h_);
|
||||
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(stream_window_, stream_renderer_);
|
||||
ImGui_ImplSDLRenderer2_Init(stream_renderer_);
|
||||
ImGui_ImplSDL3_InitForSDLRenderer(stream_window_, stream_renderer_);
|
||||
ImGui_ImplSDLRenderer3_Init(stream_renderer_);
|
||||
|
||||
stream_window_inited_ = true;
|
||||
LOG_INFO("Stream window inited");
|
||||
@@ -807,8 +776,8 @@ int Render::SetupStreamWindow() {
|
||||
int Render::DestroyStreamWindowContext() {
|
||||
stream_window_inited_ = false;
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui_ImplSDLRenderer3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext(stream_ctx_);
|
||||
|
||||
return 0;
|
||||
@@ -821,8 +790,8 @@ int Render::DrawMainWindow() {
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
ImGui_ImplSDLRenderer2_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
@@ -845,7 +814,7 @@ int Render::DrawMainWindow() {
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
SDL_RenderClear(main_renderer_);
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), main_renderer_);
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), main_renderer_);
|
||||
SDL_RenderPresent(main_renderer_);
|
||||
|
||||
return 0;
|
||||
@@ -858,8 +827,8 @@ int Render::DrawStreamWindow() {
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
ImGui_ImplSDLRenderer2_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
StreamWindow();
|
||||
@@ -889,11 +858,16 @@ int Render::DrawStreamWindow() {
|
||||
for (auto& it : client_properties_) {
|
||||
auto props = it.second;
|
||||
if (props->tab_selected_) {
|
||||
SDL_RenderCopy(stream_renderer_, props->stream_texture_, NULL,
|
||||
&(props->stream_render_rect_));
|
||||
SDL_FRect render_rect_f = {
|
||||
static_cast<float>(props->stream_render_rect_.x),
|
||||
static_cast<float>(props->stream_render_rect_.y),
|
||||
static_cast<float>(props->stream_render_rect_.w),
|
||||
static_cast<float>(props->stream_render_rect_.h)};
|
||||
SDL_RenderTexture(stream_renderer_, props->stream_texture_, NULL,
|
||||
&render_rect_f);
|
||||
}
|
||||
}
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), stream_renderer_);
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), stream_renderer_);
|
||||
SDL_RenderPresent(stream_renderer_);
|
||||
|
||||
return 0;
|
||||
@@ -907,6 +881,20 @@ int Render::Run() {
|
||||
exec_log_path_ = path_manager_->GetLogPath().string();
|
||||
dll_log_path_ = path_manager_->GetLogPath().string();
|
||||
cache_path_ = path_manager_->GetCachePath().string();
|
||||
config_center_ =
|
||||
std::make_unique<ConfigCenter>(cache_path_ + "/config.ini", cert_path_);
|
||||
strncpy(signal_server_ip_tmp_, config_center_->GetServerHost().c_str(),
|
||||
sizeof(signal_server_ip_tmp_) - 1);
|
||||
signal_server_ip_tmp_[sizeof(signal_server_ip_tmp_) - 1] = '\0';
|
||||
strncpy(signal_server_port_tmp_,
|
||||
std::to_string(config_center_->GetServerPort()).c_str(),
|
||||
sizeof(signal_server_port_tmp_) - 1);
|
||||
signal_server_port_tmp_[sizeof(signal_server_port_tmp_) - 1] = '\0';
|
||||
strncpy(cert_file_path_, cert_path_.c_str(), sizeof(cert_file_path_) - 1);
|
||||
cert_file_path_[sizeof(cert_file_path_) - 1] = '\0';
|
||||
} else {
|
||||
std::cerr << "Failed to create PathManager" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
InitializeLogger();
|
||||
@@ -925,13 +913,7 @@ int Render::Run() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Render::InitializeLogger() {
|
||||
if (std::filesystem::exists(exec_log_path_)) {
|
||||
InitLogger(exec_log_path_);
|
||||
} else {
|
||||
InitLogger("logs");
|
||||
}
|
||||
}
|
||||
void Render::InitializeLogger() { InitLogger(exec_log_path_); }
|
||||
|
||||
void Render::InitializeSettings() {
|
||||
LoadSettingsFromCacheFile();
|
||||
@@ -946,20 +928,23 @@ void Render::InitializeSettings() {
|
||||
}
|
||||
|
||||
void Render::InitializeSDL() {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
|
||||
SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
printf("Error: %s\n", SDL_GetError());
|
||||
exit(-1);
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
|
||||
LOG_ERROR("Error: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_DisplayMode DM;
|
||||
SDL_GetCurrentDisplayMode(0, &DM);
|
||||
screen_width_ = DM.w;
|
||||
screen_height_ = DM.h;
|
||||
const SDL_DisplayMode* dm = SDL_GetCurrentDisplayMode(0);
|
||||
if (dm) {
|
||||
screen_width_ = dm->w;
|
||||
screen_height_ = dm->h;
|
||||
}
|
||||
|
||||
STREAM_REFRESH_EVENT = SDL_RegisterEvents(1);
|
||||
if (STREAM_REFRESH_EVENT == (uint32_t)-1) {
|
||||
LOG_ERROR("Failed to register custom SDL event");
|
||||
}
|
||||
|
||||
LOG_INFO("Screen resolution: [{}x{}]", screen_width_, screen_height_);
|
||||
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
|
||||
}
|
||||
|
||||
void Render::InitializeModules() {
|
||||
@@ -989,8 +974,20 @@ void Render::MainLoop() {
|
||||
CreateConnectionPeer();
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
if (SDL_WaitEventTimeout(&event, sdl_refresh_ms_)) {
|
||||
ProcessSdlEvent(event);
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
UpdateLabels();
|
||||
ProcessSdlEvent();
|
||||
HandleRecentConnections();
|
||||
HandleStreamWindow();
|
||||
|
||||
@@ -1082,10 +1079,10 @@ void Render::HandleStreamWindow() {
|
||||
|
||||
if (stream_window_inited_) {
|
||||
if (!stream_window_grabbed_ && control_mouse_) {
|
||||
SDL_SetWindowGrab(stream_window_, SDL_TRUE);
|
||||
SDL_SetWindowMouseGrab(stream_window_, true);
|
||||
stream_window_grabbed_ = true;
|
||||
} else if (stream_window_grabbed_ && !control_mouse_) {
|
||||
SDL_SetWindowGrab(stream_window_, SDL_FALSE);
|
||||
SDL_SetWindowMouseGrab(stream_window_, false);
|
||||
stream_window_grabbed_ = false;
|
||||
}
|
||||
}
|
||||
@@ -1141,7 +1138,7 @@ void Render::CleanupFactories() {
|
||||
}
|
||||
|
||||
void Render::CleanupPeer(std::shared_ptr<SubStreamWindowProperties> props) {
|
||||
SDL_FlushEvent(STREAM_FRASH);
|
||||
SDL_FlushEvent(STREAM_REFRESH_EVENT);
|
||||
|
||||
if (props->dst_buffer_) {
|
||||
thumbnail_->SaveToThumbnail(
|
||||
@@ -1236,144 +1233,183 @@ void Render::UpdateRenderRect() {
|
||||
}
|
||||
}
|
||||
|
||||
void Render::ProcessSdlEvent() {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (main_ctx_) {
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
void Render::ProcessSdlEvent(const SDL_Event& event) {
|
||||
if (main_ctx_) {
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
ImGui_ImplSDL3_ProcessEvent(&event);
|
||||
} else {
|
||||
LOG_ERROR("Main context is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (stream_window_inited_) {
|
||||
if (stream_ctx_) {
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
ImGui_ImplSDL3_ProcessEvent(&event);
|
||||
} else {
|
||||
LOG_ERROR("Main context is null");
|
||||
LOG_ERROR("Stream context is null");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (stream_window_inited_) {
|
||||
if (stream_ctx_) {
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
if (stream_window_inited_) {
|
||||
LOG_INFO("Destroy stream window");
|
||||
SDL_SetWindowMouseGrab(stream_window_, false);
|
||||
DestroyStreamWindow();
|
||||
DestroyStreamWindowContext();
|
||||
|
||||
for (auto& [host_name, props] : client_properties_) {
|
||||
thumbnail_->SaveToThumbnail(
|
||||
(char*)props->dst_buffer_, props->video_width_,
|
||||
props->video_height_, host_name, props->remote_host_name_,
|
||||
props->remember_password_ ? props->remote_password_ : "");
|
||||
|
||||
if (props->peer_) {
|
||||
std::string client_id = (host_name == client_id_)
|
||||
? "C-" + std::string(client_id_)
|
||||
: client_id_;
|
||||
LOG_INFO("[{}] Leave connection [{}]", client_id, host_name);
|
||||
LeaveConnection(props->peer_, host_name.c_str());
|
||||
LOG_INFO("Destroy peer [{}]", client_id);
|
||||
DestroyPeer(&props->peer_);
|
||||
}
|
||||
|
||||
props->streaming_ = false;
|
||||
props->remember_password_ = false;
|
||||
props->connection_established_ = false;
|
||||
props->audio_capture_button_pressed_ = false;
|
||||
|
||||
memset(&props->net_traffic_stats_, 0,
|
||||
sizeof(props->net_traffic_stats_));
|
||||
SDL_SetWindowFullscreen(main_window_, false);
|
||||
SDL_FlushEvents(STREAM_REFRESH_EVENT, STREAM_REFRESH_EVENT);
|
||||
memset(audio_buffer_, 0, 720);
|
||||
}
|
||||
client_properties_.clear();
|
||||
|
||||
rejoin_ = false;
|
||||
is_client_mode_ = false;
|
||||
reload_recent_connections_ = true;
|
||||
fullscreen_button_pressed_ = false;
|
||||
just_created_ = false;
|
||||
recent_connection_image_save_time_ = SDL_GetTicks();
|
||||
} else {
|
||||
LOG_ERROR("Stream context is null");
|
||||
return;
|
||||
LOG_INFO("Quit program");
|
||||
exit_ = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
if (stream_window_inited_) {
|
||||
LOG_INFO("Destroy stream window");
|
||||
SDL_SetWindowGrab(stream_window_, SDL_FALSE);
|
||||
DestroyStreamWindow();
|
||||
DestroyStreamWindowContext();
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
if (event.window.windowID != SDL_GetWindowID(stream_window_)) {
|
||||
exit_ = true;
|
||||
}
|
||||
break;
|
||||
|
||||
for (auto& [host_name, props] : client_properties_) {
|
||||
thumbnail_->SaveToThumbnail(
|
||||
(char*)props->dst_buffer_, props->video_width_,
|
||||
props->video_height_, host_name, props->remote_host_name_,
|
||||
props->remember_password_ ? props->remote_password_ : "");
|
||||
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
||||
if (stream_window_created_ &&
|
||||
event.window.windowID == SDL_GetWindowID(stream_window_)) {
|
||||
UpdateRenderRect();
|
||||
}
|
||||
break;
|
||||
|
||||
if (props->peer_) {
|
||||
std::string client_id = (host_name == client_id_)
|
||||
? "C-" + std::string(client_id_)
|
||||
: client_id_;
|
||||
LOG_INFO("[{}] Leave connection [{}]", client_id, host_name);
|
||||
LeaveConnection(props->peer_, host_name.c_str());
|
||||
LOG_INFO("Destroy peer [{}]", client_id);
|
||||
DestroyPeer(&props->peer_);
|
||||
}
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
if (stream_window_ &&
|
||||
SDL_GetWindowID(stream_window_) == event.window.windowID) {
|
||||
foucs_on_stream_window_ = true;
|
||||
} else if (main_window_ &&
|
||||
SDL_GetWindowID(main_window_) == event.window.windowID) {
|
||||
foucs_on_main_window_ = true;
|
||||
}
|
||||
break;
|
||||
|
||||
props->streaming_ = false;
|
||||
props->remember_password_ = false;
|
||||
props->connection_established_ = false;
|
||||
props->audio_capture_button_pressed_ = false;
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||
if (stream_window_ &&
|
||||
SDL_GetWindowID(stream_window_) == event.window.windowID) {
|
||||
foucs_on_stream_window_ = false;
|
||||
} else if (main_window_ &&
|
||||
SDL_GetWindowID(main_window_) == event.window.windowID) {
|
||||
foucs_on_main_window_ = false;
|
||||
}
|
||||
break;
|
||||
|
||||
memset(&props->net_traffic_stats_, 0,
|
||||
sizeof(props->net_traffic_stats_));
|
||||
SDL_SetWindowFullscreen(main_window_, SDL_FALSE);
|
||||
SDL_FlushEvents(STREAM_FRASH, STREAM_FRASH);
|
||||
memset(audio_buffer_, 0, 720);
|
||||
}
|
||||
client_properties_.clear();
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
if (foucs_on_stream_window_) {
|
||||
ProcessMouseEvent(event);
|
||||
}
|
||||
break;
|
||||
|
||||
rejoin_ = false;
|
||||
is_client_mode_ = false;
|
||||
reload_recent_connections_ = true;
|
||||
fullscreen_button_pressed_ = false;
|
||||
just_created_ = false;
|
||||
recent_connection_image_save_time_ = SDL_GetTicks();
|
||||
} else {
|
||||
LOG_INFO("Quit program");
|
||||
exit_ = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
if (event.window.event == SDL_WINDOWEVENT_CLOSE &&
|
||||
event.window.windowID != SDL_GetWindowID(stream_window_)) {
|
||||
exit_ = true;
|
||||
} else if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED &&
|
||||
stream_window_created_ &&
|
||||
event.window.windowID == SDL_GetWindowID(stream_window_)) {
|
||||
UpdateRenderRect();
|
||||
} else if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED ||
|
||||
event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
|
||||
bool focus_gained =
|
||||
event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED;
|
||||
if (stream_window_ &&
|
||||
SDL_GetWindowID(stream_window_) == event.window.windowID) {
|
||||
foucs_on_stream_window_ = focus_gained;
|
||||
} else if (main_window_ &&
|
||||
SDL_GetWindowID(main_window_) == event.window.windowID) {
|
||||
foucs_on_main_window_ = focus_gained;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STREAM_FRASH: {
|
||||
default:
|
||||
if (event.type == STREAM_REFRESH_EVENT) {
|
||||
auto* props = static_cast<SubStreamWindowProperties*>(event.user.data1);
|
||||
if (!props) {
|
||||
LOG_ERROR("Invalid stream window properties");
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (props->video_width_ <= 0 || props->video_height_ <= 0) {
|
||||
break;
|
||||
}
|
||||
if (!props->dst_buffer_) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (props->stream_texture_) {
|
||||
if (props->video_width_ != props->texture_width_ ||
|
||||
props->video_height_ != props->texture_height_) {
|
||||
// LOG_WARN("Resolution changed, old: [{}x{}], new: [{}x{}]",
|
||||
// props->texture_width_, props->texture_height_,
|
||||
// props->video_width_, props->video_height_);
|
||||
props->texture_width_ = props->video_width_;
|
||||
props->texture_height_ = props->video_height_;
|
||||
|
||||
SDL_DestroyTexture(props->stream_texture_);
|
||||
props->stream_texture_ = SDL_CreateTexture(
|
||||
stream_renderer_, stream_pixformat_,
|
||||
SDL_TEXTUREACCESS_STREAMING, props->texture_width_,
|
||||
props->texture_height_);
|
||||
// props->stream_texture_ = SDL_CreateTexture(
|
||||
// stream_renderer_, stream_pixformat_,
|
||||
// SDL_TEXTUREACCESS_STREAMING, props->texture_width_,
|
||||
// props->texture_height_);
|
||||
|
||||
SDL_PropertiesID nvProps = SDL_CreateProperties();
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER,
|
||||
props->texture_width_);
|
||||
SDL_SetNumberProperty(nvProps,
|
||||
SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER,
|
||||
props->texture_height_);
|
||||
SDL_SetNumberProperty(nvProps,
|
||||
SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER,
|
||||
SDL_PIXELFORMAT_NV12);
|
||||
SDL_SetNumberProperty(nvProps,
|
||||
SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER,
|
||||
SDL_COLORSPACE_BT601_LIMITED);
|
||||
props->stream_texture_ =
|
||||
SDL_CreateTextureWithProperties(stream_renderer_, nvProps);
|
||||
SDL_DestroyProperties(nvProps);
|
||||
}
|
||||
} else {
|
||||
props->texture_width_ = props->video_width_;
|
||||
props->texture_height_ = props->video_height_;
|
||||
props->stream_texture_ = SDL_CreateTexture(
|
||||
stream_renderer_, stream_pixformat_, SDL_TEXTUREACCESS_STREAMING,
|
||||
props->texture_width_, props->texture_height_);
|
||||
// props->stream_texture_ = SDL_CreateTexture(
|
||||
// stream_renderer_, stream_pixformat_,
|
||||
// SDL_TEXTUREACCESS_STREAMING, props->texture_width_,
|
||||
// props->texture_height_);
|
||||
|
||||
SDL_PropertiesID nvProps = SDL_CreateProperties();
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER,
|
||||
props->texture_width_);
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER,
|
||||
props->texture_height_);
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER,
|
||||
SDL_PIXELFORMAT_NV12);
|
||||
SDL_SetNumberProperty(nvProps,
|
||||
SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER,
|
||||
SDL_COLORSPACE_BT601_LIMITED);
|
||||
props->stream_texture_ =
|
||||
SDL_CreateTextureWithProperties(stream_renderer_, nvProps);
|
||||
SDL_DestroyProperties(nvProps);
|
||||
}
|
||||
|
||||
SDL_UpdateTexture(props->stream_texture_, NULL, props->dst_buffer_,
|
||||
props->texture_width_);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (foucs_on_stream_window_) {
|
||||
ProcessMouseEvent(event);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
#ifndef _MAIN_WINDOW_H_
|
||||
#define _MAIN_WINDOW_H_
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
@@ -21,20 +21,23 @@
|
||||
#include "config_center.h"
|
||||
#include "device_controller_factory.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl2.h"
|
||||
#include "imgui_impl_sdlrenderer2.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include "imgui_impl_sdlrenderer3.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "minirtc.h"
|
||||
#include "path_manager.h"
|
||||
#include "screen_capturer_factory.h"
|
||||
#include "speaker_capturer_factory.h"
|
||||
#include "thumbnail.h"
|
||||
#if _WIN32
|
||||
#include "win_tray.h"
|
||||
#endif
|
||||
|
||||
class Render {
|
||||
public:
|
||||
struct SubStreamWindowProperties {
|
||||
Params params_;
|
||||
PeerPtr *peer_ = nullptr;
|
||||
PeerPtr* peer_ = nullptr;
|
||||
std::string audio_label_ = "control_audio";
|
||||
std::string data_label_ = "control_data";
|
||||
std::string local_id_ = "";
|
||||
@@ -65,7 +68,7 @@ class Render {
|
||||
float control_window_min_width_ = 20;
|
||||
float control_window_max_width_ = 230;
|
||||
float control_window_min_height_ = 40;
|
||||
float control_window_max_height_ = 150;
|
||||
float control_window_max_height_ = 170;
|
||||
float control_window_width_ = 230;
|
||||
float control_window_height_ = 40;
|
||||
float control_bar_pos_x_ = 0;
|
||||
@@ -74,12 +77,12 @@ class Render {
|
||||
float mouse_diff_control_bar_pos_y_ = 0;
|
||||
double control_bar_button_pressed_time_ = 0;
|
||||
double net_traffic_stats_button_pressed_time_ = 0;
|
||||
unsigned char *dst_buffer_ = nullptr;
|
||||
unsigned char* dst_buffer_ = nullptr;
|
||||
size_t dst_buffer_capacity_ = 0;
|
||||
int mouse_pos_x_ = 0;
|
||||
int mouse_pos_y_ = 0;
|
||||
int mouse_pos_x_last_ = 0;
|
||||
int mouse_pos_y_last_ = 0;
|
||||
float mouse_pos_x_ = 0;
|
||||
float mouse_pos_y_ = 0;
|
||||
float mouse_pos_x_last_ = 0;
|
||||
float mouse_pos_y_last_ = 0;
|
||||
int texture_width_ = 1280;
|
||||
int texture_height_ = 720;
|
||||
int video_width_ = 0;
|
||||
@@ -102,12 +105,17 @@ class Render {
|
||||
std::string audio_capture_button_label_ = "Audio Capture";
|
||||
std::string remote_host_name_ = "";
|
||||
std::vector<DisplayInfo> display_info_list_;
|
||||
SDL_Texture *stream_texture_ = nullptr;
|
||||
SDL_Texture* stream_texture_ = nullptr;
|
||||
uint8_t* argb_buffer_ = nullptr;
|
||||
int argb_buffer_size_ = 0;
|
||||
SDL_Rect stream_render_rect_;
|
||||
SDL_Rect stream_render_rect_last_;
|
||||
ImVec2 control_window_pos_;
|
||||
ConnectionStatus connection_status_ = ConnectionStatus::Closed;
|
||||
TraversalMode traversal_mode_ = TraversalMode::UnknownMode;
|
||||
int fps_ = 0;
|
||||
int frame_count_ = 0;
|
||||
std::chrono::steady_clock::time_point last_time_;
|
||||
XNetTrafficStats net_traffic_stats_;
|
||||
};
|
||||
|
||||
@@ -136,7 +144,7 @@ class Render {
|
||||
void CleanSubStreamWindowProperties(
|
||||
std::shared_ptr<SubStreamWindowProperties> props);
|
||||
void UpdateRenderRect();
|
||||
void ProcessSdlEvent();
|
||||
void ProcessSdlEvent(const SDL_Event& event);
|
||||
|
||||
private:
|
||||
int CreateStreamRenderWindow();
|
||||
@@ -147,15 +155,18 @@ class Render {
|
||||
int RemoteWindow();
|
||||
int RecentConnectionsWindow();
|
||||
int SettingWindow();
|
||||
int ControlWindow(std::shared_ptr<SubStreamWindowProperties> &props);
|
||||
int ControlBar(std::shared_ptr<SubStreamWindowProperties> &props);
|
||||
int SelfHostedServerWindow();
|
||||
int ShowSimpleFileBrowser();
|
||||
int ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props);
|
||||
int ControlBar(std::shared_ptr<SubStreamWindowProperties>& props);
|
||||
int AboutWindow();
|
||||
int StatusBar();
|
||||
int ConnectionStatusWindow(std::shared_ptr<SubStreamWindowProperties> &props);
|
||||
bool ConnectionStatusWindow(
|
||||
std::shared_ptr<SubStreamWindowProperties>& props);
|
||||
int ShowRecentConnections();
|
||||
|
||||
private:
|
||||
int ConnectTo(const std::string &remote_id, const char *password,
|
||||
int ConnectTo(const std::string& remote_id, const char* password,
|
||||
bool remember_password);
|
||||
int CreateMainWindow();
|
||||
int DestroyMainWindow();
|
||||
@@ -169,51 +180,51 @@ class Render {
|
||||
int DrawMainWindow();
|
||||
int DrawStreamWindow();
|
||||
int ConfirmDeleteConnection();
|
||||
int NetTrafficStats(std::shared_ptr<SubStreamWindowProperties> &props);
|
||||
int NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props);
|
||||
void DrawConnectionStatusText(
|
||||
std::shared_ptr<SubStreamWindowProperties> &props);
|
||||
std::shared_ptr<SubStreamWindowProperties>& props);
|
||||
|
||||
public:
|
||||
static void OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data);
|
||||
static void OnReceiveVideoBufferCb(const XVideoFrame* video_frame,
|
||||
const char* user_id, size_t user_id_size,
|
||||
void* user_data);
|
||||
|
||||
static void OnReceiveAudioBufferCb(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data);
|
||||
static void OnReceiveAudioBufferCb(const char* data, size_t size,
|
||||
const char* user_id, size_t user_id_size,
|
||||
void* user_data);
|
||||
|
||||
static void OnReceiveDataBufferCb(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data);
|
||||
static void OnReceiveDataBufferCb(const char* data, size_t size,
|
||||
const char* user_id, size_t user_id_size,
|
||||
void* user_data);
|
||||
|
||||
static void OnSignalStatusCb(SignalStatus status, const char *user_id,
|
||||
size_t user_id_size, void *user_data);
|
||||
static void OnSignalStatusCb(SignalStatus status, const char* user_id,
|
||||
size_t user_id_size, void* user_data);
|
||||
|
||||
static void OnConnectionStatusCb(ConnectionStatus status, const char *user_id,
|
||||
size_t user_id_size, void *user_data);
|
||||
static void OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
||||
size_t user_id_size, void* user_data);
|
||||
|
||||
static void NetStatusReport(const char *client_id, size_t client_id_size,
|
||||
static void NetStatusReport(const char* client_id, size_t client_id_size,
|
||||
TraversalMode mode,
|
||||
const XNetTrafficStats *net_traffic_stats,
|
||||
const char *user_id, const size_t user_id_size,
|
||||
void *user_data);
|
||||
const XNetTrafficStats* net_traffic_stats,
|
||||
const char* user_id, const size_t user_id_size,
|
||||
void* user_data);
|
||||
|
||||
static SDL_HitTestResult HitTestCallback(SDL_Window *window,
|
||||
const SDL_Point *area, void *data);
|
||||
static SDL_HitTestResult HitTestCallback(SDL_Window* window,
|
||||
const SDL_Point* area, void* data);
|
||||
|
||||
static std::vector<char> SerializeRemoteAction(const RemoteAction &action);
|
||||
static std::vector<char> SerializeRemoteAction(const RemoteAction& action);
|
||||
|
||||
static bool DeserializeRemoteAction(const char *data, size_t size,
|
||||
RemoteAction &out);
|
||||
static bool DeserializeRemoteAction(const char* data, size_t size,
|
||||
RemoteAction& out);
|
||||
|
||||
static void FreeRemoteAction(RemoteAction &action);
|
||||
static void FreeRemoteAction(RemoteAction& action);
|
||||
|
||||
private:
|
||||
int SendKeyCommand(int key_code, bool is_down);
|
||||
int ProcessMouseEvent(SDL_Event &event);
|
||||
int ProcessMouseEvent(const SDL_Event& event);
|
||||
|
||||
static void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len);
|
||||
static void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len);
|
||||
static void SdlCaptureAudioIn(void* userdata, Uint8* stream, int len);
|
||||
static void SdlCaptureAudioOut(void* userdata, Uint8* stream, int len);
|
||||
|
||||
private:
|
||||
int SaveSettingsIntoCacheFile();
|
||||
@@ -242,9 +253,11 @@ class Render {
|
||||
char client_id_with_password[17];
|
||||
int language;
|
||||
int video_quality;
|
||||
int video_frame_rate;
|
||||
int video_encode_format;
|
||||
bool enable_hardware_video_codec;
|
||||
bool enable_turn;
|
||||
bool enable_srtp;
|
||||
|
||||
unsigned char key[16];
|
||||
unsigned char iv[16];
|
||||
@@ -253,7 +266,7 @@ class Render {
|
||||
private:
|
||||
CDCache cd_cache_;
|
||||
std::mutex cd_cache_mutex_;
|
||||
ConfigCenter config_center_;
|
||||
std::unique_ptr<ConfigCenter> config_center_;
|
||||
ConfigCenter::LANGUAGE localization_language_ =
|
||||
ConfigCenter::LANGUAGE::CHINESE;
|
||||
std::unique_ptr<PathManager> path_manager_;
|
||||
@@ -261,7 +274,6 @@ class Render {
|
||||
std::string exec_log_path_;
|
||||
std::string dll_log_path_;
|
||||
std::string cache_path_;
|
||||
std::string imgui_cache_path_;
|
||||
int localization_language_index_ = -1;
|
||||
int localization_language_index_last_ = -1;
|
||||
bool modules_inited_ = false;
|
||||
@@ -284,10 +296,14 @@ class Render {
|
||||
uint32_t recent_connection_image_save_time_ = 0;
|
||||
|
||||
// main window render
|
||||
SDL_Window *main_window_ = nullptr;
|
||||
SDL_Renderer *main_renderer_ = nullptr;
|
||||
ImGuiContext *main_ctx_ = nullptr;
|
||||
SDL_Window* main_window_ = nullptr;
|
||||
SDL_Renderer* main_renderer_ = nullptr;
|
||||
ImGuiContext* main_ctx_ = nullptr;
|
||||
bool exit_ = false;
|
||||
const int sdl_refresh_ms_ = 16; // ~60 FPS
|
||||
#if _WIN32
|
||||
std::unique_ptr<WinTray> tray_;
|
||||
#endif
|
||||
|
||||
// main window properties
|
||||
bool start_mouse_controller_ = false;
|
||||
@@ -325,8 +341,8 @@ class Render {
|
||||
float connection_status_window_height_ = 150;
|
||||
float notification_window_width_ = 200;
|
||||
float notification_window_height_ = 80;
|
||||
float about_window_width_ = 200;
|
||||
float about_window_height_ = 150;
|
||||
float about_window_width_ = 300;
|
||||
float about_window_height_ = 170;
|
||||
int screen_width_ = 1280;
|
||||
int screen_height_ = 720;
|
||||
int selected_display_ = 0;
|
||||
@@ -342,13 +358,16 @@ class Render {
|
||||
bool need_to_rejoin_ = false;
|
||||
bool just_created_ = false;
|
||||
std::string controlled_remote_id_ = "";
|
||||
std::string focused_remote_id_ = "";
|
||||
bool need_to_send_host_info_ = false;
|
||||
SDL_Event last_mouse_event;
|
||||
SDL_AudioStream* output_stream_;
|
||||
uint32_t STREAM_REFRESH_EVENT = 0;
|
||||
|
||||
// stream window render
|
||||
SDL_Window *stream_window_ = nullptr;
|
||||
SDL_Renderer *stream_renderer_ = nullptr;
|
||||
ImGuiContext *stream_ctx_ = nullptr;
|
||||
SDL_Window* stream_window_ = nullptr;
|
||||
SDL_Renderer* stream_renderer_ = nullptr;
|
||||
ImGuiContext* stream_ctx_ = nullptr;
|
||||
|
||||
// stream window properties
|
||||
bool need_to_create_stream_window_ = false;
|
||||
@@ -361,7 +380,7 @@ class Render {
|
||||
int stream_window_height_default_ = 720;
|
||||
float stream_window_width_ = 1280;
|
||||
float stream_window_height_ = 720;
|
||||
uint32_t stream_pixformat_ = 0;
|
||||
SDL_PixelFormat stream_pixformat_ = SDL_PIXELFORMAT_NV12;
|
||||
int stream_window_width_real_ = 1280;
|
||||
int stream_window_height_real_ = 720;
|
||||
float stream_window_dpi_scaling_w_ = 1.0f;
|
||||
@@ -372,10 +391,10 @@ class Render {
|
||||
bool password_validating_ = false;
|
||||
uint32_t password_validating_time_ = 0;
|
||||
bool show_settings_window_ = false;
|
||||
bool show_self_hosted_server_config_window_ = false;
|
||||
bool rejoin_ = false;
|
||||
bool local_id_copied_ = false;
|
||||
bool show_password_ = true;
|
||||
bool regenerate_password_ = false;
|
||||
bool show_about_window_ = false;
|
||||
bool show_connection_status_window_ = false;
|
||||
bool show_reset_password_window_ = false;
|
||||
@@ -389,12 +408,11 @@ class Render {
|
||||
std::string delete_connection_name_ = "";
|
||||
bool re_enter_remote_id_ = false;
|
||||
double copy_start_time_ = 0;
|
||||
double regenerate_password_start_time_ = 0;
|
||||
SignalStatus signal_status_ = SignalStatus::SignalClosed;
|
||||
std::string signal_status_str_ = "";
|
||||
bool signal_connected_ = false;
|
||||
PeerPtr *peer_ = nullptr;
|
||||
PeerPtr *peer_reserved_ = nullptr;
|
||||
PeerPtr* peer_ = nullptr;
|
||||
PeerPtr* peer_reserved_ = nullptr;
|
||||
std::string video_primary_label_ = "primary_display";
|
||||
std::string video_secondary_label_ = "secondary_display";
|
||||
std::string audio_label_ = "audio";
|
||||
@@ -402,13 +420,13 @@ class Render {
|
||||
Params params_;
|
||||
SDL_AudioDeviceID input_dev_;
|
||||
SDL_AudioDeviceID output_dev_;
|
||||
ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
|
||||
ScreenCapturer *screen_capturer_ = nullptr;
|
||||
SpeakerCapturerFactory *speaker_capturer_factory_ = nullptr;
|
||||
SpeakerCapturer *speaker_capturer_ = nullptr;
|
||||
DeviceControllerFactory *device_controller_factory_ = nullptr;
|
||||
MouseController *mouse_controller_ = nullptr;
|
||||
KeyboardCapturer *keyboard_capturer_ = nullptr;
|
||||
ScreenCapturerFactory* screen_capturer_factory_ = nullptr;
|
||||
ScreenCapturer* screen_capturer_ = nullptr;
|
||||
SpeakerCapturerFactory* speaker_capturer_factory_ = nullptr;
|
||||
SpeakerCapturer* speaker_capturer_ = nullptr;
|
||||
DeviceControllerFactory* device_controller_factory_ = nullptr;
|
||||
MouseController* mouse_controller_ = nullptr;
|
||||
KeyboardCapturer* keyboard_capturer_ = nullptr;
|
||||
std::vector<DisplayInfo> display_info_list_;
|
||||
uint64_t last_frame_time_;
|
||||
char client_id_[10] = "";
|
||||
@@ -417,21 +435,35 @@ class Render {
|
||||
char password_saved_[7] = "";
|
||||
int language_button_value_ = 0;
|
||||
int video_quality_button_value_ = 0;
|
||||
int video_frame_rate_button_value_ = 0;
|
||||
int video_encode_format_button_value_ = 0;
|
||||
bool enable_hardware_video_codec_ = false;
|
||||
bool enable_turn_ = false;
|
||||
bool enable_srtp_ = false;
|
||||
char signal_server_ip_[256] = "api.crossdesk.cn";
|
||||
char signal_server_port_[6] = "9099";
|
||||
char cert_file_path_[256] = "";
|
||||
bool enable_self_hosted_server_ = false;
|
||||
int language_button_value_last_ = 0;
|
||||
int video_quality_button_value_last_ = 0;
|
||||
int video_encode_format_button_value_last_ = 0;
|
||||
bool enable_hardware_video_codec_last_ = false;
|
||||
bool enable_turn_last_ = false;
|
||||
bool enable_srtp_last_ = false;
|
||||
bool enable_minimize_to_tray_ = false;
|
||||
bool enable_minimize_to_tray_last_ = false;
|
||||
char signal_server_ip_tmp_[256] = "api.crossdesk.cn";
|
||||
char signal_server_port_tmp_[6] = "9099";
|
||||
bool settings_window_pos_reset_ = true;
|
||||
bool self_hosted_server_config_window_pos_reset_ = true;
|
||||
std::string selected_current_file_path_ = "";
|
||||
std::string selected_file_ = "";
|
||||
/* ------ main window property end ------ */
|
||||
|
||||
/* ------ sub stream window property start ------ */
|
||||
std::unordered_map<std::string, std::shared_ptr<SubStreamWindowProperties>>
|
||||
client_properties_;
|
||||
void CloseTab(decltype(client_properties_)::iterator &it);
|
||||
void CloseTab(decltype(client_properties_)::iterator& it);
|
||||
/* ------ stream window property end ------ */
|
||||
};
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
||||
|
||||
#define STREAM_FRASH (SDL_USEREVENT + 1)
|
||||
|
||||
#ifdef DESK_PORT_DEBUG
|
||||
#else
|
||||
#define MOUSE_CONTROL 1
|
||||
@@ -28,7 +26,7 @@ int Render::SendKeyCommand(int key_code, bool is_down) {
|
||||
client_properties_.end()) {
|
||||
auto props = client_properties_[controlled_remote_id_];
|
||||
if (props->connection_status_ == ConnectionStatus::Connected) {
|
||||
SendDataFrame(props->peer_, (const char *)&remote_action,
|
||||
SendDataFrame(props->peer_, (const char*)&remote_action,
|
||||
sizeof(remote_action), props->data_label_.c_str());
|
||||
}
|
||||
}
|
||||
@@ -37,14 +35,14 @@ int Render::SendKeyCommand(int key_code, bool is_down) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::ProcessMouseEvent(SDL_Event &event) {
|
||||
int Render::ProcessMouseEvent(const SDL_Event& event) {
|
||||
controlled_remote_id_ = "";
|
||||
int video_width, video_height = 0;
|
||||
int render_width, render_height = 0;
|
||||
float ratio_x, ratio_y = 0;
|
||||
RemoteAction remote_action;
|
||||
|
||||
for (auto &it : client_properties_) {
|
||||
for (auto& it : client_properties_) {
|
||||
auto props = it.second;
|
||||
if (!props->control_mouse_) {
|
||||
continue;
|
||||
@@ -68,7 +66,7 @@ int Render::ProcessMouseEvent(SDL_Event &event) {
|
||||
(float)(event.button.y - props->stream_render_rect_.y) /
|
||||
render_height;
|
||||
|
||||
if (SDL_MOUSEBUTTONDOWN == event.type) {
|
||||
if (SDL_EVENT_MOUSE_BUTTON_DOWN == event.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_down;
|
||||
@@ -77,7 +75,7 @@ int Render::ProcessMouseEvent(SDL_Event &event) {
|
||||
} else if (SDL_BUTTON_MIDDLE == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::middle_down;
|
||||
}
|
||||
} else if (SDL_MOUSEBUTTONUP == event.type) {
|
||||
} else if (SDL_EVENT_MOUSE_BUTTON_UP == event.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_up;
|
||||
@@ -86,7 +84,7 @@ int Render::ProcessMouseEvent(SDL_Event &event) {
|
||||
} else if (SDL_BUTTON_MIDDLE == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::middle_up;
|
||||
}
|
||||
} else if (SDL_MOUSEMOTION == event.type) {
|
||||
} else if (SDL_EVENT_MOUSE_MOTION == event.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
}
|
||||
@@ -94,9 +92,9 @@ int Render::ProcessMouseEvent(SDL_Event &event) {
|
||||
if (props->control_bar_hovered_ || props->display_selectable_hovered_) {
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
}
|
||||
SendDataFrame(props->peer_, (const char *)&remote_action,
|
||||
SendDataFrame(props->peer_, (const char*)&remote_action,
|
||||
sizeof(remote_action), props->data_label_.c_str());
|
||||
} else if (SDL_MOUSEWHEEL == event.type &&
|
||||
} else if (SDL_EVENT_MOUSE_WHEEL == event.type &&
|
||||
last_mouse_event.button.x >= props->stream_render_rect_.x &&
|
||||
last_mouse_event.button.x <= props->stream_render_rect_.x +
|
||||
props->stream_render_rect_.w &&
|
||||
@@ -127,7 +125,7 @@ int Render::ProcessMouseEvent(SDL_Event &event) {
|
||||
(float)(event.button.y - props->stream_render_rect_.y) /
|
||||
render_height;
|
||||
|
||||
SendDataFrame(props->peer_, (const char *)&remote_action,
|
||||
SendDataFrame(props->peer_, (const char*)&remote_action,
|
||||
sizeof(remote_action), props->data_label_.c_str());
|
||||
}
|
||||
}
|
||||
@@ -135,8 +133,8 @@ int Render::ProcessMouseEvent(SDL_Event &event) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Render::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
||||
Render *render = (Render *)userdata;
|
||||
void Render::SdlCaptureAudioIn(void* userdata, Uint8* stream, int len) {
|
||||
Render* render = (Render*)userdata;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
@@ -145,7 +143,7 @@ void Render::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
||||
for (auto it : render->client_properties_) {
|
||||
auto props = it.second;
|
||||
if (props->connection_status_ == ConnectionStatus::Connected) {
|
||||
SendAudioFrame(props->peer_, (const char *)stream, len,
|
||||
SendAudioFrame(props->peer_, (const char*)stream, len,
|
||||
render->audio_label_.c_str());
|
||||
}
|
||||
}
|
||||
@@ -158,8 +156,8 @@ void Render::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
||||
}
|
||||
}
|
||||
|
||||
void Render::SdlCaptureAudioOut([[maybe_unused]] void *userdata,
|
||||
[[maybe_unused]] Uint8 *stream,
|
||||
void Render::SdlCaptureAudioOut([[maybe_unused]] void* userdata,
|
||||
[[maybe_unused]] Uint8* stream,
|
||||
[[maybe_unused]] int len) {
|
||||
// Render *render = (Render *)userdata;
|
||||
// for (auto it : render->client_properties_) {
|
||||
@@ -186,10 +184,10 @@ void Render::SdlCaptureAudioOut([[maybe_unused]] void *userdata,
|
||||
// render->audio_buffer_fresh_ = false;
|
||||
}
|
||||
|
||||
void Render::OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
void Render::OnReceiveVideoBufferCb(const XVideoFrame* video_frame,
|
||||
const char* user_id, size_t user_id_size,
|
||||
void* user_data) {
|
||||
Render* render = (Render*)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
@@ -199,7 +197,7 @@ void Render::OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
|
||||
render->client_properties_.end()) {
|
||||
return;
|
||||
}
|
||||
SubStreamWindowProperties *props =
|
||||
SubStreamWindowProperties* props =
|
||||
render->client_properties_.find(remote_id)->second.get();
|
||||
|
||||
if (props->connection_established_) {
|
||||
@@ -231,30 +229,50 @@ void Render::OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
event.type = STREAM_FRASH;
|
||||
event.user.type = STREAM_FRASH;
|
||||
event.type = render->STREAM_REFRESH_EVENT;
|
||||
event.user.data1 = props;
|
||||
SDL_PushEvent(&event);
|
||||
props->streaming_ = true;
|
||||
|
||||
if (props->net_traffic_stats_button_pressed_) {
|
||||
props->frame_count_++;
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - props->last_time_)
|
||||
.count();
|
||||
|
||||
if (elapsed >= 1000) {
|
||||
props->fps_ = props->frame_count_ * 1000 / elapsed;
|
||||
props->frame_count_ = 0;
|
||||
props->last_time_ = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Render::OnReceiveAudioBufferCb(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
void Render::OnReceiveAudioBufferCb(const char* data, size_t size,
|
||||
const char* user_id, size_t user_id_size,
|
||||
void* user_data) {
|
||||
Render* render = (Render*)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
render->audio_buffer_fresh_ = true;
|
||||
SDL_QueueAudio(render->output_dev_, data, (uint32_t)size);
|
||||
|
||||
if (render->output_stream_) {
|
||||
int pushed = SDL_PutAudioStreamData(
|
||||
render->output_stream_, (const Uint8*)data, static_cast<int>(size));
|
||||
if (pushed < 0) {
|
||||
LOG_ERROR("Failed to push audio data: {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Render::OnReceiveDataBufferCb(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
void Render::OnReceiveDataBufferCb(const char* data, size_t size,
|
||||
const char* user_id, size_t user_id_size,
|
||||
void* user_data) {
|
||||
Render* render = (Render*)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
@@ -321,9 +339,9 @@ void Render::OnReceiveDataBufferCb(const char *data, size_t size,
|
||||
}
|
||||
}
|
||||
|
||||
void Render::OnSignalStatusCb(SignalStatus status, const char *user_id,
|
||||
size_t user_id_size, void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
void Render::OnSignalStatusCb(SignalStatus status, const char* user_id,
|
||||
size_t user_id_size, void* user_data) {
|
||||
Render* render = (Render*)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
@@ -374,9 +392,9 @@ void Render::OnSignalStatusCb(SignalStatus status, const char *user_id,
|
||||
}
|
||||
}
|
||||
|
||||
void Render::OnConnectionStatusCb(ConnectionStatus status, const char *user_id,
|
||||
const size_t user_id_size, void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
||||
const size_t user_id_size, void* user_data) {
|
||||
Render* render = (Render*)user_data;
|
||||
if (!render) return;
|
||||
|
||||
std::string remote_id(user_id, user_id_size);
|
||||
@@ -464,19 +482,19 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char *user_id,
|
||||
}
|
||||
}
|
||||
|
||||
void Render::NetStatusReport(const char *client_id, size_t client_id_size,
|
||||
void Render::NetStatusReport(const char* client_id, size_t client_id_size,
|
||||
TraversalMode mode,
|
||||
const XNetTrafficStats *net_traffic_stats,
|
||||
const char *user_id, const size_t user_id_size,
|
||||
void *user_data) {
|
||||
Render *render = (Render *)user_data;
|
||||
const XNetTrafficStats* net_traffic_stats,
|
||||
const char* user_id, const size_t user_id_size,
|
||||
void* user_data) {
|
||||
Render* render = (Render*)user_data;
|
||||
if (!render) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strchr(client_id, '@') != nullptr && strchr(user_id, '-') == nullptr) {
|
||||
std::string id, password;
|
||||
const char *at_pos = strchr(client_id, '@');
|
||||
const char* at_pos = strchr(client_id, '@');
|
||||
if (at_pos == nullptr) {
|
||||
id = client_id;
|
||||
password.clear();
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -195,9 +195,9 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
|
||||
: localization::fullscreen[localization_language_index_];
|
||||
|
||||
if (fullscreen_button_pressed_) {
|
||||
SDL_SetWindowFullscreen(stream_window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
SDL_SetWindowFullscreen(stream_window_, true);
|
||||
} else {
|
||||
SDL_SetWindowFullscreen(stream_window_, SDL_FALSE);
|
||||
SDL_SetWindowFullscreen(stream_window_, false);
|
||||
}
|
||||
props->reset_control_bar_pos_ = true;
|
||||
}
|
||||
@@ -259,7 +259,7 @@ int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
|
||||
|
||||
if (ImGui::BeginTable("NetTrafficStats", 4, ImGuiTableFlags_BordersH,
|
||||
ImVec2(props->control_window_max_width_ - 10.0f,
|
||||
props->control_window_max_height_ - 40.0f))) {
|
||||
props->control_window_max_height_ - 60.0f))) {
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
|
||||
@@ -275,7 +275,6 @@ int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
|
||||
ImGui::Text("%s",
|
||||
localization::loss_rate[localization_language_index_].c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::video[localization_language_index_].c_str());
|
||||
@@ -286,7 +285,6 @@ int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(props->net_traffic_stats_.video_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::audio[localization_language_index_].c_str());
|
||||
@@ -297,7 +295,6 @@ int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(props->net_traffic_stats_.audio_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", localization::data[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
@@ -307,7 +304,6 @@ int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(props->net_traffic_stats_.data_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::total[localization_language_index_].c_str());
|
||||
@@ -318,6 +314,11 @@ int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(props->net_traffic_stats_.total_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("FPS");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", props->fps_);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ int Render::TitleBar(bool main_window) {
|
||||
|
||||
{
|
||||
SettingWindow();
|
||||
SelfHostedServerWindow();
|
||||
AboutWindow();
|
||||
}
|
||||
}
|
||||
@@ -138,9 +139,17 @@ int Render::TitleBar(bool main_window) {
|
||||
float xmark_size = 12.0f;
|
||||
std::string close_button = "##xmark"; // ICON_FA_XMARK;
|
||||
if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
#if _WIN32
|
||||
if (enable_minimize_to_tray_) {
|
||||
tray_->MinimizeToTray();
|
||||
} else {
|
||||
#endif
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
#if _WIN32
|
||||
}
|
||||
#endif
|
||||
}
|
||||
draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
|
||||
xmark_pos_y - xmark_size / 2 + 0.75f),
|
||||
112
src/gui/tray/win_tray.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "win_tray.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "localization.h"
|
||||
|
||||
// callback for the message-only window that handles tray icon messages
|
||||
static LRESULT CALLBACK MsgWndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
WinTray* tray =
|
||||
reinterpret_cast<WinTray*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
|
||||
if (!tray) {
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
if (msg == WM_TRAY_CALLBACK) {
|
||||
MSG tmpMsg = {};
|
||||
tmpMsg.message = msg;
|
||||
tmpMsg.wParam = wParam;
|
||||
tmpMsg.lParam = lParam;
|
||||
tray->HandleTrayMessage(&tmpMsg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
WinTray::WinTray(HWND app_hwnd, HICON icon, const std::wstring& tooltip,
|
||||
int language_index)
|
||||
: app_hwnd_(app_hwnd),
|
||||
icon_(icon),
|
||||
tip_(tooltip),
|
||||
hwnd_message_only_(nullptr),
|
||||
language_index_(language_index) {
|
||||
WNDCLASS wc = {};
|
||||
wc.lpfnWndProc = MsgWndProc;
|
||||
wc.hInstance = GetModuleHandle(nullptr);
|
||||
wc.lpszClassName = L"TrayMessageWindow";
|
||||
RegisterClass(&wc);
|
||||
|
||||
// create a message-only window to receive tray messages
|
||||
hwnd_message_only_ =
|
||||
CreateWindowEx(0, wc.lpszClassName, L"TrayMsg", 0, 0, 0, 0, 0,
|
||||
HWND_MESSAGE, nullptr, wc.hInstance, nullptr);
|
||||
|
||||
// store pointer to this WinTray instance in window data
|
||||
SetWindowLongPtr(hwnd_message_only_, GWLP_USERDATA,
|
||||
reinterpret_cast<LONG_PTR>(this));
|
||||
|
||||
// initialize NOTIFYICONDATA structure
|
||||
ZeroMemory(&nid_, sizeof(nid_));
|
||||
nid_.cbSize = sizeof(nid_);
|
||||
nid_.hWnd = hwnd_message_only_;
|
||||
nid_.uID = 1;
|
||||
nid_.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
nid_.uCallbackMessage = WM_TRAY_CALLBACK;
|
||||
nid_.hIcon = icon_;
|
||||
wcsncpy_s(nid_.szTip, tip_.c_str(), _TRUNCATE);
|
||||
}
|
||||
|
||||
WinTray::~WinTray() {
|
||||
RemoveTrayIcon();
|
||||
if (hwnd_message_only_) DestroyWindow(hwnd_message_only_);
|
||||
}
|
||||
|
||||
void WinTray::MinimizeToTray() {
|
||||
Shell_NotifyIcon(NIM_ADD, &nid_);
|
||||
// hide application window
|
||||
ShowWindow(app_hwnd_, SW_HIDE);
|
||||
}
|
||||
|
||||
void WinTray::RemoveTrayIcon() { Shell_NotifyIcon(NIM_DELETE, &nid_); }
|
||||
|
||||
bool WinTray::HandleTrayMessage(MSG* msg) {
|
||||
if (!msg || msg->message != WM_TRAY_CALLBACK) return false;
|
||||
|
||||
switch (LOWORD(msg->lParam)) {
|
||||
case WM_LBUTTONDBLCLK:
|
||||
case WM_LBUTTONUP: {
|
||||
ShowWindow(app_hwnd_, SW_SHOW);
|
||||
SetForegroundWindow(app_hwnd_);
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_RBUTTONUP: {
|
||||
POINT pt;
|
||||
GetCursorPos(&pt);
|
||||
HMENU menu = CreatePopupMenu();
|
||||
AppendMenuW(menu, MF_STRING, 1001,
|
||||
localization::exit_program[language_index_]);
|
||||
|
||||
SetForegroundWindow(hwnd_message_only_);
|
||||
int cmd =
|
||||
TrackPopupMenu(menu, TPM_RETURNCMD | TPM_NONOTIFY | TPM_LEFTALIGN,
|
||||
pt.x, pt.y, 0, hwnd_message_only_, nullptr);
|
||||
DestroyMenu(menu);
|
||||
|
||||
// handle menu command
|
||||
if (cmd == 1001) {
|
||||
// exit application
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
} else if (cmd == 1002) {
|
||||
ShowWindow(app_hwnd_, SW_SHOW); // show main window
|
||||
SetForegroundWindow(app_hwnd_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
36
src/gui/tray/win_tray.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2025-10-22
|
||||
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _WIN_TRAY_H_
|
||||
#define _WIN_TRAY_H_
|
||||
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#define WM_TRAY_CALLBACK (WM_USER + 1)
|
||||
|
||||
class WinTray {
|
||||
public:
|
||||
WinTray(HWND app_hwnd, HICON icon, const std::wstring& tooltip,
|
||||
int language_index);
|
||||
~WinTray();
|
||||
|
||||
void MinimizeToTray();
|
||||
void RemoveTrayIcon();
|
||||
bool HandleTrayMessage(MSG* msg);
|
||||
|
||||
private:
|
||||
HWND app_hwnd_;
|
||||
HWND hwnd_message_only_;
|
||||
HICON icon_;
|
||||
std::wstring tip_;
|
||||
int language_index_;
|
||||
NOTIFYICONDATA nid_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -27,15 +27,21 @@ int Render::AboutWindow() {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
|
||||
std::string version;
|
||||
#ifdef RD_VERSION
|
||||
version = RD_VERSION;
|
||||
#ifdef CROSSDESK_VERSION
|
||||
version = CROSSDESK_VERSION;
|
||||
#else
|
||||
version = "Unknown";
|
||||
#endif
|
||||
|
||||
std::string text =
|
||||
localization::version[localization_language_index_] + ": " + version;
|
||||
std::string text = localization::version[localization_language_index_] +
|
||||
": CrossDesk v" + version;
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::Text("");
|
||||
|
||||
std::string copyright_text = "© 2025 by JUNKUN DI. All rights reserved.";
|
||||
std::string license_text = "Licensed under GNU LGPL v3.";
|
||||
ImGui::Text("%s", copyright_text.c_str());
|
||||
ImGui::Text("%s", license_text.c_str());
|
||||
|
||||
ImGui::SetCursorPosX(about_window_width_ * 0.42f);
|
||||
ImGui::SetCursorPosY(about_window_height_ * 0.75f);
|
||||
171
src/gui/windows/connection_status_window.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
bool Render::ConnectionStatusWindow(
|
||||
std::shared_ptr<SubStreamWindowProperties> &props) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
bool ret_flag = false;
|
||||
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
connection_status_window_width_) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
connection_status_window_height_) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
|
||||
connection_status_window_height_));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
|
||||
ImGui::Begin("ConnectionStatusWindow", nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
std::string text;
|
||||
|
||||
if (ConnectionStatus::Connecting == props->connection_status_) {
|
||||
text = localization::p2p_connecting[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
} else if (ConnectionStatus::Connected == props->connection_status_) {
|
||||
text = localization::p2p_connected[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Disconnected == props->connection_status_) {
|
||||
text = localization::p2p_disconnected[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Failed == props->connection_status_) {
|
||||
text = localization::p2p_failed[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Closed == props->connection_status_) {
|
||||
text = localization::p2p_closed[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::IncorrectPassword == props->connection_status_) {
|
||||
if (!password_validating_) {
|
||||
if (password_validating_time_ == 1) {
|
||||
text = localization::input_password[localization_language_index_];
|
||||
} else {
|
||||
text = localization::reinput_password[localization_language_index_];
|
||||
}
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.4f);
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
|
||||
if (focus_on_input_widget_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
focus_on_input_widget_ = false;
|
||||
}
|
||||
|
||||
ImGui::InputText("##password", props->remote_password_,
|
||||
IM_ARRAYSIZE(props->remote_password_),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
ImGui::SetWindowFontScale(0.4f);
|
||||
|
||||
ImVec2 text_size = ImGui::CalcTextSize(
|
||||
localization::remember_password[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SetCursorPosX((window_width - text_size.x) * 0.5f - 13.0f);
|
||||
ImGui::Checkbox(
|
||||
localization::remember_password[localization_language_index_].c_str(),
|
||||
&(props->remember_password_));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SetCursorPosX(window_width * 0.315f);
|
||||
ImGui::SetCursorPosY(window_height * 0.75f);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
show_connection_status_window_ = true;
|
||||
password_validating_ = true;
|
||||
props->rejoin_ = true;
|
||||
need_to_rejoin_ = true;
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
memset(props->remote_password_, 0, sizeof(props->remote_password_));
|
||||
show_connection_status_window_ = false;
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
} else if (password_validating_) {
|
||||
text = localization::validate_password[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
}
|
||||
} else if (ConnectionStatus::NoSuchTransmissionId ==
|
||||
props->connection_status_) {
|
||||
text = localization::no_such_id[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
show_connection_status_window_ = false;
|
||||
re_enter_remote_id_ = true;
|
||||
DestroyPeer(&props->peer_);
|
||||
ret_flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.2f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return ret_flag;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "layout_style.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
@@ -6,7 +6,7 @@
|
||||
int Render::SettingWindow() {
|
||||
if (show_settings_window_) {
|
||||
if (settings_window_pos_reset_) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
@@ -36,6 +36,9 @@ int Render::SettingWindow() {
|
||||
|
||||
// Settings
|
||||
{
|
||||
static int settings_items_padding = 30;
|
||||
int settings_items_offset = 0;
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
@@ -49,11 +52,12 @@ int Render::SettingWindow() {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
{
|
||||
const char *language_items[] = {
|
||||
const char* language_items[] = {
|
||||
localization::language_zh[localization_language_index_].c_str(),
|
||||
localization::language_en[localization_language_index_].c_str()};
|
||||
|
||||
ImGui::SetCursorPosY(32);
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s", localization::language[localization_language_index_].c_str());
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
@@ -61,7 +65,7 @@ int Render::SettingWindow() {
|
||||
} else {
|
||||
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(30);
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::Combo("##language", &language_button_value_, language_items,
|
||||
@@ -75,7 +79,7 @@ int Render::SettingWindow() {
|
||||
}
|
||||
|
||||
{
|
||||
const char *video_quality_items[] = {
|
||||
const char* video_quality_items[] = {
|
||||
localization::video_quality_high[localization_language_index_]
|
||||
.c_str(),
|
||||
localization::video_quality_medium[localization_language_index_]
|
||||
@@ -83,7 +87,8 @@ int Render::SettingWindow() {
|
||||
localization::video_quality_low[localization_language_index_]
|
||||
.c_str()};
|
||||
|
||||
ImGui::SetCursorPosY(62);
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::video_quality[localization_language_index_].c_str());
|
||||
@@ -93,7 +98,7 @@ int Render::SettingWindow() {
|
||||
} else {
|
||||
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(60);
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::Combo("##video_quality", &video_quality_button_value_,
|
||||
@@ -103,11 +108,36 @@ int Render::SettingWindow() {
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
const char *video_encode_format_items[] = {
|
||||
localization::av1[localization_language_index_].c_str(),
|
||||
localization::h264[localization_language_index_].c_str()};
|
||||
const char* video_frame_rate_items[] = {"30 fps", "60 fps"};
|
||||
|
||||
ImGui::SetCursorPosY(92);
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text("%s",
|
||||
localization::video_frame_rate[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::Combo("##video_frame_rate", &video_frame_rate_button_value_,
|
||||
video_frame_rate_items,
|
||||
IM_ARRAYSIZE(video_frame_rate_items));
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
const char* video_encode_format_items[] = {
|
||||
localization::h264[localization_language_index_].c_str(),
|
||||
localization::av1[localization_language_index_].c_str()};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::video_encode_format[localization_language_index_]
|
||||
@@ -118,7 +148,7 @@ int Render::SettingWindow() {
|
||||
} else {
|
||||
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(90);
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::Combo(
|
||||
@@ -129,7 +159,8 @@ int Render::SettingWindow() {
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
ImGui::SetCursorPosY(122);
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text("%s", localization::enable_hardware_video_codec
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
@@ -139,7 +170,7 @@ int Render::SettingWindow() {
|
||||
} else {
|
||||
ImGui::SetCursorPosX(ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(120);
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::Checkbox("##enable_hardware_video_codec",
|
||||
&enable_hardware_video_codec_);
|
||||
}
|
||||
@@ -147,7 +178,8 @@ int Render::SettingWindow() {
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
ImGui::SetCursorPosY(152);
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::enable_turn[localization_language_index_].c_str());
|
||||
@@ -157,10 +189,70 @@ int Render::SettingWindow() {
|
||||
} else {
|
||||
ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(150);
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::Checkbox("##enable_turn", &enable_turn_);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::enable_srtp[localization_language_index_].c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(ENABLE_SRTP_CHECKBOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(ENABLE_SRTP_CHECKBOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::Checkbox("##enable_srtp", &enable_srtp_);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 1);
|
||||
|
||||
if (ImGui::Button(localization::self_hosted_server_config
|
||||
[localization_language_index_]
|
||||
.c_str())) {
|
||||
show_self_hosted_server_config_window_ = true;
|
||||
}
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::Checkbox("##enable_self_hosted_server",
|
||||
&enable_self_hosted_server_);
|
||||
}
|
||||
#if _WIN32
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
|
||||
ImGui::Text("%s",
|
||||
localization::minimize_to_tray[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(ENABLE_MINIZE_TO_TRAY_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(ENABLE_MINIZE_TO_TRAY_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::Checkbox("##enable_minimize_to_tray_",
|
||||
&enable_minimize_to_tray_);
|
||||
}
|
||||
#endif
|
||||
if (stream_window_inited_) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
@@ -170,19 +262,22 @@ int Render::SettingWindow() {
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(190.0f);
|
||||
|
||||
settings_items_offset += settings_items_padding + 10;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
// OK
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = false;
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
// Language
|
||||
if (language_button_value_ == 0) {
|
||||
config_center_.SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
|
||||
config_center_->SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
|
||||
} else {
|
||||
config_center_.SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
|
||||
config_center_->SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
|
||||
}
|
||||
language_button_value_last_ = language_button_value_;
|
||||
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
|
||||
@@ -192,42 +287,55 @@ int Render::SettingWindow() {
|
||||
|
||||
// Video quality
|
||||
if (video_quality_button_value_ == 0) {
|
||||
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
|
||||
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
|
||||
} else if (video_quality_button_value_ == 1) {
|
||||
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
|
||||
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
|
||||
} else {
|
||||
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
|
||||
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
|
||||
}
|
||||
video_quality_button_value_last_ = video_quality_button_value_;
|
||||
|
||||
// Video encode format
|
||||
if (video_encode_format_button_value_ == 0) {
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
|
||||
} else if (video_encode_format_button_value_ == 1) {
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
config_center_->SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::H264);
|
||||
} else if (video_encode_format_button_value_ == 1) {
|
||||
config_center_->SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
|
||||
}
|
||||
video_encode_format_button_value_last_ =
|
||||
video_encode_format_button_value_;
|
||||
|
||||
// Hardware video codec
|
||||
if (enable_hardware_video_codec_) {
|
||||
config_center_.SetHardwareVideoCodec(true);
|
||||
config_center_->SetHardwareVideoCodec(true);
|
||||
} else {
|
||||
config_center_.SetHardwareVideoCodec(false);
|
||||
config_center_->SetHardwareVideoCodec(false);
|
||||
}
|
||||
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
|
||||
|
||||
// TURN mode
|
||||
if (enable_turn_) {
|
||||
config_center_.SetTurn(true);
|
||||
config_center_->SetTurn(true);
|
||||
} else {
|
||||
config_center_.SetTurn(false);
|
||||
config_center_->SetTurn(false);
|
||||
}
|
||||
enable_turn_last_ = enable_turn_;
|
||||
|
||||
SaveSettingsIntoCacheFile();
|
||||
// SRTP
|
||||
if (enable_srtp_) {
|
||||
config_center_->SetSrtp(true);
|
||||
} else {
|
||||
config_center_->SetSrtp(false);
|
||||
}
|
||||
enable_srtp_last_ = enable_srtp_;
|
||||
|
||||
if (enable_self_hosted_server_) {
|
||||
config_center_->SetSelfHosted(true);
|
||||
} else {
|
||||
config_center_->SetSelfHosted(false);
|
||||
}
|
||||
|
||||
settings_window_pos_reset_ = true;
|
||||
|
||||
// Recreate peer instance
|
||||
@@ -236,7 +344,7 @@ int Render::SettingWindow() {
|
||||
// Recreate peer instance
|
||||
if (!stream_window_inited_) {
|
||||
LOG_INFO("Recreate peer instance");
|
||||
DestroyPeer(&peer_);
|
||||
CleanupPeers();
|
||||
CreateConnectionPeer();
|
||||
}
|
||||
}
|
||||
@@ -246,6 +354,8 @@ int Render::SettingWindow() {
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = false;
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
if (language_button_value_ != language_button_value_last_) {
|
||||
language_button_value_ = language_button_value_last_;
|
||||
}
|
||||
@@ -29,8 +29,20 @@ int Render::MainWindow() {
|
||||
RecentConnectionsWindow();
|
||||
StatusBar();
|
||||
|
||||
for (auto& it : client_properties_) {
|
||||
ConnectionStatusWindow(it.second);
|
||||
if (show_connection_status_window_) {
|
||||
for (auto it = client_properties_.begin();
|
||||
it != client_properties_.end();) {
|
||||
auto& props = it->second;
|
||||
if (focused_remote_id_ == props->remote_id_) {
|
||||
if (ConnectionStatusWindow(props)) {
|
||||
it = client_properties_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
270
src/gui/windows/server_settings_window.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
std::vector<std::string> GetRootEntries() {
|
||||
std::vector<std::string> roots;
|
||||
#ifdef _WIN32
|
||||
DWORD mask = GetLogicalDrives();
|
||||
for (char letter = 'A'; letter <= 'Z'; ++letter) {
|
||||
if (mask & 1) {
|
||||
roots.push_back(std::string(1, letter) + ":\\");
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
#else
|
||||
roots.push_back("/");
|
||||
#endif
|
||||
return roots;
|
||||
}
|
||||
|
||||
int Render::ShowSimpleFileBrowser() {
|
||||
std::string display_text;
|
||||
if (!selected_file_.empty()) {
|
||||
display_text = std::filesystem::path(selected_file_).filename().string();
|
||||
} else if (selected_current_file_path_ != "Root") {
|
||||
display_text =
|
||||
std::filesystem::path(selected_current_file_path_).filename().string();
|
||||
if (display_text.empty()) {
|
||||
display_text = selected_current_file_path_;
|
||||
}
|
||||
}
|
||||
|
||||
if (display_text.empty()) {
|
||||
display_text =
|
||||
localization::select_a_file[localization_language_index_].c_str();
|
||||
}
|
||||
|
||||
if (ImGui::BeginCombo("##select_a_file", display_text.c_str())) {
|
||||
if (selected_current_file_path_ == "Root" ||
|
||||
!std::filesystem::exists(selected_current_file_path_) ||
|
||||
!std::filesystem::is_directory(selected_current_file_path_)) {
|
||||
auto roots = GetRootEntries();
|
||||
for (const auto& root : roots) {
|
||||
if (ImGui::Selectable(root.c_str())) {
|
||||
selected_current_file_path_ = root;
|
||||
selected_file_.clear();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::filesystem::path p(selected_current_file_path_);
|
||||
|
||||
if (ImGui::Selectable("..")) {
|
||||
if (p.has_parent_path() && p != p.root_path())
|
||||
selected_current_file_path_ = p.parent_path().string();
|
||||
else
|
||||
selected_current_file_path_ = "Root";
|
||||
selected_file_.clear();
|
||||
}
|
||||
|
||||
try {
|
||||
for (const auto& entry :
|
||||
std::filesystem::directory_iterator(selected_current_file_path_)) {
|
||||
std::string name = entry.path().filename().string();
|
||||
if (entry.is_directory()) {
|
||||
if (ImGui::Selectable(name.c_str())) {
|
||||
selected_current_file_path_ = entry.path().string();
|
||||
selected_file_.clear();
|
||||
}
|
||||
} else {
|
||||
if (ImGui::Selectable(name.c_str())) {
|
||||
selected_file_ = entry.path().string();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Error: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Render::SelfHostedServerWindow() {
|
||||
if (show_self_hosted_server_config_window_) {
|
||||
if (self_hosted_server_config_window_pos_reset_) {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN,
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN));
|
||||
} else {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN,
|
||||
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN));
|
||||
}
|
||||
|
||||
self_hosted_server_config_window_pos_reset_ = false;
|
||||
}
|
||||
|
||||
// Settings
|
||||
{
|
||||
static int settings_items_padding = 30;
|
||||
int settings_items_offset = 0;
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImGui::Begin(localization::self_hosted_server_settings
|
||||
[localization_language_index_]
|
||||
.c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::Text("%s", localization::self_hosted_server_address
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::InputText("##signal_server_ip_tmp_", signal_server_ip_tmp_,
|
||||
IM_ARRAYSIZE(signal_server_ip_tmp_),
|
||||
ImGuiInputTextFlags_AlwaysOverwrite);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::self_hosted_server_port[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::InputText("##signal_server_port_tmp_", signal_server_port_tmp_,
|
||||
IM_ARRAYSIZE(signal_server_port_tmp_));
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::Text("%s", localization::self_hosted_server_certificate_path
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
|
||||
|
||||
ShowSimpleFileBrowser();
|
||||
}
|
||||
|
||||
if (stream_window_inited_) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN);
|
||||
}
|
||||
|
||||
settings_items_offset += settings_items_padding + 10;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
// OK
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str())) {
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
config_center_->SetServerHost(signal_server_ip_tmp_);
|
||||
config_center_->SetServerPort(atoi(signal_server_port_tmp_));
|
||||
config_center_->SetCertFilePath(selected_file_);
|
||||
strncpy(signal_server_ip_, signal_server_ip_tmp_,
|
||||
sizeof(signal_server_ip_) - 1);
|
||||
signal_server_ip_[sizeof(signal_server_ip_) - 1] = '\0';
|
||||
strncpy(signal_server_port_, signal_server_port_tmp_,
|
||||
sizeof(signal_server_port_) - 1);
|
||||
signal_server_port_[sizeof(signal_server_port_) - 1] = '\0';
|
||||
strncpy(cert_file_path_, selected_file_.c_str(),
|
||||
sizeof(cert_file_path_) - 1);
|
||||
cert_file_path_[sizeof(cert_file_path_) - 1] = '\0';
|
||||
|
||||
self_hosted_server_config_window_pos_reset_ = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// Cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
self_hosted_server_config_window_pos_reset_ = true;
|
||||
|
||||
strncpy(signal_server_ip_tmp_, signal_server_ip_,
|
||||
sizeof(signal_server_ip_tmp_) - 1);
|
||||
signal_server_ip_tmp_[sizeof(signal_server_ip_tmp_) - 1] = '\0';
|
||||
strncpy(signal_server_port_tmp_, signal_server_port_,
|
||||
sizeof(signal_server_port_tmp_) - 1);
|
||||
signal_server_port_tmp_[sizeof(signal_server_port_tmp_) - 1] = '\0';
|
||||
config_center_->SetServerHost(signal_server_ip_tmp_);
|
||||
config_center_->SetServerPort(atoi(signal_server_port_tmp_));
|
||||
selected_file_.clear();
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -34,7 +34,7 @@ void Render::CloseTab(decltype(client_properties_)::iterator& it) {
|
||||
it = client_properties_.erase(it);
|
||||
if (client_properties_.empty()) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
@@ -86,8 +86,11 @@ int Render::StreamWindow() {
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(0.6f);
|
||||
if (ImGui::BeginTabItem(props->remote_id_.c_str(),
|
||||
&props->tab_opened_)) {
|
||||
std::string tab_label =
|
||||
enable_srtp_
|
||||
? std::string(ICON_FA_SHIELD_HALVED) + " " + props->remote_id_
|
||||
: props->remote_id_;
|
||||
if (ImGui::BeginTabItem(tab_label.c_str(), &props->tab_opened_)) {
|
||||
props->tab_selected_ = true;
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
@@ -114,11 +117,13 @@ int Render::StreamWindow() {
|
||||
|
||||
ControlWindow(props);
|
||||
|
||||
focused_remote_id_ = props->remote_id_;
|
||||
|
||||
if (!props->peer_) {
|
||||
it = client_properties_.erase(it);
|
||||
if (client_properties_.empty()) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
} else {
|
||||
@@ -173,11 +178,11 @@ int Render::StreamWindow() {
|
||||
|
||||
if (!props->peer_) {
|
||||
fullscreen_button_pressed_ = false;
|
||||
SDL_SetWindowFullscreen(stream_window_, SDL_FALSE);
|
||||
SDL_SetWindowFullscreen(stream_window_, false);
|
||||
it = client_properties_.erase(it);
|
||||
if (client_properties_.empty()) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
} else {
|
||||
@@ -52,7 +52,7 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
||||
std::atomic<bool> running_{false};
|
||||
std::atomic<bool> paused_{false};
|
||||
std::atomic<int> monitor_index_{0};
|
||||
int fps_ = 30;
|
||||
int fps_ = 60;
|
||||
cb_desktop_data callback_;
|
||||
std::vector<DisplayInfo> display_info_list_;
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer {
|
||||
unsigned char *nv12_frame_ = nullptr;
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
int fps_ = 30;
|
||||
int fps_ = 60;
|
||||
|
||||
public:
|
||||
// Called by SckHelper when shareable content is returned by ScreenCaptureKit. `content` will be
|
||||
|
||||
@@ -32,7 +32,7 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
||||
|
||||
int SwitchTo(int monitor_index);
|
||||
|
||||
void OnFrame(const WgcSession::wgc_session_frame &frame, int id);
|
||||
void OnFrame(const WgcSession::wgc_session_frame& frame, int id);
|
||||
|
||||
protected:
|
||||
void CleanUp();
|
||||
@@ -57,12 +57,12 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
||||
std::atomic_bool running_;
|
||||
std::atomic_bool inited_;
|
||||
|
||||
int fps_;
|
||||
int fps_ = 60;
|
||||
|
||||
cb_desktop_data on_data_ = nullptr;
|
||||
|
||||
unsigned char *nv12_frame_ = nullptr;
|
||||
unsigned char *nv12_frame_scaled_ = nullptr;
|
||||
unsigned char* nv12_frame_ = nullptr;
|
||||
unsigned char* nv12_frame_scaled_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,179 +0,0 @@
|
||||
#include "layout_style.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int Render::ConnectionStatusWindow(
|
||||
std::shared_ptr<SubStreamWindowProperties> &props) {
|
||||
if (show_connection_status_window_) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
connection_status_window_width_) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
connection_status_window_height_) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
|
||||
connection_status_window_height_));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
|
||||
ImGui::Begin("ConnectionStatusWindow", nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
std::string text;
|
||||
|
||||
if (ConnectionStatus::Connecting == props->connection_status_) {
|
||||
text = localization::p2p_connecting[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
} else if (ConnectionStatus::Connected == props->connection_status_) {
|
||||
text = localization::p2p_connected[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Disconnected == props->connection_status_) {
|
||||
text = localization::p2p_disconnected[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Failed == props->connection_status_) {
|
||||
text = localization::p2p_failed[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Closed == props->connection_status_) {
|
||||
text = localization::p2p_closed[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::IncorrectPassword ==
|
||||
props->connection_status_) {
|
||||
if (!password_validating_) {
|
||||
if (password_validating_time_ == 1) {
|
||||
text = localization::input_password[localization_language_index_];
|
||||
} else {
|
||||
text = localization::reinput_password[localization_language_index_];
|
||||
}
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.4f);
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
|
||||
if (focus_on_input_widget_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
focus_on_input_widget_ = false;
|
||||
}
|
||||
|
||||
ImGui::InputText("##password", props->remote_password_,
|
||||
IM_ARRAYSIZE(props->remote_password_),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
ImGui::SetWindowFontScale(0.4f);
|
||||
|
||||
ImVec2 text_size = ImGui::CalcTextSize(
|
||||
localization::remember_password[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SetCursorPosX((window_width - text_size.x) * 0.5f - 13.0f);
|
||||
ImGui::Checkbox(
|
||||
localization::remember_password[localization_language_index_]
|
||||
.c_str(),
|
||||
&(props->remember_password_));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SetCursorPosX(window_width * 0.315f);
|
||||
ImGui::SetCursorPosY(window_height * 0.75f);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
show_connection_status_window_ = true;
|
||||
password_validating_ = true;
|
||||
props->rejoin_ = true;
|
||||
need_to_rejoin_ = true;
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
memset(props->remote_password_, 0, sizeof(props->remote_password_));
|
||||
show_connection_status_window_ = false;
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
} else if (password_validating_) {
|
||||
text = localization::validate_password[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
}
|
||||
} else if (ConnectionStatus::NoSuchTransmissionId ==
|
||||
props->connection_status_) {
|
||||
text = localization::no_such_id[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
|
||||
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
show_connection_status_window_ = false;
|
||||
re_enter_remote_id_ = true;
|
||||
DestroyPeer(&props->peer_);
|
||||
client_properties_.erase(props->remote_id_);
|
||||
}
|
||||
}
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.2f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-06-14
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LAYOUT_STYLE_H_
|
||||
#define _LAYOUT_STYLE_H_
|
||||
|
||||
#define MENU_WINDOW_WIDTH_CN 300
|
||||
#define MENU_WINDOW_HEIGHT_CN 280
|
||||
#define LOCAL_WINDOW_WIDTH_CN 300
|
||||
#define LOCAL_WINDOW_HEIGHT_CN 280
|
||||
#define REMOTE_WINDOW_WIDTH_CN 300
|
||||
#define REMOTE_WINDOW_HEIGHT_CN 280
|
||||
#define MENU_WINDOW_WIDTH_EN 190
|
||||
#define MENU_WINDOW_HEIGHT_EN 245
|
||||
#define IPUT_WINDOW_WIDTH 160
|
||||
#define INPUT_WINDOW_PADDING_CN 66
|
||||
#define INPUT_WINDOW_PADDING_EN 96
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 181
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 228
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 220
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 220
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 147
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 147
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 147
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 151
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 198
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_CN 151
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_EN 198
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 73
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 55
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 78
|
||||
|
||||
#endif
|
||||
@@ -38,9 +38,10 @@ bool LoadTextureFromMemory(const void* data, size_t data_size,
|
||||
}
|
||||
|
||||
// ABGR
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(
|
||||
(void*)image_data, image_width, image_height, channels * 8,
|
||||
channels * image_width, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
|
||||
int pitch = image_width * channels;
|
||||
SDL_Surface* surface =
|
||||
SDL_CreateSurfaceFrom(image_width, image_height, SDL_PIXELFORMAT_RGBA32,
|
||||
(void*)image_data, pitch);
|
||||
if (surface == nullptr) {
|
||||
LOG_ERROR("Failed to create SDL surface: [{}]", SDL_GetError());
|
||||
return false;
|
||||
@@ -55,7 +56,7 @@ bool LoadTextureFromMemory(const void* data, size_t data_size,
|
||||
*out_width = image_width;
|
||||
*out_height = image_height;
|
||||
|
||||
SDL_FreeSurface(surface);
|
||||
SDL_DestroySurface(surface);
|
||||
stbi_image_free(image_data);
|
||||
|
||||
return true;
|
||||
@@ -7,7 +7,7 @@
|
||||
#ifndef _THUMBNAIL_H_
|
||||
#define _THUMBNAIL_H_
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
2
thirdparty/minirtc
vendored
65
xmake.lua
@@ -1,8 +1,11 @@
|
||||
set_project("crossdesk")
|
||||
set_license("LGPL-3.0")
|
||||
|
||||
set_version("0.0.1")
|
||||
add_defines("RD_VERSION=\"0.0.1\"");
|
||||
option("CROSSDESK_VERSION")
|
||||
set_default("0.0.0")
|
||||
set_showmenu(true)
|
||||
set_description("Set CROSSDESK_VERSION for build")
|
||||
option_end()
|
||||
|
||||
add_rules("mode.release", "mode.debug")
|
||||
set_languages("c++17")
|
||||
@@ -17,26 +20,24 @@ if is_mode("debug") then
|
||||
end
|
||||
|
||||
add_requires("spdlog 1.14.1", {system = false})
|
||||
add_requires("imgui v1.91.5-docking", {configs = {sdl2 = true, sdl2_renderer = true}})
|
||||
add_requires("imgui v1.91.5-docking", {configs = {sdl3 = true, sdl3_renderer = true}})
|
||||
add_requires("openssl3 3.3.2", {system = false})
|
||||
|
||||
add_requires("libsdl2", {configs = {pulseaudio = true}})
|
||||
add_packages("libsdl2")
|
||||
|
||||
if is_os("windows") then
|
||||
add_requires("libyuv", "miniaudio 0.11.21")
|
||||
add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32",
|
||||
"SDL2-static", "SDL2main", "gdi32", "winmm", "setupapi", "version",
|
||||
"SDL3-static", "gdi32", "winmm", "setupapi", "version",
|
||||
"Imm32", "iphlpapi")
|
||||
add_cxflags("/WX")
|
||||
set_runtimes("MT")
|
||||
elseif is_os("linux") then
|
||||
add_links("pulse-simple", "pulse")
|
||||
add_requires("libyuv")
|
||||
add_syslinks("pthread", "dl")
|
||||
add_links("SDL2", "asound", "X11", "Xtst", "Xrandr")
|
||||
add_links("SDL3", "asound", "X11", "Xtst", "Xrandr")
|
||||
add_cxflags("-Wno-unused-variable")
|
||||
elseif is_os("macosx") then
|
||||
add_links("SDL2", "SDL2main")
|
||||
add_links("SDL3")
|
||||
add_ldflags("-Wl,-ld_classic")
|
||||
add_cxflags("-Wno-unused-variable")
|
||||
add_frameworks("OpenGL", "IOSurface", "ScreenCaptureKit", "AVFoundation",
|
||||
@@ -122,33 +123,43 @@ target("device_controller")
|
||||
"src/device_controller/keyboard/linux", {public = true})
|
||||
end
|
||||
|
||||
target("thumbnail")
|
||||
set_kind("object")
|
||||
add_packages("libyuv", "openssl3")
|
||||
add_deps("rd_log", "common")
|
||||
add_files("src/thumbnail/*.cpp")
|
||||
add_includedirs("src/thumbnail", {public = true})
|
||||
|
||||
target("config_center")
|
||||
set_kind("object")
|
||||
add_deps("rd_log")
|
||||
add_files("src/config_center/*.cpp")
|
||||
add_includedirs("src/config_center", {public = true})
|
||||
|
||||
target("localization")
|
||||
target("assets")
|
||||
set_kind("headeronly")
|
||||
add_includedirs("src/localization", {public = true})
|
||||
add_includedirs("src/gui/assets/localization",
|
||||
"src/gui/assets/fonts",
|
||||
"src/gui/assets/icons",
|
||||
"src/gui/assets/layouts", {public = true})
|
||||
|
||||
target("single_window")
|
||||
target("gui")
|
||||
set_kind("object")
|
||||
add_packages("libyuv", "openssl3")
|
||||
add_deps("rd_log", "common", "localization", "config_center", "minirtc",
|
||||
"path_manager", "screen_capturer", "speaker_capturer", "device_controller")
|
||||
add_files("src/single_window/*.cpp")
|
||||
add_includedirs("src/single_window", {public = true})
|
||||
add_includedirs("fonts", {public = true})
|
||||
add_packages("libyuv")
|
||||
add_defines("CROSSDESK_VERSION=\"" .. (get_config("CROSSDESK_VERSION") or "Unknown") .. "\"")
|
||||
add_deps("rd_log", "common", "assets", "config_center", "minirtc",
|
||||
"path_manager", "screen_capturer", "speaker_capturer",
|
||||
"device_controller", "thumbnail")
|
||||
add_files("src/gui/*.cpp", "src/gui/panels/*.cpp", "src/gui/toolbars/*.cpp",
|
||||
"src/gui/windows/*.cpp")
|
||||
add_includedirs("src/gui", "src/gui/panels", "src/gui/toolbars",
|
||||
"src/gui/windows", {public = true})
|
||||
if is_os("windows") then
|
||||
add_files("src/gui/tray/*.cpp")
|
||||
add_includedirs("src/gui/tray", {public = true})
|
||||
end
|
||||
|
||||
target("crossdesk")
|
||||
set_kind("binary")
|
||||
add_deps("rd_log", "common", "single_window")
|
||||
if is_os("windows") then
|
||||
add_files("icons/app.rc")
|
||||
elseif is_os("macosx") then
|
||||
-- add_rules("xcode.application")
|
||||
-- add_files("Info.plist")
|
||||
elseif is_os("linux") then
|
||||
end
|
||||
add_files("src/gui/main.cpp")
|
||||
add_deps("rd_log", "common", "gui")
|
||||
add_files("src/app/main.cpp")
|
||||