mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-28 20:06:14 +08:00
Compare commits
24 Commits
win-virtua
...
v1.0.2-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
495d179fae | ||
|
|
b668b3c936 | ||
|
|
cc19ec125a | ||
|
|
ffa77fdf44 | ||
|
|
47cf806532 | ||
|
|
911dce2e71 | ||
|
|
9f80d4f69d | ||
|
|
cba644f055 | ||
|
|
f733fe9e49 | ||
|
|
27263fe1db | ||
|
|
698bf72a6c | ||
|
|
0bd27d0b17 | ||
|
|
ee5612da8b | ||
|
|
c7a2023c88 | ||
|
|
c031a8c145 | ||
|
|
0bf83f07ad | ||
|
|
3638b712bd | ||
|
|
b2ab940f20 | ||
|
|
17f9536476 | ||
|
|
0ef51e3faf | ||
|
|
cccf5dadb2 | ||
|
|
2f0b0ffc22 | ||
|
|
c7411b59f1 | ||
|
|
8222782522 |
31
.github/workflows/build.yml
vendored
31
.github/workflows/build.yml
vendored
@@ -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
|
||||||
|
|||||||
54
.github/workflows/close-issue.yml
vendored
54
.github/workflows/close-issue.yml
vendored
@@ -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 }}
|
||||||
|
|||||||
3
.github/workflows/update-pages.yml
vendored
3
.github/workflows/update-pages.yml
vendored
@@ -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
|
||||||
|
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -3,7 +3,7 @@
|
|||||||
[]()
|
[]()
|
||||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||||
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
||||||
[](https://github.com/kunkundi/crossdesk/actions)
|
[](https://github.com/kunkundi/crossdesk/actions)
|
||||||
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
@@ -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_PORT:COTURN 服务使用的端口范围,例如:MIN_PORT=50000, MAX_PORT=60000,范围可根据客户端数量调整。
|
||||||
|
|
||||||
- /path/to/your/certs:证书文件目录
|
- /path/to/your/certs:证书文件目录
|
||||||
|
|
||||||
- /path/to/your/db:CrossDesk Server 设备管理数据库
|
- /path/to/your/db:CrossDesk Server 设备管理数据库
|
||||||
@@ -169,7 +176,7 @@ sudo docker run -d \
|
|||||||
|
|
||||||
**注意**:
|
**注意**:
|
||||||
- **/path/to/your/ 是示例路径,请替换为你自己的实际路径。挂载的目录必须事先创建好,否则容器会报错。**
|
- **/path/to/your/ 是示例路径,请替换为你自己的实际路径。挂载的目录必须事先创建好,否则容器会报错。**
|
||||||
- **服务器需开放端口:3478/udp,3478/tcp,30000-60000/udp,CROSSDESK_SERVER_PORT/tcp,443/tcp。**
|
- **服务器需开放端口:3478/udp,3478/tcp,MIN_PORT-MAX_PORT/udp,CROSSDESK_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) 。
|
||||||
26
README_EN.md
26
README_EN.md
@@ -3,7 +3,7 @@
|
|||||||
[]()
|
[]()
|
||||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||||
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
||||||
[](https://github.com/kunkundi/crossdesk/actions)
|
[](https://github.com/kunkundi/crossdesk/actions)
|
||||||
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
@@ -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
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)
|
||||||
|
|
||||||
|
---
|
||||||
@@ -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_; }
|
||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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 = {
|
||||||
@@ -103,6 +107,8 @@ 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"服务器端口:"), "Server Port:"};
|
||||||
|
static std::vector<std::string> self_hosted_server_coturn_server_port = {
|
||||||
|
reinterpret_cast<const char*>(u8"中继服务器端口:"), "Relay Server 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
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 ------ */
|
||||||
|
|||||||
@@ -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
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
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
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
16
xmake.lua
16
xmake.lua
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user