24 Commits

Author SHA1 Message Date
dijunkun
3b34c26555 [feat] enable custom configuration of Coturn server port 2025-10-27 16:04:40 +08:00
dijunkun
b668b3c936 [chore] update README 2025-10-27 13:42:49 +08:00
dijunkun
cc19ec125a [ci] update close-issue.yml 2025-10-27 11:04:36 +08:00
dijunkun
ffa77fdf44 Merge branch 'run-in-bg' into self-hosted-server 2025-10-27 10:41:16 +08:00
dijunkun
47cf806532 [ci] add permissions to close-issue.yml 2025-10-25 17:06:14 +08:00
dijunkun
911dce2e71 [feat] optimize certificate selection table 2025-10-24 17:39:25 +08:00
dijunkun
9f80d4f69d Merge branch 'self-hosted-server' into run-in-bg 2025-10-24 14:16:18 +08:00
dijunkun
cba644f055 [fix] fix TURN server authentication credentials, fixes #8 2025-10-24 11:41:23 +08:00
dijunkun
f733fe9e49 Merge branch 'self-hosted-server' into run-in-bg 2025-10-24 11:05:07 +08:00
dijunkun
27263fe1db [ci] fix permission issue in close inactive issues script by updating token usage 2025-10-24 10:57:11 +08:00
dijunkun
698bf72a6c Merge branch 'self-hosted-server' into run-in-bg 2025-10-24 10:05:54 +08:00
dijunkun
0bd27d0b17 [ci] use GITHUB_SHORT_SHA instead of build number in version 2025-10-24 02:09:50 +08:00
dijunkun
ee5612da8b [ci] resolve compilation issue 2025-10-23 23:30:52 +08:00
dijunkun
c7a2023c88 [ci] add build ID to version number 2025-10-23 23:18:44 +08:00
dijunkun
c031a8c145 [fix] adjust settings window height for different platforms 2025-10-23 23:04:31 +08:00
dijunkun
0bf83f07ad [fix] correct version display in about window 2025-10-23 23:01:38 +08:00
dijunkun
3638b712bd [ci] update close-issue.yml 2025-10-23 21:20:10 +08:00
dijunkun
b2ab940f20 [feat] use no close select table 2025-10-23 17:55:30 +08:00
dijunkun
17f9536476 [chore] update README 2025-10-23 10:37:45 +08:00
dijunkun
0ef51e3faf [feat] add FAQ.md 2025-10-23 10:33:39 +08:00
dijunkun
cccf5dadb2 Merge branch 'run-in-bg' into self-hosted-server 2025-10-22 17:23:08 +08:00
dijunkun
2f0b0ffc22 [feat] add configuration to minimize to system tray when clicking the close button, refs #4 2025-10-22 17:21:47 +08:00
dijunkun
c7411b59f1 [chore] update README 2025-10-21 22:51:21 +08:00
dijunkun
8222782522 [chore] update README 2025-10-21 20:54:37 +08:00
20 changed files with 2632 additions and 1974 deletions

View File

@@ -34,10 +34,11 @@ jobs:
shell: bash shell: bash
id: set_deb_version id: set_deb_version
run: | run: |
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
LEGAL_VERSION="0.0.0-${VERSION_NUM}" LEGAL_VERSION="0.0.0-${VERSION_NUM}-${SHORT_SHA}"
else else
LEGAL_VERSION="${VERSION_NUM}" LEGAL_VERSION="${VERSION_NUM}-${SHORT_SHA}"
fi fi
echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV
@@ -52,6 +53,7 @@ jobs:
XMAKE_GLOBALDIR: /data XMAKE_GLOBALDIR: /data
run: | run: |
ls -la $XMAKE_GLOBALDIR ls -la $XMAKE_GLOBALDIR
xmake f --CROSSDESK_VERSION=${LEGAL_VERSION} --root -y
xmake b -vy --root crossdesk xmake b -vy --root crossdesk
- name: Decode and save certificate - name: Decode and save certificate
@@ -96,10 +98,11 @@ jobs:
shell: bash shell: bash
id: set_deb_version id: set_deb_version
run: | run: |
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
LEGAL_VERSION="0.0.0-${VERSION_NUM}" LEGAL_VERSION="0.0.0-${VERSION_NUM}-${SHORT_SHA}"
else else
LEGAL_VERSION="${VERSION_NUM}" LEGAL_VERSION="${VERSION_NUM}-${SHORT_SHA}"
fi fi
echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV
@@ -113,6 +116,7 @@ jobs:
CUDA_PATH: /usr/local/cuda CUDA_PATH: /usr/local/cuda
XMAKE_GLOBALDIR: /data XMAKE_GLOBALDIR: /data
run: | run: |
xmake f --CROSSDESK_VERSION=${LEGAL_VERSION} --root -y
xmake b -vy --root crossdesk xmake b -vy --root crossdesk
- name: Decode and save certificate - name: Decode and save certificate
@@ -155,7 +159,8 @@ jobs:
id: version id: version
run: | run: |
VERSION="${GITHUB_REF##*/}" VERSION="${GITHUB_REF##*/}"
VERSION_NUM="${VERSION#v}" 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}" >> $GITHUB_ENV
echo "VERSION_NUM=${VERSION_NUM}" echo "VERSION_NUM=${VERSION_NUM}"
@@ -177,7 +182,9 @@ jobs:
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: Build CrossDesk - name: Build CrossDesk
run: xmake b -vy crossdesk run: |
xmake f --CROSSDESK_VERSION=${VERSION_NUM} -y
xmake b -vy crossdesk
- name: Decode and save certificate - name: Decode and save certificate
shell: bash shell: bash
@@ -215,7 +222,8 @@ jobs:
$version = $ref -replace '^refs/(tags|heads)/', '' $version = $ref -replace '^refs/(tags|heads)/', ''
$version = $version -replace '^v', '' $version = $version -replace '^v', ''
$version = $version -replace '/', '-' $version = $version -replace '/', '-'
echo "VERSION_NUM=$version" >> $env:GITHUB_ENV $SHORT_SHA = $env:GITHUB_SHA.Substring(0,7)
echo "VERSION_NUM=$version-$SHORT_SHA" >> $env:GITHUB_ENV
- name: Cache xmake dependencies - name: Cache xmake dependencies
uses: actions/cache@v4 uses: actions/cache@v4
@@ -281,8 +289,10 @@ jobs:
copy "${{ github.workspace }}\scripts\windows\nsProcess.dll" $nsisPluginDir copy "${{ github.workspace }}\scripts\windows\nsProcess.dll" $nsisPluginDir
- name: Build CrossDesk - name: Build CrossDesk
run: xmake b -vy crossdesk run: |
xmake f --CROSSDESK_VERSION=${{ env.VERSION_NUM }} -y
xmake b -vy crossdesk
- name: Decode and save certificate - name: Decode and save certificate
shell: powershell shell: powershell
run: | run: |
@@ -321,7 +331,8 @@ jobs:
id: version id: version
run: | run: |
VERSION="${GITHUB_REF##*/}" VERSION="${GITHUB_REF##*/}"
VERSION_NUM="${VERSION#v}" SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
VERSION_NUM="${VERSION#v}-${SHORT_SHA}"
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT
- name: Rename artifacts - name: Rename artifacts

View File

@@ -5,6 +5,11 @@ on:
# run every day at midnight # run every day at midnight
- cron: "0 0 * * *" - cron: "0 0 * * *"
permissions:
issues: write
pull-requests: write
contents: read
jobs: jobs:
close_inactive_issues: close_inactive_issues:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -17,26 +22,59 @@ jobs:
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
state: 'open', state: 'open',
per_page: 100,
}); });
const now = new Date().getTime(); const now = new Date().getTime();
const inactivePeriod = 7 * 24 * 60 * 60 * 1000; // 7 days const inactivePeriod = 7 * 24 * 60 * 60 * 1000; // 7 days
for (const issue of issues) { for (const issue of issues) {
const lastUpdated = new Date(issue.updated_at).getTime(); // skip pull requests (they are also returned by listForRepo)
if (issue.pull_request) continue;
// if the issue hasn't been updated in the past week, close it
if (now - lastUpdated > inactivePeriod && issue.labels.length === 0) { // skip labeled issues
console.log(`Closing inactive issue: ${issue.number} (No labels)`); 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.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: "This issue has been automatically closed due to inactivity for 7 days."
});
await github.rest.issues.update({ await github.rest.issues.update({
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
issue_number: issue.number, issue_number: issue.number,
state: 'closed', state: 'closed',
}); });
} else if (issue.labels.length === 0) {
console.log(`Skipping issue ${issue.number} (No labels) as it has been recently updated.`);
} else { } else {
console.log(`Skipping issue ${issue.number} (Has labels).`); console.log(`Skipping issue #${issue.number} (Active within 7 days).`);
} }
} }
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -15,8 +15,9 @@ jobs:
- name: Set version number - name: Set version number
id: version id: version
run: | run: |
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
VERSION_NUM="${GITHUB_REF##*/}" VERSION_NUM="${GITHUB_REF##*/}"
VERSION_NUM="${VERSION_NUM#v}" VERSION_NUM="${VERSION_NUM#v}-${SHORT_SHA}"
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT
@@ -30,11 +31,13 @@ jobs:
- name: Update download links - name: Update download links
run: | run: |
cd pages 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 echo "Updating download links to ${VERSION_NUM}"
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-win-x64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.exe/crossdesk-win-x64-${VERSION_NUM}.exe/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-macos-x64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.pkg/crossdesk-macos-x64-${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-macos-arm64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.pkg/crossdesk-macos-arm64-${VERSION_NUM}.pkg/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 sed -E -i "s/crossdesk-linux-amd64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.deb/crossdesk-linux-amd64-${VERSION_NUM}.deb/g" index.html
sed -E -i "s/crossdesk-linux-arm64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.deb/crossdesk-linux-arm64-${VERSION_NUM}.deb/g" index.html
git diff index.html || true
- name: Commit & Push changes - name: Commit & Push changes
run: | run: |

View File

@@ -3,7 +3,7 @@
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)]() [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)]()
[![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
[![GitHub last commit](https://img.shields.io/github/last-commit/kunkundi/crossdesk)](https://github.com/kunkundi/crossdesk/commits/self-hosted-server) [![GitHub last commit](https://img.shields.io/github/last-commit/kunkundi/crossdesk)](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
[![Build Status](https://github.com/kunkundi/crossdesk/actions/workflows/build.yaml/badge.svg)](https://github.com/kunkundi/crossdesk/actions) [![Build Status](https://github.com/kunkundi/crossdesk/actions/workflows/build.yml/badge.svg)](https://github.com/kunkundi/crossdesk/actions)
[![Docker Pulls](https://img.shields.io/docker/pulls/crossdesk/crossdesk-server)](https://hub.docker.com/r/crossdesk/crossdesk-server/tags) [![Docker Pulls](https://img.shields.io/docker/pulls/crossdesk/crossdesk-server)](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
[![GitHub issues](https://img.shields.io/github/issues/kunkundi/crossdesk.svg)]() [![GitHub issues](https://img.shields.io/github/issues/kunkundi/crossdesk.svg)]()
[![GitHub stars](https://img.shields.io/github/stars/kunkundi/crossdesk.svg?style=social)]() [![GitHub stars](https://img.shields.io/github/stars/kunkundi/crossdesk.svg?style=social)]()
@@ -65,7 +65,7 @@ xmake r crossdesk
### 无 CUDA 环境下的开发支持 ### 无 CUDA 环境下的开发支持
对于未安装 **CUDA 环境** 的 Linux 开发者,这里提供了预配置的 [Ubuntu 22.04 Docker 镜像](https://hub.docker.com/r/crossdesk/ubuntu22.04)。该镜像内置必要的构建依赖,可在容器中开箱即用,无需额外配置即可直接编译项目。 对于**未安装 CUDA 环境的 Linux 开发者**,这里提供了预配置的 [Ubuntu 22.04 Docker 镜像](https://hub.docker.com/r/crossdesk/ubuntu22.04)。该镜像内置必要的构建依赖,可在容器中开箱即用,无需额外配置即可直接编译项目。
进入容器,下载工程后执行: 进入容器,下载工程后执行:
``` ```
@@ -75,7 +75,7 @@ export XMAKE_GLOBALDIR=/data
xmake b --root -vy crossdesk xmake b --root -vy crossdesk
``` ```
对于未安装 **CUDA 环境** 的 Windows 开发者,执行下面的命令安装 CUDA 编译环境: 对于**未安装 CUDA 环境的 Windows 开发者**,执行下面的命令安装 CUDA 编译环境:
``` ```
xmake require -vy "cuda 12.6.3" xmake require -vy "cuda 12.6.3"
``` ```
@@ -146,11 +146,14 @@ sudo docker run -d \
--network host \ --network host \
-e EXTERNAL_IP=xxx.xxx.xxx.xxx \ -e EXTERNAL_IP=xxx.xxx.xxx.xxx \
-e INTERNAL_IP=xxx.xxx.xxx.xxx \ -e INTERNAL_IP=xxx.xxx.xxx.xxx \
-e CROSSDESK_SERVER_PORT=9099 \ -e CROSSDESK_SERVER_PORT=xxxx \
-e COTURN_PORT=xxxx \
-e MIN_PORT=xxxxx \
-e MAX_PORT=xxxxx \
-v /path/to/your/certs:/crossdesk-server/certs \ -v /path/to/your/certs:/crossdesk-server/certs \
-v /path/to/your/db:/crossdesk-server/db \ -v /path/to/your/db:/crossdesk-server/db \
-v /path/to/your/logs:/crossdesk-server/logs \ -v /path/to/your/logs:/crossdesk-server/logs \
crossdesk/crossdesk-server:latest crossdesk/crossdesk-server:v1.0.0
``` ```
上述命令中,用户需注意的参数如下: 上述命令中,用户需注意的参数如下:
@@ -161,6 +164,10 @@ sudo docker run -d \
- CROSSDESK_SERVER_PORT自托管服务使用的端口对应 CrossDesk 客户端**自托管服务器配置**中填写的**服务器端口** - CROSSDESK_SERVER_PORT自托管服务使用的端口对应 CrossDesk 客户端**自托管服务器配置**中填写的**服务器端口**
- COTURN_PORT: COTURN 服务使用的端口, 对应 CrossDesk 客户端**自托管服务器配置**中填写的**中继服务端口**
- MIN_PORT/MAX_PORTCOTURN 服务使用的端口范围例如MIN_PORT=50000, MAX_PORT=60000范围可根据客户端数量调整。
- /path/to/your/certs证书文件目录 - /path/to/your/certs证书文件目录
- /path/to/your/dbCrossDesk Server 设备管理数据库 - /path/to/your/dbCrossDesk Server 设备管理数据库
@@ -169,7 +176,7 @@ sudo docker run -d \
**注意** **注意**
- **/path/to/your/ 是示例路径,请替换为你自己的实际路径。挂载的目录必须事先创建好,否则容器会报错。** - **/path/to/your/ 是示例路径,请替换为你自己的实际路径。挂载的目录必须事先创建好,否则容器会报错。**
- **服务器需开放端口3478/udp3478/tcp30000-60000/udpCROSSDESK_SERVER_PORT/tcp443/tcp。** - **服务器需开放端口3478/udp3478/tcpMIN_PORT-MAX_PORT/udpCROSSDESK_SERVER_PORT/tcp。**
## 证书文件 ## 证书文件
客户端需加载根证书文件,服务端需加载服务器私钥和服务器证书文件。 客户端需加载根证书文件,服务端需加载服务器私钥和服务器证书文件。
@@ -295,3 +302,6 @@ Generation complete. Deployment files::
7. 勾选使用**自托管服务器配置**,点击确认配置生效。<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> <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) 。

View File

@@ -3,7 +3,7 @@
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)]() [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)]()
[![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
[![GitHub last commit](https://img.shields.io/github/last-commit/kunkundi/crossdesk)](https://github.com/kunkundi/crossdesk/commits/self-hosted-server) [![GitHub last commit](https://img.shields.io/github/last-commit/kunkundi/crossdesk)](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
[![Build Status](https://github.com/kunkundi/crossdesk/actions/workflows/build.yaml/badge.svg)](https://github.com/kunkundi/crossdesk/actions) [![Build Status](https://github.com/kunkundi/crossdesk/actions/workflows/build.yml/badge.svg)](https://github.com/kunkundi/crossdesk/actions)
[![Docker Pulls](https://img.shields.io/docker/pulls/crossdesk/crossdesk-server)](https://hub.docker.com/r/crossdesk/crossdesk-server/tags) [![Docker Pulls](https://img.shields.io/docker/pulls/crossdesk/crossdesk-server)](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
[![GitHub issues](https://img.shields.io/github/issues/kunkundi/crossdesk.svg)]() [![GitHub issues](https://img.shields.io/github/issues/kunkundi/crossdesk.svg)]()
[![GitHub stars](https://img.shields.io/github/stars/kunkundi/crossdesk.svg?style=social)]() [![GitHub stars](https://img.shields.io/github/stars/kunkundi/crossdesk.svg?style=social)]()
@@ -65,7 +65,7 @@ xmake r crossdesk
#### Development Without CUDA Environment #### 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. 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. 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: After entering the container, download the project and run:
@@ -76,7 +76,7 @@ export XMAKE_GLOBALDIR=/data
xmake b --root -vy crossdesk xmake b --root -vy crossdesk
``` ```
For **Windows** developers without a **CUDA environment** installed, run the following command to install the CUDA build environment: 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" xmake require -vy "cuda 12.6.3"
``` ```
@@ -149,13 +149,16 @@ It is recommended to deploy CrossDesk Server using Docker.
sudo docker run -d \ sudo docker run -d \
--name crossdesk_server \ --name crossdesk_server \
--network host \ --network host \
-e EXTERNAL_IP=150.158.81.30 \ -e EXTERNAL_IP=xxx.xxx.xxx.xxx \
-e INTERNAL_IP=10.0.4.3 \ -e INTERNAL_IP=xxx.xxx.xxx.xxx \
-e CROSSDESK_SERVER_PORT=9099 \ -e CROSSDESK_SERVER_PORT=xxxx \
-e COTURN_PORT=xxxx \
-e MIN_PORT=xxxxx \
-e MAX_PORT=xxxxx \
-v /path/to/your/certs:/crossdesk-server/certs \ -v /path/to/your/certs:/crossdesk-server/certs \
-v /path/to/your/db:/crossdesk-server/db \ -v /path/to/your/db:/crossdesk-server/db \
-v /path/to/your/logs:/crossdesk-server/logs \ -v /path/to/your/logs:/crossdesk-server/logs \
crossdesk/crossdesk-server:latest crossdesk/crossdesk-server:v1.0.0
``` ```
The parameters you need to pay attention to are as follows: The parameters you need to pay attention to are as follows:
@@ -166,6 +169,10 @@ The parameters you need to pay attention to are as follows:
- **CROSSDESK_SERVER_PORT**: The port used by the self-hosted server, corresponding to the **Server Port** in the CrossDesk client **Self-Hosted Server Configuration**. - **CROSSDESK_SERVER_PORT**: The port used by the self-hosted server, corresponding to the **Server Port** in the CrossDesk client **Self-Hosted Server Configuration**.
- **COTURN_PORT**: The port used by Coturn, corresponding to the **Relay Server Port** in the CrossDesk client **Self-Hosted Server Configuration**.
- **MIN_PORT** and **MAX_PORT**: The range of ports used by the self-hosted server, corresponding to the **Minimum Port** and **Maximum Port** in the CrossDesk client **Self-Hosted Server Configuration**. Example: 50000-60000. It depends on the number of devices connected to the server.
- **/path/to/your/certs**: Directory for certificate files. - **/path/to/your/certs**: Directory for certificate files.
- **/path/to/your/db**: CrossDesk Server device management database. - **/path/to/your/db**: CrossDesk Server device management database.
@@ -174,7 +181,7 @@ The parameters you need to pay attention to are as follows:
**Note**: **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.** - **/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.** - **The server must open the following ports: 3478/udp, 3478/tcp, 30000-60000/udp, CROSSDESK_SERVER_PORT/tcp.**
## Certificate Files ## Certificate Files
The client needs to load the root certificate, and the server needs to load the server private key and server certificate. The client needs to load the root certificate, and the server needs to load the server private key and server certificate.
@@ -300,3 +307,6 @@ Place **crossdesk.cn.key** and **crossdesk.cn_bundle.crt** into the **/path/to/y
4. Check the option to use **Self-Hosted Server Configuration**.<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> <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
View 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)
---

View File

@@ -36,14 +36,20 @@ int ConfigCenter::Load() {
enable_turn_ = ini_.GetBoolValue(section_, "enable_turn", enable_turn_); enable_turn_ = ini_.GetBoolValue(section_, "enable_turn", enable_turn_);
enable_srtp_ = ini_.GetBoolValue(section_, "enable_srtp", enable_srtp_); enable_srtp_ = ini_.GetBoolValue(section_, "enable_srtp", enable_srtp_);
server_host_ = ini_.GetValue(section_, "server_host", server_host_.c_str()); signal_server_host_ = ini_.GetValue(section_, "signal_server_host",
server_port_ = static_cast<int>( signal_server_host_.c_str());
ini_.GetLongValue(section_, "server_port", server_port_)); signal_server_port_ = static_cast<int>(
ini_.GetLongValue(section_, "signal_server_port", signal_server_port_));
coturn_server_port_ = static_cast<int>(
ini_.GetLongValue(section_, "coturn_server_port", coturn_server_port_));
cert_file_path_ = cert_file_path_ =
ini_.GetValue(section_, "cert_file_path", cert_file_path_.c_str()); ini_.GetValue(section_, "cert_file_path", cert_file_path_.c_str());
enable_self_hosted_ = enable_self_hosted_ =
ini_.GetBoolValue(section_, "enable_self_hosted", 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; return 0;
} }
@@ -58,10 +64,13 @@ int ConfigCenter::Save() {
ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_); ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
ini_.SetBoolValue(section_, "enable_turn", enable_turn_); ini_.SetBoolValue(section_, "enable_turn", enable_turn_);
ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_); ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
ini_.SetValue(section_, "server_host", server_host_.c_str()); ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
ini_.SetLongValue(section_, "server_port", static_cast<long>(server_port_)); ini_.SetLongValue(section_, "signal_server_port",
static_cast<long>(signal_server_port_));
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str()); ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_); 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()); SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) { if (rc < 0) {
@@ -147,9 +156,9 @@ int ConfigCenter::SetSrtp(bool enable_srtp) {
return 0; return 0;
} }
int ConfigCenter::SetServerHost(const std::string& server_host) { int ConfigCenter::SetServerHost(const std::string& signal_server_host) {
server_host_ = server_host; signal_server_host_ = signal_server_host;
ini_.SetValue(section_, "server_host", server_host_.c_str()); ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
SI_Error rc = ini_.SaveFile(config_path_.c_str()); SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) { if (rc < 0) {
return -1; return -1;
@@ -157,9 +166,10 @@ int ConfigCenter::SetServerHost(const std::string& server_host) {
return 0; return 0;
} }
int ConfigCenter::SetServerPort(int server_port) { int ConfigCenter::SetServerPort(int signal_server_port) {
server_port_ = server_port; signal_server_port_ = signal_server_port;
ini_.SetLongValue(section_, "server_port", static_cast<long>(server_port_)); ini_.SetLongValue(section_, "signal_server_port",
static_cast<long>(signal_server_port_));
SI_Error rc = ini_.SaveFile(config_path_.c_str()); SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) { if (rc < 0) {
return -1; return -1;
@@ -167,6 +177,16 @@ int ConfigCenter::SetServerPort(int server_port) {
return 0; return 0;
} }
int ConfigCenter::SetCoturnServerPort(int coturn_server_port) {
coturn_server_port_ = coturn_server_port;
SI_Error rc = ini_.SetLongValue(section_, "coturn_server_port",
static_cast<long>(coturn_server_port_));
if (rc < 0) {
return -1;
}
return 0;
}
int ConfigCenter::SetCertFilePath(const std::string& cert_file_path) { int ConfigCenter::SetCertFilePath(const std::string& cert_file_path) {
cert_file_path_ = cert_file_path; cert_file_path_ = cert_file_path;
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str()); ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
@@ -186,6 +206,11 @@ int ConfigCenter::SetSelfHosted(bool enable_self_hosted) {
return 0; return 0;
} }
int ConfigCenter::SetMinimizeToTray(bool enable_minimize_to_tray) {
enable_minimize_to_tray_ = enable_minimize_to_tray;
return 0;
}
// getters // getters
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; } ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; }
@@ -210,20 +235,32 @@ bool ConfigCenter::IsEnableTurn() const { return enable_turn_; }
bool ConfigCenter::IsEnableSrtp() const { return enable_srtp_; } bool ConfigCenter::IsEnableSrtp() const { return enable_srtp_; }
std::string ConfigCenter::GetServerHost() const { return server_host_; } std::string ConfigCenter::GetSignalServerHost() const {
return signal_server_host_;
}
int ConfigCenter::GetServerPort() const { return server_port_; } int ConfigCenter::GetSignalServerPort() const { return signal_server_port_; }
int ConfigCenter::GetCoturnServerPort() const { return coturn_server_port_; }
std::string ConfigCenter::GetCertFilePath() const { return cert_file_path_; } std::string ConfigCenter::GetCertFilePath() const { return cert_file_path_; }
std::string ConfigCenter::GetDefaultServerHost() const { std::string ConfigCenter::GetDefaultServerHost() const {
return server_host_default_; return signal_server_host_default_;
} }
int ConfigCenter::GetDefaultServerPort() const { return server_port_default_; } int ConfigCenter::GetDefaultSignalServerPort() const {
return server_port_default_;
}
int ConfigCenter::GetDefaultCoturnServerPort() const {
return coturn_server_port_default_;
}
std::string ConfigCenter::GetDefaultCertFilePath() const { std::string ConfigCenter::GetDefaultCertFilePath() const {
return cert_file_path_default_; return cert_file_path_default_;
} }
bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; } bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }
bool ConfigCenter::IsMinimizeToTray() const { return enable_minimize_to_tray_; }

View File

@@ -32,10 +32,12 @@ class ConfigCenter {
int SetHardwareVideoCodec(bool hardware_video_codec); int SetHardwareVideoCodec(bool hardware_video_codec);
int SetTurn(bool enable_turn); int SetTurn(bool enable_turn);
int SetSrtp(bool enable_srtp); int SetSrtp(bool enable_srtp);
int SetServerHost(const std::string& server_host); int SetServerHost(const std::string& signal_server_host);
int SetServerPort(int server_port); int SetServerPort(int signal_server_port);
int SetCoturnServerPort(int coturn_server_port);
int SetCertFilePath(const std::string& cert_file_path); int SetCertFilePath(const std::string& cert_file_path);
int SetSelfHosted(bool enable_self_hosted); int SetSelfHosted(bool enable_self_hosted);
int SetMinimizeToTray(bool enable_minimize_to_tray);
// read config // read config
@@ -46,13 +48,16 @@ class ConfigCenter {
bool IsHardwareVideoCodec() const; bool IsHardwareVideoCodec() const;
bool IsEnableTurn() const; bool IsEnableTurn() const;
bool IsEnableSrtp() const; bool IsEnableSrtp() const;
std::string GetServerHost() const; std::string GetSignalServerHost() const;
int GetServerPort() const; int GetSignalServerPort() const;
int GetCoturnServerPort() const;
std::string GetCertFilePath() const; std::string GetCertFilePath() const;
std::string GetDefaultServerHost() const; std::string GetDefaultServerHost() const;
int GetDefaultServerPort() const; int GetDefaultSignalServerPort() const;
int GetDefaultCoturnServerPort() const;
std::string GetDefaultCertFilePath() const; std::string GetDefaultCertFilePath() const;
bool IsSelfHosted() const; bool IsSelfHosted() const;
bool IsMinimizeToTray() const;
int Load(); int Load();
int Save(); int Save();
@@ -70,12 +75,15 @@ class ConfigCenter {
bool hardware_video_codec_ = false; bool hardware_video_codec_ = false;
bool enable_turn_ = false; bool enable_turn_ = false;
bool enable_srtp_ = false; bool enable_srtp_ = false;
std::string server_host_ = "api.crossdesk.cn"; std::string signal_server_host_ = "api.crossdesk.cn";
int server_port_ = 9099; std::string signal_server_host_default_ = "api.crossdesk.cn";
std::string server_host_default_ = "api.crossdesk.cn"; int signal_server_port_ = 9099;
int server_port_default_ = 9099; int server_port_default_ = 9099;
int coturn_server_port_ = 3478;
int coturn_server_port_default_ = 3478;
std::string cert_file_path_default_ = ""; std::string cert_file_path_default_ = "";
bool enable_self_hosted_ = false; bool enable_self_hosted_ = false;
bool enable_minimize_to_tray_ = false;
}; };
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -20,12 +20,17 @@
#define INPUT_WINDOW_PADDING_EN 96 #define INPUT_WINDOW_PADDING_EN 96
#define SETTINGS_WINDOW_WIDTH_CN 202 #define SETTINGS_WINDOW_WIDTH_CN 202
#define SETTINGS_WINDOW_WIDTH_EN 248 #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_CN 315
#define SETTINGS_WINDOW_HEIGHT_EN 315 #define SETTINGS_WINDOW_HEIGHT_EN 315
#endif
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228 #define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275 #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_CN 195
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN 165 #define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN 195
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 120 #define LANGUAGE_SELECT_WINDOW_PADDING_CN 120
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 167 #define LANGUAGE_SELECT_WINDOW_PADDING_EN 167
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 120 #define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 120
@@ -42,6 +47,8 @@
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218 #define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171 #define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218 #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_CN 90
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137 #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_CN 90

View File

@@ -8,6 +8,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
#if _WIN32
#include <Windows.h>
#endif
namespace localization { namespace localization {
static std::vector<std::string> local_desktop = { static std::vector<std::string> local_desktop = {
@@ -102,7 +106,9 @@ static std::vector<std::string> self_hosted_server_settings = {
static std::vector<std::string> self_hosted_server_address = { static std::vector<std::string> self_hosted_server_address = {
reinterpret_cast<const char*>(u8"服务器地址:"), "Server Address:"}; reinterpret_cast<const char*>(u8"服务器地址:"), "Server Address:"};
static std::vector<std::string> self_hosted_server_port = { static std::vector<std::string> self_hosted_server_port = {
reinterpret_cast<const char*>(u8"服务端口:"), "Server Port:"}; reinterpret_cast<const char*>(u8"信令服务端口:"), "Signal Service Port:"};
static std::vector<std::string> self_hosted_server_coturn_server_port = {
reinterpret_cast<const char*>(u8"中继服务端口:"), "Relay Service Port:"};
static std::vector<std::string> self_hosted_server_certificate_path = { static std::vector<std::string> self_hosted_server_certificate_path = {
reinterpret_cast<const char*>(u8"证书文件路径:"), "Certificate File Path:"}; reinterpret_cast<const char*>(u8"证书文件路径:"), "Certificate File Path:"};
static std::vector<std::string> select_a_file = { static std::vector<std::string> select_a_file = {
@@ -155,6 +161,12 @@ static std::vector<std::string> version = {
static std::vector<std::string> confirm_delete_connection = { static std::vector<std::string> confirm_delete_connection = {
reinterpret_cast<const char*>(u8"确认删除此连接"), reinterpret_cast<const char*>(u8"确认删除此连接"),
"Confirm to delete this connection"}; "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 #endif

View File

@@ -427,39 +427,57 @@ int Render::StopKeyboardCapturer() {
int Render::CreateConnectionPeer() { int Render::CreateConnectionPeer() {
params_.use_cfg_file = false; params_.use_cfg_file = false;
std::string server_ip; std::string signal_server_ip;
int server_port; int signal_server_port;
std::string server_cert_path; int coturn_server_port;
std::string tls_cert_path;
if (config_center_->IsSelfHosted()) { if (config_center_->IsSelfHosted()) {
server_ip = config_center_->GetServerHost(); signal_server_ip = config_center_->GetSignalServerHost();
server_port = config_center_->GetServerPort(); signal_server_port = config_center_->GetSignalServerPort();
server_cert_path = config_center_->GetCertFilePath(); coturn_server_port = config_center_->GetCoturnServerPort();
tls_cert_path = config_center_->GetCertFilePath();
} else { } else {
server_ip = config_center_->GetDefaultServerHost(); signal_server_ip = config_center_->GetDefaultServerHost();
server_port = config_center_->GetDefaultServerPort(); signal_server_port = config_center_->GetDefaultSignalServerPort();
server_cert_path = config_center_->GetDefaultCertFilePath(); coturn_server_port = config_center_->GetDefaultCoturnServerPort();
tls_cert_path = config_center_->GetDefaultCertFilePath();
} }
strncpy((char*)params_.signal_server_ip, server_ip.c_str(), // self hosted server config
strncpy(signal_server_ip_self_, config_center_->GetSignalServerHost().c_str(),
sizeof(signal_server_ip_self_) - 1);
signal_server_ip_self_[sizeof(signal_server_ip_self_) - 1] = '\0';
strncpy(signal_server_port_self_,
std::to_string(config_center_->GetSignalServerPort()).c_str(),
sizeof(signal_server_port_self_) - 1);
signal_server_port_self_[sizeof(signal_server_port_self_) - 1] = '\0';
strncpy(coturn_server_port_self_,
std::to_string(config_center_->GetCoturnServerPort()).c_str(),
sizeof(coturn_server_port_self_) - 1);
coturn_server_port_self_[sizeof(coturn_server_port_self_) - 1] = '\0';
tls_cert_path_self_ = config_center_->GetCertFilePath();
// peer config
strncpy((char*)params_.signal_server_ip, signal_server_ip.c_str(),
sizeof(params_.signal_server_ip) - 1); sizeof(params_.signal_server_ip) - 1);
params_.signal_server_ip[sizeof(params_.signal_server_ip) - 1] = '\0'; params_.signal_server_ip[sizeof(params_.signal_server_ip) - 1] = '\0';
params_.signal_server_port = server_port; params_.signal_server_port = signal_server_port;
strncpy((char*)params_.stun_server_ip, server_ip.c_str(), strncpy((char*)params_.stun_server_ip, signal_server_ip.c_str(),
sizeof(params_.stun_server_ip) - 1); sizeof(params_.stun_server_ip) - 1);
params_.stun_server_ip[sizeof(params_.stun_server_ip) - 1] = '\0'; params_.stun_server_ip[sizeof(params_.stun_server_ip) - 1] = '\0';
params_.stun_server_port = 3478; params_.stun_server_port = coturn_server_port;
strncpy((char*)params_.turn_server_ip, server_ip.c_str(), strncpy((char*)params_.turn_server_ip, signal_server_ip.c_str(),
sizeof(params_.turn_server_ip) - 1); sizeof(params_.turn_server_ip) - 1);
params_.turn_server_ip[sizeof(params_.turn_server_ip) - 1] = '\0'; params_.turn_server_ip[sizeof(params_.turn_server_ip) - 1] = '\0';
params_.turn_server_port = 3478; params_.turn_server_port = coturn_server_port;
strncpy((char*)params_.turn_server_username, "dijunkun", strncpy((char*)params_.turn_server_username, "crossdesk",
sizeof(params_.turn_server_username) - 1); sizeof(params_.turn_server_username) - 1);
params_.turn_server_username[sizeof(params_.turn_server_username) - 1] = '\0'; params_.turn_server_username[sizeof(params_.turn_server_username) - 1] = '\0';
strncpy((char*)params_.turn_server_password, "dijunkunpw", strncpy((char*)params_.turn_server_password, "crossdeskpw",
sizeof(params_.turn_server_password) - 1); sizeof(params_.turn_server_password) - 1);
params_.turn_server_password[sizeof(params_.turn_server_password) - 1] = '\0'; params_.turn_server_password[sizeof(params_.turn_server_password) - 1] = '\0';
strncpy(params_.tls_cert_path, server_cert_path.c_str(), strncpy(params_.tls_cert_path, tls_cert_path.c_str(),
sizeof(params_.tls_cert_path) - 1); sizeof(params_.tls_cert_path) - 1);
params_.tls_cert_path[sizeof(params_.tls_cert_path) - 1] = '\0'; params_.tls_cert_path[sizeof(params_.tls_cert_path) - 1] = '\0';
@@ -588,6 +606,17 @@ int Render::CreateMainWindow() {
// for window region action // for window region action
SDL_SetWindowHitTest(main_window_, HitTestCallback, this); 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; return 0;
} }
@@ -872,13 +901,14 @@ int Render::Run() {
cache_path_ = path_manager_->GetCachePath().string(); cache_path_ = path_manager_->GetCachePath().string();
config_center_ = config_center_ =
std::make_unique<ConfigCenter>(cache_path_ + "/config.ini", cert_path_); std::make_unique<ConfigCenter>(cache_path_ + "/config.ini", cert_path_);
strncpy(signal_server_ip_tmp_, config_center_->GetServerHost().c_str(), strncpy(signal_server_ip_self_,
sizeof(signal_server_ip_tmp_) - 1); config_center_->GetSignalServerHost().c_str(),
signal_server_ip_tmp_[sizeof(signal_server_ip_tmp_) - 1] = '\0'; sizeof(signal_server_ip_self_) - 1);
strncpy(signal_server_port_tmp_, signal_server_ip_self_[sizeof(signal_server_ip_self_) - 1] = '\0';
std::to_string(config_center_->GetServerPort()).c_str(), strncpy(signal_server_port_self_,
sizeof(signal_server_port_tmp_) - 1); std::to_string(config_center_->GetSignalServerPort()).c_str(),
signal_server_port_tmp_[sizeof(signal_server_port_tmp_) - 1] = '\0'; sizeof(signal_server_port_self_) - 1);
signal_server_port_self_[sizeof(signal_server_port_self_) - 1] = '\0';
strncpy(cert_file_path_, cert_path_.c_str(), sizeof(cert_file_path_) - 1); strncpy(cert_file_path_, cert_path_.c_str(), sizeof(cert_file_path_) - 1);
cert_file_path_[sizeof(cert_file_path_) - 1] = '\0'; cert_file_path_[sizeof(cert_file_path_) - 1] = '\0';
} else { } else {
@@ -968,6 +998,14 @@ void Render::MainLoop() {
ProcessSdlEvent(event); ProcessSdlEvent(event);
} }
#if _WIN32
MSG msg;
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#endif
UpdateLabels(); UpdateLabels();
HandleRecentConnections(); HandleRecentConnections();
HandleStreamWindow(); HandleStreamWindow();

View File

@@ -29,6 +29,9 @@
#include "screen_capturer_factory.h" #include "screen_capturer_factory.h"
#include "speaker_capturer_factory.h" #include "speaker_capturer_factory.h"
#include "thumbnail.h" #include "thumbnail.h"
#if _WIN32
#include "win_tray.h"
#endif
class Render { class Render {
public: public:
@@ -298,6 +301,9 @@ class Render {
ImGuiContext* main_ctx_ = nullptr; ImGuiContext* main_ctx_ = nullptr;
bool exit_ = false; bool exit_ = false;
const int sdl_refresh_ms_ = 16; // ~60 FPS const int sdl_refresh_ms_ = 16; // ~60 FPS
#if _WIN32
std::unique_ptr<WinTray> tray_;
#endif
// main window properties // main window properties
bool start_mouse_controller_ = false; bool start_mouse_controller_ = false;
@@ -335,8 +341,8 @@ class Render {
float connection_status_window_height_ = 150; float connection_status_window_height_ = 150;
float notification_window_width_ = 200; float notification_window_width_ = 200;
float notification_window_height_ = 80; float notification_window_height_ = 80;
float about_window_width_ = 200; float about_window_width_ = 300;
float about_window_height_ = 150; float about_window_height_ = 170;
int screen_width_ = 1280; int screen_width_ = 1280;
int screen_height_ = 720; int screen_height_ = 720;
int selected_display_ = 0; int selected_display_ = 0;
@@ -436,6 +442,7 @@ class Render {
bool enable_srtp_ = false; bool enable_srtp_ = false;
char signal_server_ip_[256] = "api.crossdesk.cn"; char signal_server_ip_[256] = "api.crossdesk.cn";
char signal_server_port_[6] = "9099"; char signal_server_port_[6] = "9099";
char coturn_server_port_[6] = "3478";
char cert_file_path_[256] = ""; char cert_file_path_[256] = "";
bool enable_self_hosted_server_ = false; bool enable_self_hosted_server_ = false;
int language_button_value_last_ = 0; int language_button_value_last_ = 0;
@@ -444,12 +451,16 @@ class Render {
bool enable_hardware_video_codec_last_ = false; bool enable_hardware_video_codec_last_ = false;
bool enable_turn_last_ = false; bool enable_turn_last_ = false;
bool enable_srtp_last_ = false; bool enable_srtp_last_ = false;
char signal_server_ip_tmp_[256] = "api.crossdesk.cn"; bool enable_minimize_to_tray_ = false;
char signal_server_port_tmp_[6] = "9099"; bool enable_minimize_to_tray_last_ = false;
char signal_server_ip_self_[256] = "";
char signal_server_port_self_[6] = "";
char coturn_server_port_self_[6] = "";
std::string tls_cert_path_self_ = "";
bool settings_window_pos_reset_ = true; bool settings_window_pos_reset_ = true;
bool self_hosted_server_config_window_pos_reset_ = true; bool self_hosted_server_config_window_pos_reset_ = true;
std::string selected_current_file_path_ = ""; std::string selected_current_file_path_ = "";
std::string selected_file_ = ""; bool show_file_browser_ = true;
/* ------ main window property end ------ */ /* ------ main window property end ------ */
/* ------ sub stream window property start ------ */ /* ------ sub stream window property start ------ */

View File

@@ -139,9 +139,17 @@ int Render::TitleBar(bool main_window) {
float xmark_size = 12.0f; float xmark_size = 12.0f;
std::string close_button = "##xmark"; // ICON_FA_XMARK; std::string close_button = "##xmark"; // ICON_FA_XMARK;
if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) { if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) {
SDL_Event event; #if _WIN32
event.type = SDL_EVENT_QUIT; if (enable_minimize_to_tray_) {
SDL_PushEvent(&event); 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, draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
xmark_pos_y - xmark_size / 2 + 0.75f), xmark_pos_y - xmark_size / 2 + 0.75f),

112
src/gui/tray/win_tray.cpp Normal file
View 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
View 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

View File

@@ -27,15 +27,21 @@ int Render::AboutWindow() {
ImGui::SetWindowFontScale(0.5f); ImGui::SetWindowFontScale(0.5f);
std::string version; std::string version;
#ifdef RD_VERSION #ifdef CROSSDESK_VERSION
version = RD_VERSION; version = CROSSDESK_VERSION;
#else #else
version = "Unknown"; version = "Unknown";
#endif #endif
std::string text = std::string text = localization::version[localization_language_index_] +
localization::version[localization_language_index_] + ": " + version; ": CrossDesk v" + version;
ImGui::Text("%s", text.c_str()); 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::SetCursorPosX(about_window_width_ * 0.42f);
ImGui::SetCursorPosY(about_window_height_ * 0.75f); ImGui::SetCursorPosY(about_window_height_ * 0.75f);

View File

@@ -57,7 +57,7 @@ int Render::SettingWindow() {
localization::language_en[localization_language_index_].c_str()}; localization::language_en[localization_language_index_].c_str()};
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", localization::language[localization_language_index_].c_str()); "%s", localization::language[localization_language_index_].c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
@@ -88,7 +88,7 @@ int Render::SettingWindow() {
.c_str()}; .c_str()};
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", "%s",
localization::video_quality[localization_language_index_].c_str()); localization::video_quality[localization_language_index_].c_str());
@@ -111,7 +111,7 @@ int Render::SettingWindow() {
const char* video_frame_rate_items[] = {"30 fps", "60 fps"}; const char* video_frame_rate_items[] = {"30 fps", "60 fps"};
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text("%s", ImGui::Text("%s",
localization::video_frame_rate[localization_language_index_] localization::video_frame_rate[localization_language_index_]
.c_str()); .c_str());
@@ -137,7 +137,7 @@ int Render::SettingWindow() {
localization::av1[localization_language_index_].c_str()}; localization::av1[localization_language_index_].c_str()};
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", "%s",
localization::video_encode_format[localization_language_index_] localization::video_encode_format[localization_language_index_]
@@ -160,7 +160,7 @@ int Render::SettingWindow() {
{ {
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text("%s", localization::enable_hardware_video_codec ImGui::Text("%s", localization::enable_hardware_video_codec
[localization_language_index_] [localization_language_index_]
.c_str()); .c_str());
@@ -179,7 +179,7 @@ int Render::SettingWindow() {
{ {
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", "%s",
localization::enable_turn[localization_language_index_].c_str()); localization::enable_turn[localization_language_index_].c_str());
@@ -197,7 +197,7 @@ int Render::SettingWindow() {
{ {
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", "%s",
localization::enable_srtp[localization_language_index_].c_str()); localization::enable_srtp[localization_language_index_].c_str());
@@ -215,7 +215,7 @@ int Render::SettingWindow() {
{ {
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 1);
if (ImGui::Button(localization::self_hosted_server_config if (ImGui::Button(localization::self_hosted_server_config
[localization_language_index_] [localization_language_index_]
@@ -232,7 +232,27 @@ int Render::SettingWindow() {
ImGui::Checkbox("##enable_self_hosted_server", ImGui::Checkbox("##enable_self_hosted_server",
&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_) { if (stream_window_inited_) {
ImGui::EndDisabled(); ImGui::EndDisabled();
} }

View File

@@ -28,8 +28,14 @@ std::vector<std::string> GetRootEntries() {
int Render::ShowSimpleFileBrowser() { int Render::ShowSimpleFileBrowser() {
std::string display_text; std::string display_text;
if (!selected_file_.empty()) {
display_text = std::filesystem::path(selected_file_).filename().string(); if (selected_current_file_path_.empty()) {
selected_current_file_path_ = std::filesystem::current_path().string();
}
if (!tls_cert_path_self_.empty()) {
display_text =
std::filesystem::path(tls_cert_path_self_).filename().string();
} else if (selected_current_file_path_ != "Root") { } else if (selected_current_file_path_ != "Root") {
display_text = display_text =
std::filesystem::path(selected_current_file_path_).filename().string(); std::filesystem::path(selected_current_file_path_).filename().string();
@@ -43,49 +49,69 @@ int Render::ShowSimpleFileBrowser() {
localization::select_a_file[localization_language_index_].c_str(); localization::select_a_file[localization_language_index_].c_str();
} }
if (ImGui::BeginCombo("##select_a_file", display_text.c_str())) { if (show_file_browser_) {
if (selected_current_file_path_ == "Root" || ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
!std::filesystem::exists(selected_current_file_path_) ||
!std::filesystem::is_directory(selected_current_file_path_)) { float fixed_width = 130.0f;
ImGui::SetNextItemWidth(fixed_width);
ImGui::SetNextWindowSizeConstraints(ImVec2(fixed_width, 0),
ImVec2(fixed_width, 100.0f));
if (ImGui::BeginCombo("##select_a_file", display_text.c_str(), 0)) {
bool file_selected = false;
auto roots = GetRootEntries(); auto roots = GetRootEntries();
for (const auto& root : roots) { if (selected_current_file_path_ == "Root" ||
if (ImGui::Selectable(root.c_str())) { !std::filesystem::exists(selected_current_file_path_) ||
selected_current_file_path_ = root; !std::filesystem::is_directory(selected_current_file_path_)) {
selected_file_.clear(); for (const auto& root : roots) {
} if (ImGui::Selectable(root.c_str())) {
} selected_current_file_path_ = root;
} else { tls_cert_path_self_.clear();
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) { } else {
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Error: %s", e.what()); std::filesystem::path p(selected_current_file_path_);
}
}
ImGui::EndCombo(); if (ImGui::Selectable("..")) {
if (std::find(roots.begin(), roots.end(),
selected_current_file_path_) != roots.end()) {
selected_current_file_path_ = "Root";
} else if (p.has_parent_path()) {
selected_current_file_path_ = p.parent_path().string();
} else {
selected_current_file_path_ = "Root";
}
tls_cert_path_self_.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();
tls_cert_path_self_.clear();
}
} else {
if (ImGui::Selectable(name.c_str())) {
tls_cert_path_self_ = entry.path().string();
file_selected = true;
show_file_browser_ = false;
}
}
}
} catch (const std::exception& e) {
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Error: %s", e.what());
}
}
ImGui::EndCombo();
}
ImGui::PopItemFlag();
} else {
show_file_browser_ = true;
} }
return 0; return 0;
@@ -157,8 +183,8 @@ int Render::SelfHostedServerWindow() {
ImGui::SetCursorPosY(settings_items_offset); ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH); ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::InputText("##signal_server_ip_tmp_", signal_server_ip_tmp_, ImGui::InputText("##signal_server_ip_self_", signal_server_ip_self_,
IM_ARRAYSIZE(signal_server_ip_tmp_), IM_ARRAYSIZE(signal_server_ip_self_),
ImGuiInputTextFlags_AlwaysOverwrite); ImGuiInputTextFlags_AlwaysOverwrite);
} }
@@ -180,8 +206,29 @@ int Render::SelfHostedServerWindow() {
ImGui::SetCursorPosY(settings_items_offset); ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH); ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::InputText("##signal_server_port_tmp_", signal_server_port_tmp_, ImGui::InputText("##signal_server_port_self_", signal_server_port_self_,
IM_ARRAYSIZE(signal_server_port_tmp_)); IM_ARRAYSIZE(signal_server_port_self_));
}
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text("%s", localization::self_hosted_server_coturn_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("##coturn_server_port_self_", coturn_server_port_self_,
IM_ARRAYSIZE(coturn_server_port_self_));
} }
ImGui::Separator(); ImGui::Separator();
@@ -223,16 +270,20 @@ int Render::SelfHostedServerWindow() {
localization::ok[localization_language_index_].c_str())) { localization::ok[localization_language_index_].c_str())) {
show_self_hosted_server_config_window_ = false; show_self_hosted_server_config_window_ = false;
config_center_->SetServerHost(signal_server_ip_tmp_); config_center_->SetServerHost(signal_server_ip_self_);
config_center_->SetServerPort(atoi(signal_server_port_tmp_)); config_center_->SetServerPort(atoi(signal_server_port_self_));
config_center_->SetCertFilePath(selected_file_); config_center_->SetCoturnServerPort(atoi(coturn_server_port_self_));
strncpy(signal_server_ip_, signal_server_ip_tmp_, config_center_->SetCertFilePath(tls_cert_path_self_);
strncpy(signal_server_ip_, signal_server_ip_self_,
sizeof(signal_server_ip_) - 1); sizeof(signal_server_ip_) - 1);
signal_server_ip_[sizeof(signal_server_ip_) - 1] = '\0'; signal_server_ip_[sizeof(signal_server_ip_) - 1] = '\0';
strncpy(signal_server_port_, signal_server_port_tmp_, strncpy(signal_server_port_, signal_server_port_self_,
sizeof(signal_server_port_) - 1); sizeof(signal_server_port_) - 1);
signal_server_port_[sizeof(signal_server_port_) - 1] = '\0'; signal_server_port_[sizeof(signal_server_port_) - 1] = '\0';
strncpy(cert_file_path_, selected_file_.c_str(), strncpy(coturn_server_port_, coturn_server_port_self_,
sizeof(coturn_server_port_) - 1);
coturn_server_port_[sizeof(coturn_server_port_) - 1] = '\0';
strncpy(cert_file_path_, tls_cert_path_self_.c_str(),
sizeof(cert_file_path_) - 1); sizeof(cert_file_path_) - 1);
cert_file_path_[sizeof(cert_file_path_) - 1] = '\0'; cert_file_path_[sizeof(cert_file_path_) - 1] = '\0';
@@ -246,15 +297,15 @@ int Render::SelfHostedServerWindow() {
show_self_hosted_server_config_window_ = false; show_self_hosted_server_config_window_ = false;
self_hosted_server_config_window_pos_reset_ = true; self_hosted_server_config_window_pos_reset_ = true;
strncpy(signal_server_ip_tmp_, signal_server_ip_, strncpy(signal_server_ip_self_, signal_server_ip_,
sizeof(signal_server_ip_tmp_) - 1); sizeof(signal_server_ip_self_) - 1);
signal_server_ip_tmp_[sizeof(signal_server_ip_tmp_) - 1] = '\0'; signal_server_ip_self_[sizeof(signal_server_ip_self_) - 1] = '\0';
strncpy(signal_server_port_tmp_, signal_server_port_, strncpy(signal_server_port_self_, signal_server_port_,
sizeof(signal_server_port_tmp_) - 1); sizeof(signal_server_port_self_) - 1);
signal_server_port_tmp_[sizeof(signal_server_port_tmp_) - 1] = '\0'; signal_server_port_self_[sizeof(signal_server_port_self_) - 1] = '\0';
config_center_->SetServerHost(signal_server_ip_tmp_); config_center_->SetServerHost(signal_server_ip_self_);
config_center_->SetServerPort(atoi(signal_server_port_tmp_)); config_center_->SetServerPort(atoi(signal_server_port_self_));
selected_file_.clear(); tls_cert_path_self_.clear();
} }
ImGui::SetWindowFontScale(1.0f); ImGui::SetWindowFontScale(1.0f);

View File

@@ -1,8 +1,11 @@
set_project("crossdesk") set_project("crossdesk")
set_license("LGPL-3.0") set_license("LGPL-3.0")
set_version("0.0.1") option("CROSSDESK_VERSION")
add_defines("RD_VERSION=\"0.0.1\""); set_default("0.0.0")
set_showmenu(true)
set_description("Set CROSSDESK_VERSION for build")
option_end()
add_rules("mode.release", "mode.debug") add_rules("mode.release", "mode.debug")
set_languages("c++17") set_languages("c++17")
@@ -143,13 +146,18 @@ target("assets")
target("gui") target("gui")
set_kind("object") set_kind("object")
add_packages("libyuv") add_packages("libyuv")
add_defines("CROSSDESK_VERSION=\"" .. (get_config("CROSSDESK_VERSION") or "Unknown") .. "\"")
add_deps("rd_log", "common", "assets", "config_center", "minirtc", add_deps("rd_log", "common", "assets", "config_center", "minirtc",
"path_manager", "screen_capturer", "speaker_capturer", "path_manager", "screen_capturer", "speaker_capturer",
"device_controller", "thumbnail") "device_controller", "thumbnail")
add_files("src/gui/*.cpp", "src/gui/panels/*.cpp", "src/gui/toolbars/*.cpp", add_files("src/gui/*.cpp", "src/gui/panels/*.cpp", "src/gui/toolbars/*.cpp",
"src/gui/windows/*.cpp") "src/gui/windows/*.cpp")
add_includedirs("src/gui", "src/gui/panels", "src/gui/toolbars", add_includedirs("src/gui", "src/gui/panels", "src/gui/toolbars",
"src/gui/windows", {public = true}) "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") target("crossdesk")
set_kind("binary") set_kind("binary")