41 Commits

Author SHA1 Message Date
dijunkun
2c622bc76e [ci] update GitHub Actions to use macos-13 runner instead of macos-15 for Intel builds 2025-10-27 22:31:23 +08:00
dijunkun
b790c7d08e [chore] update README 2025-10-27 22:11:30 +08:00
dijunkun
0ca90d2516 [chore] move minirtc into submodules 2025-10-27 21:36:11 +08:00
dijunkun
401bfe4483 [refactor] add namespace 'crossdesk' to codebase 2025-10-27 21:09:39 +08:00
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
dijunkun
5fed09c1aa [ci] only close inactive issues with no labels 2025-10-21 11:37:25 +08:00
dijunkun
8e499772f9 [ci] add GitHub Actions workflow to automatically close inactive issues 2025-10-21 11:29:31 +08:00
dijunkun
0da812e7e9 [chore] update README 2025-10-20 21:51:09 +08:00
dijunkun
3d67b6e9d6 [chore] update README 2025-10-20 11:18:35 +08:00
dijunkun
506b2893c6 [chore] update README 2025-10-20 11:04:41 +08:00
Junkun Di
56abe9e690 [chore] update README 2025-10-20 09:55:59 +08:00
dijunkun
ab13fa582d [chore] update README 2025-10-20 01:36:42 +08:00
dijunkun
b9e8192eee [feat] support self-hosted service, fixes #3 2025-10-20 01:02:44 +08:00
dijunkun
5590aaeb1e [feat] add support for self-hosted server configuration 2025-10-18 23:41:10 +08:00
dijunkun
adfe14809f [ci] switch GitHub-hosted runner from macos-13 to macos-15-intel 2025-10-18 02:40:32 +08:00
dijunkun
a21dbc8d69 Merge branch 'sdl3' into self-hosted-server 2025-10-18 02:26:01 +08:00
dijunkun
b9c70f54d3 [feat] add self-hosted server configuration section in settings window 2025-10-17 17:22:31 +08:00
dijunkun
9cd617d078 [feat] add self-hosted server config item in settings windows 2025-10-16 17:42:49 +08:00
89 changed files with 7031 additions and 1777 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
*.h linguist-language=C++
*.cpp linguist-language=C++
*.lua linguist-language=Xmake

View File

@@ -1,4 +1,4 @@
name: Build and Release CrossDesk name: Build and Release
on: on:
push: push:
@@ -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

80
.github/workflows/close-issue.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
name: Close Inactive Issues
on:
schedule:
# run every day at midnight
- cron: "0 0 * * *"
permissions:
issues: write
pull-requests: write
contents: read
jobs:
close_inactive_issues:
runs-on: ubuntu-latest
steps:
- name: Check inactive issues and close them
uses: actions/github-script@v6
with:
script: |
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100,
});
const now = new Date().getTime();
const inactivePeriod = 7 * 24 * 60 * 60 * 1000; // 7 days
for (const issue of issues) {
// skip pull requests (they are also returned by listForRepo)
if (issue.pull_request) continue;
// skip labeled issues
if (issue.labels.length > 0) {
console.log(`Skipping issue #${issue.number} (Has labels).`);
continue;
}
// fetch comments for this issue
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
per_page: 100,
});
// determine the "last activity" time
let lastActivityTime;
if (comments.length > 0) {
const lastComment = comments[comments.length - 1];
lastActivityTime = new Date(lastComment.updated_at).getTime();
} else {
lastActivityTime = new Date(issue.created_at).getTime();
}
// check inactivity
if (now - lastActivityTime > inactivePeriod) {
console.log(`Closing inactive issue: #${issue.number} (No recent replies for 7 days)`);
await github.rest.issues.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({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed',
});
} else {
console.log(`Skipping issue #${issue.number} (Active within 7 days).`);
}
}
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: |

4
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "thirdparty/minirtc"] [submodule "submodules/minirtc"]
path = thirdparty/minirtc path = submodules/minirtc
url = https://github.com/kunkundi/minirtc.git url = https://github.com/kunkundi/minirtc.git

229
README.md
View File

@@ -1,9 +1,15 @@
# CrossDesk # CrossDesk
#### 跨界连接,高效如一 [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-brightgreen.svg)]()
[![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)
[![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)
[![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 forks](https://img.shields.io/github/forks/kunkundi/crossdesk.svg?style=social)]()
---- [ [English](README_EN.md) / 中文 ]
[English](README_EN.md) / [中文](README.md)
![sup_example](https://github.com/user-attachments/assets/eeb64fbe-1f07-4626-be1c-b77396beb905) ![sup_example](https://github.com/user-attachments/assets/eeb64fbe-1f07-4626-be1c-b77396beb905)
@@ -49,19 +55,7 @@ git submodule init
git submodule update git submodule update
xmake b crossdesk xmake b -vy crossdesk
```
#### 无 CUDA 环境下的开发支持
对于未安装 **CUDA 环境** 的Linux开发者这里提供了预配置的 [Ubuntu 22.04 Docker 镜像](https://hub.docker.com/r/crossdesk/ubuntu22.04)。
该镜像内置必要的构建依赖,可在容器中开箱即用,无需额外配置即可直接编译项目。
进入容器,下载工程后执行:
```
export CUDA_PATH=/usr/local/cuda
export XMAKE_GLOBALDIR=/data
xmake b --root crossdesk
``` ```
运行 运行
@@ -69,10 +63,43 @@ xmake b --root crossdesk
xmake r crossdesk xmake r crossdesk
``` ```
### 无 CUDA 环境下的开发支持
对于**未安装 CUDA 环境的 Linux 开发者**,这里提供了预配置的 [Ubuntu 22.04 Docker 镜像](https://hub.docker.com/r/crossdesk/ubuntu22.04)。该镜像内置必要的构建依赖,可在容器中开箱即用,无需额外配置即可直接编译项目。
进入容器,下载工程后执行:
```
export CUDA_PATH=/usr/local/cuda
export XMAKE_GLOBALDIR=/data
xmake b --root -vy crossdesk
```
对于**未安装 CUDA 环境的 Windows 开发者**,执行下面的命令安装 CUDA 编译环境:
```
xmake require -vy "cuda 12.6.3"
```
安装完成后执行:
```
xmake require --info "cuda 12.6.3"
```
输出如下:
<img width="860" height="226" alt="Image" src="https://github.com/user-attachments/assets/999ac365-581a-4b9a-806e-05eb3e4cf44d" />
根据上述输出获取到 CUDA 的安装目录,即 installdir 指向的位置。将 CUDA_PATH 加入系统环境变量,或在终端中输入:
```
set CUDA_PATH=path_to_cuda_installdir
```
重新执行:
```
xmake b -vy crossdesk
```
#### 注意 #### 注意
运行时如果客户端状态栏显示 **未连接服务器**,请先在 [CrossDesk 官方网站](https://www.crossdesk.cn/) 安装客户端,以便在环境中安装所需的证书文件。 运行时如果客户端状态栏显示 **未连接服务器**,请先在 [CrossDesk 官方网站](https://www.crossdesk.cn/) 安装客户端,以便在环境中安装所需的证书文件。
<img width="129" height="60" alt="image" src="https://github.com/user-attachments/assets/1812f7d6-516b-4b4f-8a3d-98bee505cc5a" /> <img width="256" height="120" alt="image" src="https://github.com/user-attachments/assets/1812f7d6-516b-4b4f-8a3d-98bee505cc5a" />
## 关于 Xmake ## 关于 Xmake
@@ -110,3 +137,171 @@ xmake b -vy crossdesk
xmake r -d crossdesk xmake r -d crossdesk
``` ```
更多使用方法可参考 [Xmake官方文档](https://xmake.io/guide/quick-start.html) 。 更多使用方法可参考 [Xmake官方文档](https://xmake.io/guide/quick-start.html) 。
## 自托管服务器
推荐使用Docker部署CrossDesk Server。
```
sudo docker run -d \
--name crossdesk_server \
--network host \
-e EXTERNAL_IP=xxx.xxx.xxx.xxx \
-e INTERNAL_IP=xxx.xxx.xxx.xxx \
-e CROSSDESK_SERVER_PORT=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/db:/crossdesk-server/db \
-v /path/to/your/logs:/crossdesk-server/logs \
crossdesk/crossdesk-server:v1.0.0
```
上述命令中,用户需注意的参数如下:
- EXTERNAL_IP服务器公网 IP , 对应 CrossDesk 客户端**自托管服务器配置**中填写的**服务器地址**
- INTERNAL_IP服务器内网 IP
- 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/dbCrossDesk Server 设备管理数据库
- /path/to/your/logs日志目录
**注意**
- **/path/to/your/ 是示例路径,请替换为你自己的实际路径。挂载的目录必须事先创建好,否则容器会报错。**
- **服务器需开放端口3478/udp3478/tcpMIN_PORT-MAX_PORT/udpCROSSDESK_SERVER_PORT/tcp。**
## 证书文件
客户端需加载根证书文件,服务端需加载服务器私钥和服务器证书文件。
如果已有SSL证书的用户可以忽略下面的证书生成步骤。
对于无证书的用户,可使用下面的脚本自行生成证书文件:
```
# 创建证书生成脚本
vim generate_certs.sh
```
拷贝到脚本中
```
#!/bin/bash
set -e
# 检查参数
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <SERVER_IP>"
exit 1
fi
SERVER_IP="$1"
# 文件名
ROOT_KEY="crossdesk.cn_root.key"
ROOT_CERT="crossdesk.cn_root.crt"
SERVER_KEY="crossdesk.cn.key"
SERVER_CSR="crossdesk.cn.csr"
SERVER_CERT="crossdesk.cn_bundle.crt"
FULLCHAIN_CERT="crossdesk.cn_fullchain.crt"
# 证书主题
SUBJ="/C=CN/ST=Zhejiang/L=Hangzhou/O=CrossDesk/OU=CrossDesk/CN=$SERVER_IP"
# 1. 生成根证书
echo "Generating root private key..."
openssl genrsa -out "$ROOT_KEY" 4096
echo "Generating self-signed root certificate..."
openssl req -x509 -new -nodes -key "$ROOT_KEY" -sha256 -days 3650 -out "$ROOT_CERT" -subj "$SUBJ"
# 2. 生成服务器私钥
echo "Generating server private key..."
openssl genrsa -out "$SERVER_KEY" 2048
# 3. 生成服务器 CSR
echo "Generating server CSR..."
openssl req -new -key "$SERVER_KEY" -out "$SERVER_CSR" -subj "$SUBJ"
# 4. 生成临时 OpenSSL 配置文件,加入 SAN
SAN_CONF="san.cnf"
cat > $SAN_CONF <<EOL
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
prompt = no
[ req_distinguished_name ]
C = CN
ST = Zhejiang
L = Hangzhou
O = CrossDesk
OU = CrossDesk
CN = $SERVER_IP
[ req_ext ]
subjectAltName = IP:$SERVER_IP
EOL
# 5. 用根证书签发服务器证书(包含 SAN
echo "Signing server certificate with root certificate..."
openssl x509 -req -in "$SERVER_CSR" -CA "$ROOT_CERT" -CAkey "$ROOT_KEY" -CAcreateserial \
-out "$SERVER_CERT" -days 3650 -sha256 -extfile "$SAN_CONF" -extensions req_ext
# 6. 生成完整链证书
cat "$SERVER_CERT" "$ROOT_CERT" > "$FULLCHAIN_CERT"
# 7. 清理中间文件
rm -f "$ROOT_CERT.srl" "$SAN_CONF" "$ROOT_KEY" "$SERVER_CSR" "FULLCHAIN_CERT"
echo "Generation complete. Deployment files:"
echo " Client root certificate: $ROOT_CERT"
echo " Server private key: $SERVER_KEY"
echo " Server certificate: $SERVER_CERT"
```
执行
```
chmod +x generate_certs.sh
./generate_certs.sh 服务器公网IP
# 例如 ./generate_certs.sh 111.111.111.111
```
输出如下:
```
Generating root private key...
Generating self-signed root certificate...
Generating server private key...
Generating server CSR...
Signing server certificate with root certificate...
Certificate request self-signature ok
subject=C = CN, ST = Zhejiang, L = Hangzhou, O = CrossDesk, OU = CrossDesk, CN = xxx.xxx.xxx.xxx
cleaning up intermediate files...
Generation complete. Deployment files::
Client root certificate:: crossdesk.cn_root.crt
Server private key: crossdesk.cn.key
Server certificate: crossdesk.cn_bundle.crt
```
#### 服务端
**crossdesk.cn.key****crossdesk.cn_bundle.crt** 放置到 **/path/to/your/certs** 目录下。
#### 客户端
1. 点击右上角设置进入设置页面。<br>
<img width="600" height="210" alt="image" src="https://github.com/user-attachments/assets/6431131d-b32a-4726-8783-6788f47baa3b" /><br><br>
3. 点击点击**自托管服务器配置**。<br><br>
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/24c761a3-1985-4d7e-84be-787383c2afb8" /><br><br>
5. 在**证书文件路径**选择框中找到 **crossdesk.cn_root.crt** 的存放路径,选中 **crossdesk.cn_root.crt**,点击确认。<br><br>
<img width="600" height="220" alt="image" src="https://github.com/user-attachments/assets/4af7cd3a-c72e-44fb-b032-30e050019c2a" /><br><br>
7. 勾选使用**自托管服务器配置**,点击确认配置生效。<br><br>
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/1e455dc3-4087-4f37-a544-1ff9f8789383" /><br><br>
# 常见问题
见 [常见问题](https://github.com/kunkundi/crossdesk/blob/self-hosted-server/docs/FAQ.md) 。

View File

@@ -1,9 +1,15 @@
# CrossDesk # CrossDesk
#### Bridging work, uniting efficiency [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-brightgreen.svg)]()
[![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)
[![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)
[![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 forks](https://img.shields.io/github/forks/kunkundi/crossdesk.svg?style=social)]()
---- [ [中文](README.md) / English ]
[中文](README.md) / [English](README_EN.md)
![sup_example](https://github.com/user-attachments/assets/3f17d8f3-7c4a-4b63-bae4-903363628687) ![sup_example](https://github.com/user-attachments/assets/3f17d8f3-7c4a-4b63-bae4-903363628687)
@@ -17,15 +23,15 @@ CrossDesk is an experimental application of [MiniRTC](https://github.com/kunkund
Enter the remote desktop ID in the menu bars “Remote ID” field and click “→” to initiate a remote connection. Enter the remote desktop ID in the menu bars “Remote ID” field and click “→” to initiate a remote connection.
![usage1](https://github.com/user-attachments/assets/bf39f8fa-de77-41a1-8db3-73d6cab9da6a) ![usage1](https://github.com/user-attachments/assets/3a4bb59f-c84c-44d2-9a20-11790aac510e)
If the remote desktop requires a connection password, you must enter the correct password on your side to successfully establish the connection. If the remote desktop requires a connection password, you must enter the correct password on your side to successfully establish the connection.
![password](https://github.com/user-attachments/assets/f6556966-a84f-4301-a79b-2726b389ed71) ![password](https://github.com/user-attachments/assets/1beadcce-640d-4f5c-8e77-51917b5294d5)
Before connecting, you can customize configuration options in the settings, such as language and video encoding format. Before connecting, you can customize configuration options in the settings, such as language and video encoding format.
![settings](https://github.com/user-attachments/assets/12f7e9c3-a472-40c1-8fb9-ae7d1ae3865c) ![settings](https://github.com/user-attachments/assets/8bc5468d-7bbb-4e30-95bd-da1f352ac08c)
## How to build ## How to build
@@ -49,20 +55,7 @@ git submodule init
git submodule update git submodule update
xmake b crossdesk xmake b -vy crossdesk
```
#### Development Without CUDA Environment
For developers who do not have a **CUDA environment** installed, a preconfigured [Ubuntu 22.04 Docker image](https://hub.docker.com/r/crossdesk/ubuntu22.04) is provided.
This image comes with all required build dependencies and allows you to build the project directly inside the container without any additional setup.
After entering the container, download the project and run:
```
export CUDA_PATH=/usr/local/cuda
export XMAKE_GLOBALDIR=/data
xmake b --root crossdesk
``` ```
Run: Run:
@@ -70,10 +63,45 @@ Run:
xmake r crossdesk xmake r crossdesk
``` ```
#### Development Without CUDA Environment
For **Linux developers who do not have a CUDA environment** installed, a preconfigured [Ubuntu 22.04 Docker image](https://hub.docker.com/r/crossdesk/ubuntu22.04) is provided.
This image comes with all required build dependencies and allows you to build the project directly inside the container without any additional setup.
After entering the container, download the project and run:
```
export CUDA_PATH=/usr/local/cuda
export XMAKE_GLOBALDIR=/data
xmake b --root -vy crossdesk
```
For **Windows developers without a CUDA environment** installed, run the following command to install the CUDA build environment:
```
xmake require -vy "cuda 12.6.3"
```
After the installation is complete, execute:
```
xmake require --info "cuda 12.6.3"
```
The output will look like this:
<img width="860" height="226" alt="Image" src="https://github.com/user-attachments/assets/999ac365-581a-4b9a-806e-05eb3e4cf44d" />
From the output above, locate the CUDA installation directory — this is the path pointed to by installdir.
Add this path to your system environment variable CUDA_PATH, or set it in the terminal using:
```
set CUDA_PATH=path_to_cuda_installdir:
```
Then re-run:
```
xmake b -vy crossdesk
```
#### Notice #### Notice
If the client status bar shows **Disconnected** during runtime, please first install the client from the [CrossDesk official website](https://www.crossdesk.cn/) to ensure the required certificate files are available in the environment. If the client status bar shows **Disconnected** during runtime, please first install the client from the [CrossDesk official website](https://www.crossdesk.cn/) to ensure the required certificate files are available in the environment.
<img width="108" height="57" alt="image" src="https://github.com/user-attachments/assets/26e8b9f3-b326-410e-9466-dd073eaf675a" /> <img width="256" height="120" alt="image" src="https://github.com/user-attachments/assets/1812f7d6-516b-4b4f-8a3d-98bee505cc5a" />
## About Xmake ## About Xmake
#### Installing Xmake #### Installing Xmake
@@ -114,3 +142,171 @@ xmake r -d crossdesk
``` ```
For more information, please refer to the [official Xmake documentation](https://xmake.io/guide/quick-start.html) . For more information, please refer to the [official Xmake documentation](https://xmake.io/guide/quick-start.html) .
## Self-Hosted Server
It is recommended to deploy CrossDesk Server using Docker.
```
sudo docker run -d \
--name crossdesk_server \
--network host \
-e EXTERNAL_IP=xxx.xxx.xxx.xxx \
-e INTERNAL_IP=xxx.xxx.xxx.xxx \
-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/db:/crossdesk-server/db \
-v /path/to/your/logs:/crossdesk-server/logs \
crossdesk/crossdesk-server:v1.0.0
```
The parameters you need to pay attention to are as follows:
- **EXTERNAL_IP**: The server's public IP, corresponding to the **Server Address** in the CrossDesk client **Self-Hosted Server Configuration**.
- **INTERNAL_IP**: The server's internal IP.
- **CROSSDESK_SERVER_PORT**: The port used by the self-hosted server, corresponding to the **Server Port** in the CrossDesk client **Self-Hosted Server Configuration**.
- **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/db**: CrossDesk Server device management database.
- **/path/to/your/logs**: Log directory.
**Note**:
- **/path/to/your/ is an example path; please replace it with your actual path. The mounted directories must be created in advance, otherwise the container will fail.**
- **The server must open the following ports: 3478/udp, 3478/tcp, 30000-60000/udp, CROSSDESK_SERVER_PORT/tcp.**
## Certificate Files
The client needs to load the root certificate, and the server needs to load the server private key and server certificate.
If you already have an SSL certificate, you can skip the following certificate generation steps.
For users without a certificate, you can use the script below to generate the certificate files:
```
# Create certificate generation script
vim generate_certs.sh
```
Copy the following into the script:
```
#!/bin/bash
set -e
# Check arguments
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <SERVER_IP>"
exit 1
fi
SERVER_IP="$1"
# Filenames
ROOT_KEY="crossdesk.cn_root.key"
ROOT_CERT="crossdesk.cn_root.crt"
SERVER_KEY="crossdesk.cn.key"
SERVER_CSR="crossdesk.cn.csr"
SERVER_CERT="crossdesk.cn_bundle.crt"
FULLCHAIN_CERT="crossdesk.cn_fullchain.crt"
# Certificate subject
SUBJ="/C=CN/ST=Zhejiang/L=Hangzhou/O=CrossDesk/OU=CrossDesk/CN=$SERVER_IP"
# 1. Generate root certificate
echo "Generating root private key..."
openssl genrsa -out "$ROOT_KEY" 4096
echo "Generating self-signed root certificate..."
openssl req -x509 -new -nodes -key "$ROOT_KEY" -sha256 -days 3650 -out "$ROOT_CERT" -subj "$SUBJ"
# 2. Generate server private key
echo "Generating server private key..."
openssl genrsa -out "$SERVER_KEY" 2048
# 3. Generate server CSR
echo "Generating server CSR..."
openssl req -new -key "$SERVER_KEY" -out "$SERVER_CSR" -subj "$SUBJ"
# 4. Create temporary OpenSSL config file with SAN
SAN_CONF="san.cnf"
cat > $SAN_CONF <<EOL
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
prompt = no
[ req_distinguished_name ]
C = CN
ST = Zhejiang
L = Hangzhou
O = CrossDesk
OU = CrossDesk
CN = $SERVER_IP
[ req_ext ]
subjectAltName = IP:$SERVER_IP
EOL
# 5. Sign server certificate with root certificate (including SAN)
echo "Signing server certificate with root certificate..."
openssl x509 -req -in "$SERVER_CSR" -CA "$ROOT_CERT" -CAkey "$ROOT_KEY" -CAcreateserial \
-out "$SERVER_CERT" -days 3650 -sha256 -extfile "$SAN_CONF" -extensions req_ext
# 6. Generate full chain certificate
cat "$SERVER_CERT" "$ROOT_CERT" > "$FULLCHAIN_CERT"
# 7. Clean up intermediate files
rm -f "$ROOT_CERT.srl" "$SAN_CONF" "$ROOT_KEY" "$SERVER_CSR" "FULLCHAIN_CERT"
echo "Generation complete. Deployment files:"
echo " Client root certificate: $ROOT_CERT"
echo " Server private key: $SERVER_KEY"
echo " Server certificate: $SERVER_CERT"
```
Execute:
```
chmod +x generate_certs.sh
./generate_certs.sh EXTERNAL_IP
# example ./generate_certs.sh 111.111.111.111
```
Expected output:
```
Generating root private key...
Generating self-signed root certificate...
Generating server private key...
Generating server CSR...
Signing server certificate with root certificate...
Certificate request self-signature ok
subject=C = CN, ST = Zhejiang, L = Hangzhou, O = CrossDesk, OU = CrossDesk, CN = xxx.xxx.xxx.xxx
cleaning up intermediate files...
Generation complete. Deployment files::
Client root certificate:: crossdesk.cn_root.crt
Server private key: crossdesk.cn.key
Server certificate: crossdesk.cn_bundle.crt
```
#### Server Side
Place **crossdesk.cn.key** and **crossdesk.cn_bundle.crt** into the **/path/to/your/certs** directory.
#### Client Side
1. Click the settings icon in the top-right corner to enter the settings page.<br>
<img width="600" height="210" alt="image" src="https://github.com/user-attachments/assets/6431131d-b32a-4726-8783-6788f47baa3b" /><br><br>
2. Click **Self-Hosted Server Configuration**.<br><br>
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/24c761a3-1985-4d7e-84be-787383c2afb8" /><br><br>
3. In the **Certificate File Path** selection, locate and select the **crossdesk.cn_root.crt** file.<br><br>
<img width="600" height="220" alt="image" src="https://github.com/user-attachments/assets/4af7cd3a-c72e-44fb-b032-30e050019c2a" /><br><br>
4. Check the option to use **Self-Hosted Server Configuration**.<br><br>
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/1e455dc3-4087-4f37-a544-1ff9f8789383" /><br><br>
# FAQ
See [FAQ](https://github.com/kunkundi/crosssesk/blob/self-hosted-server/docs/FAQ.md) .

33
docs/FAQ.md Normal file
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

@@ -9,8 +9,8 @@
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[]) { int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
Render render; crossdesk::Render render;
render.Run(); render.Run();
return 0; return 0;

View File

@@ -9,6 +9,8 @@
#include <string> #include <string>
namespace crossdesk {
class DisplayInfo { class DisplayInfo {
public: public:
DisplayInfo(std::string name, int left, int top, int right, int bottom) DisplayInfo(std::string name, int left, int top, int right, int bottom)
@@ -40,5 +42,5 @@ class DisplayInfo {
int width = 0; int width = 0;
int height = 0; int height = 0;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -19,6 +19,8 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
namespace crossdesk {
std::string GetMac() { std::string GetMac() {
char mac_addr[16]; char mac_addr[16];
int len = 0; int len = 0;
@@ -39,21 +41,21 @@ std::string GetMac() {
#elif __APPLE__ #elif __APPLE__
std::string if_name = "en0"; std::string if_name = "en0";
struct ifaddrs *addrs; struct ifaddrs* addrs;
struct ifaddrs *cursor; struct ifaddrs* cursor;
const struct sockaddr_dl *dlAddr; const struct sockaddr_dl* dlAddr;
if (!getifaddrs(&addrs)) { if (!getifaddrs(&addrs)) {
cursor = addrs; cursor = addrs;
while (cursor != 0) { while (cursor != 0) {
const struct sockaddr_dl *socAddr = const struct sockaddr_dl* socAddr =
(const struct sockaddr_dl *)cursor->ifa_addr; (const struct sockaddr_dl*)cursor->ifa_addr;
if ((cursor->ifa_addr->sa_family == AF_LINK) && if ((cursor->ifa_addr->sa_family == AF_LINK) &&
(socAddr->sdl_type == IFT_ETHER) && (socAddr->sdl_type == IFT_ETHER) &&
strcmp(if_name.c_str(), cursor->ifa_name) == 0) { strcmp(if_name.c_str(), cursor->ifa_name) == 0) {
dlAddr = (const struct sockaddr_dl *)cursor->ifa_addr; dlAddr = (const struct sockaddr_dl*)cursor->ifa_addr;
const unsigned char *base = const unsigned char* base =
(const unsigned char *)&dlAddr->sdl_data[dlAddr->sdl_nlen]; (const unsigned char*)&dlAddr->sdl_data[dlAddr->sdl_nlen];
for (int i = 0; i < dlAddr->sdl_alen; i++) { for (int i = 0; i < dlAddr->sdl_alen; i++) {
len += len +=
snprintf(mac_addr + len, sizeof(mac_addr) - len, "%.2X", base[i]); snprintf(mac_addr + len, sizeof(mac_addr) - len, "%.2X", base[i]);
@@ -77,8 +79,8 @@ std::string GetMac() {
close(sock); close(sock);
return ""; return "";
} }
struct ifreq *it = ifc.ifc_req; struct ifreq* it = ifc.ifc_req;
const struct ifreq *const end = it + (ifc.ifc_len / sizeof(struct ifreq)); const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
for (; it != end; ++it) { for (; it != end; ++it) {
std::strcpy(ifr.ifr_name, it->ifr_name); std::strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
@@ -122,4 +124,5 @@ std::string GetHostName() {
} }
#endif #endif
return hostname; return hostname;
} }
} // namespace crossdesk

View File

@@ -9,7 +9,10 @@
#include <iostream> #include <iostream>
namespace crossdesk {
std::string GetMac(); std::string GetMac();
std::string GetHostName(); std::string GetHostName();
} // namespace crossdesk
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,64 +1,269 @@
#include "config_center.h" #include "config_center.h"
#include "rd_log.h" namespace crossdesk {
ConfigCenter::ConfigCenter() {} ConfigCenter::ConfigCenter(const std::string& config_path,
const std::string& cert_file_path)
: config_path_(config_path),
cert_file_path_(cert_file_path),
cert_file_path_default_(cert_file_path) {
ini_.SetUnicode(true);
Load();
}
ConfigCenter::~ConfigCenter() {} ConfigCenter::~ConfigCenter() {}
int ConfigCenter::Load() {
SI_Error rc = ini_.LoadFile(config_path_.c_str());
if (rc < 0) {
Save();
return -1;
}
language_ = static_cast<LANGUAGE>(
ini_.GetLongValue(section_, "language", static_cast<long>(language_)));
video_quality_ = static_cast<VIDEO_QUALITY>(ini_.GetLongValue(
section_, "video_quality", static_cast<long>(video_quality_)));
video_frame_rate_ = static_cast<VIDEO_FRAME_RATE>(ini_.GetLongValue(
section_, "video_frame_rate", static_cast<long>(video_frame_rate_)));
video_encode_format_ = static_cast<VIDEO_ENCODE_FORMAT>(
ini_.GetLongValue(section_, "video_encode_format",
static_cast<long>(video_encode_format_)));
hardware_video_codec_ = ini_.GetBoolValue(section_, "hardware_video_codec",
hardware_video_codec_);
enable_turn_ = ini_.GetBoolValue(section_, "enable_turn", enable_turn_);
enable_srtp_ = ini_.GetBoolValue(section_, "enable_srtp", enable_srtp_);
signal_server_host_ = ini_.GetValue(section_, "signal_server_host",
signal_server_host_.c_str());
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_ =
ini_.GetValue(section_, "cert_file_path", cert_file_path_.c_str());
enable_self_hosted_ =
ini_.GetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
enable_minimize_to_tray_ = ini_.GetBoolValue(
section_, "enable_minimize_to_tray", enable_minimize_to_tray_);
return 0;
}
int ConfigCenter::Save() {
ini_.SetLongValue(section_, "language", static_cast<long>(language_));
ini_.SetLongValue(section_, "video_quality",
static_cast<long>(video_quality_));
ini_.SetLongValue(section_, "video_frame_rate",
static_cast<long>(video_frame_rate_));
ini_.SetLongValue(section_, "video_encode_format",
static_cast<long>(video_encode_format_));
ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
ini_.SetBoolValue(section_, "enable_turn", enable_turn_);
ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
ini_.SetLongValue(section_, "signal_server_port",
static_cast<long>(signal_server_port_));
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
ini_.SetBoolValue(section_, "enable_minimize_to_tray",
enable_minimize_to_tray_);
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0;
}
// setters
int ConfigCenter::SetLanguage(LANGUAGE language) { int ConfigCenter::SetLanguage(LANGUAGE language) {
language_ = language; language_ = language;
ini_.SetLongValue(section_, "language", static_cast<long>(language_));
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0; return 0;
} }
int ConfigCenter::SetVideoQuality(VIDEO_QUALITY video_quality) { int ConfigCenter::SetVideoQuality(VIDEO_QUALITY video_quality) {
video_quality_ = video_quality; video_quality_ = video_quality;
ini_.SetLongValue(section_, "video_quality",
static_cast<long>(video_quality_));
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0; return 0;
} }
int ConfigCenter::SetVideoFrameRate(VIDEO_FRAME_RATE video_frame_rate) { int ConfigCenter::SetVideoFrameRate(VIDEO_FRAME_RATE video_frame_rate) {
video_frame_rate_ = video_frame_rate; video_frame_rate_ = video_frame_rate;
ini_.SetLongValue(section_, "video_frame_rate",
static_cast<long>(video_frame_rate_));
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0; return 0;
} }
int ConfigCenter::SetVideoEncodeFormat( int ConfigCenter::SetVideoEncodeFormat(
VIDEO_ENCODE_FORMAT video_encode_format) { VIDEO_ENCODE_FORMAT video_encode_format) {
video_encode_format_ = video_encode_format; video_encode_format_ = video_encode_format;
ini_.SetLongValue(section_, "video_encode_format",
static_cast<long>(video_encode_format_));
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0; return 0;
} }
int ConfigCenter::SetHardwareVideoCodec(bool hardware_video_codec) { int ConfigCenter::SetHardwareVideoCodec(bool hardware_video_codec) {
hardware_video_codec_ = hardware_video_codec; hardware_video_codec_ = hardware_video_codec;
ini_.SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0; return 0;
} }
int ConfigCenter::SetTurn(bool enable_turn) { int ConfigCenter::SetTurn(bool enable_turn) {
enable_turn_ = enable_turn; enable_turn_ = enable_turn;
ini_.SetBoolValue(section_, "enable_turn", enable_turn_);
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0; return 0;
} }
int ConfigCenter::SetSrtp(bool enable_srtp) { int ConfigCenter::SetSrtp(bool enable_srtp) {
enable_srtp_ = enable_srtp; enable_srtp_ = enable_srtp;
ini_.SetBoolValue(section_, "enable_srtp", enable_srtp_);
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0; return 0;
} }
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() { return language_; } int ConfigCenter::SetServerHost(const std::string& signal_server_host) {
signal_server_host_ = signal_server_host;
ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0;
}
ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() { int ConfigCenter::SetServerPort(int signal_server_port) {
signal_server_port_ = signal_server_port;
ini_.SetLongValue(section_, "signal_server_port",
static_cast<long>(signal_server_port_));
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
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) {
cert_file_path_ = cert_file_path;
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0;
}
int ConfigCenter::SetSelfHosted(bool enable_self_hosted) {
enable_self_hosted_ = enable_self_hosted;
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0;
}
int ConfigCenter::SetMinimizeToTray(bool enable_minimize_to_tray) {
enable_minimize_to_tray_ = enable_minimize_to_tray;
return 0;
}
// getters
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; }
ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() const {
return video_quality_; return video_quality_;
} }
int ConfigCenter::GetVideoFrameRate() { ConfigCenter::VIDEO_FRAME_RATE ConfigCenter::GetVideoFrameRate() const {
int fps = video_frame_rate_ == VIDEO_FRAME_RATE::FPS_30 ? 30 : 60; return video_frame_rate_;
return fps;
} }
ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() { ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() const {
return video_encode_format_; return video_encode_format_;
} }
bool ConfigCenter::IsHardwareVideoCodec() { return hardware_video_codec_; } bool ConfigCenter::IsHardwareVideoCodec() const {
return hardware_video_codec_;
}
bool ConfigCenter::IsEnableTurn() { return enable_turn_; } bool ConfigCenter::IsEnableTurn() const { return enable_turn_; }
bool ConfigCenter::IsEnableSrtp() { return enable_srtp_; } bool ConfigCenter::IsEnableSrtp() const { return enable_srtp_; }
std::string ConfigCenter::GetSignalServerHost() const {
return signal_server_host_;
}
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::GetDefaultServerHost() const {
return signal_server_host_default_;
}
int ConfigCenter::GetDefaultSignalServerPort() const {
return server_port_default_;
}
int ConfigCenter::GetDefaultCoturnServerPort() const {
return coturn_server_port_default_;
}
std::string ConfigCenter::GetDefaultCertFilePath() const {
return cert_file_path_default_;
}
bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }
bool ConfigCenter::IsMinimizeToTray() const { return enable_minimize_to_tray_; }
} // namespace crossdesk

View File

@@ -7,18 +7,26 @@
#ifndef _CONFIG_CENTER_H_ #ifndef _CONFIG_CENTER_H_
#define _CONFIG_CENTER_H_ #define _CONFIG_CENTER_H_
#include <string>
#include "SimpleIni.h"
namespace crossdesk {
class ConfigCenter { class ConfigCenter {
public: public:
enum class LANGUAGE { CHINESE = 0, ENGLISH = 1 }; enum class LANGUAGE { CHINESE = 0, ENGLISH = 1 };
enum class VIDEO_QUALITY { LOW = 0, MEDIUM = 1, HIGH = 2 }; enum class VIDEO_QUALITY { LOW = 0, MEDIUM = 1, HIGH = 2 };
enum class VIDEO_FRAME_RATE { FPS_30 = 0, FPS_60 = 1 }; enum class VIDEO_FRAME_RATE { FPS_30 = 0, FPS_60 = 1 };
enum class VIDEO_ENCODE_FORMAT { AV1 = 0, H264 = 1 }; enum class VIDEO_ENCODE_FORMAT { H264 = 0, AV1 = 1 };
public: public:
ConfigCenter(); explicit ConfigCenter(
const std::string& config_path = "config.ini",
const std::string& cert_file_path = "crossdesk.cn_root.crt");
~ConfigCenter(); ~ConfigCenter();
public: // write config
int SetLanguage(LANGUAGE language); int SetLanguage(LANGUAGE language);
int SetVideoQuality(VIDEO_QUALITY video_quality); int SetVideoQuality(VIDEO_QUALITY video_quality);
int SetVideoFrameRate(VIDEO_FRAME_RATE video_frame_rate); int SetVideoFrameRate(VIDEO_FRAME_RATE video_frame_rate);
@@ -26,25 +34,58 @@ 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& signal_server_host);
int SetServerPort(int signal_server_port);
int SetCoturnServerPort(int coturn_server_port);
int SetCertFilePath(const std::string& cert_file_path);
int SetSelfHosted(bool enable_self_hosted);
int SetMinimizeToTray(bool enable_minimize_to_tray);
public: // read config
LANGUAGE GetLanguage();
VIDEO_QUALITY GetVideoQuality(); LANGUAGE GetLanguage() const;
int GetVideoFrameRate(); VIDEO_QUALITY GetVideoQuality() const;
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat(); VIDEO_FRAME_RATE GetVideoFrameRate() const;
bool IsHardwareVideoCodec(); VIDEO_ENCODE_FORMAT GetVideoEncodeFormat() const;
bool IsEnableTurn(); bool IsHardwareVideoCodec() const;
bool IsEnableSrtp(); bool IsEnableTurn() const;
bool IsEnableSrtp() const;
std::string GetSignalServerHost() const;
int GetSignalServerPort() const;
int GetCoturnServerPort() const;
std::string GetCertFilePath() const;
std::string GetDefaultServerHost() const;
int GetDefaultSignalServerPort() const;
int GetDefaultCoturnServerPort() const;
std::string GetDefaultCertFilePath() const;
bool IsSelfHosted() const;
bool IsMinimizeToTray() const;
int Load();
int Save();
private: private:
// Default value should be same with parameters in localization.h std::string config_path_;
std::string cert_file_path_;
CSimpleIniA ini_;
const char* section_ = "Settings";
LANGUAGE language_ = LANGUAGE::CHINESE; LANGUAGE language_ = LANGUAGE::CHINESE;
VIDEO_QUALITY video_quality_ = VIDEO_QUALITY::MEDIUM; VIDEO_QUALITY video_quality_ = VIDEO_QUALITY::MEDIUM;
VIDEO_FRAME_RATE video_frame_rate_ = VIDEO_FRAME_RATE::FPS_30; VIDEO_FRAME_RATE video_frame_rate_ = VIDEO_FRAME_RATE::FPS_30;
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::AV1; VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::H264;
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 signal_server_host_ = "api.crossdesk.cn";
std::string signal_server_host_default_ = "api.crossdesk.cn";
int signal_server_port_ = 9099;
int server_port_default_ = 9099;
int coturn_server_port_ = 3478;
int coturn_server_port_default_ = 3478;
std::string cert_file_path_default_ = "";
bool enable_self_hosted_ = false;
bool enable_minimize_to_tray_ = false;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -11,6 +11,8 @@
#include "display_info.h" #include "display_info.h"
namespace crossdesk {
typedef enum { typedef enum {
mouse = 0, mouse = 0,
keyboard, keyboard,
@@ -79,5 +81,5 @@ class DeviceController {
// virtual int Hook(); // virtual int Hook();
// virtual int Unhook(); // virtual int Unhook();
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -11,6 +11,8 @@
#include "keyboard_capturer.h" #include "keyboard_capturer.h"
#include "mouse_controller.h" #include "mouse_controller.h"
namespace crossdesk {
class DeviceControllerFactory { class DeviceControllerFactory {
public: public:
enum Device { Mouse = 0, Keyboard }; enum Device { Mouse = 0, Keyboard };
@@ -30,5 +32,5 @@ class DeviceControllerFactory {
} }
} }
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -3,6 +3,8 @@
#include "keyboard_converter.h" #include "keyboard_converter.h"
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
static OnKeyAction g_on_key_action = nullptr; static OnKeyAction g_on_key_action = nullptr;
static void* g_user_ptr = nullptr; static void* g_user_ptr = nullptr;
@@ -67,3 +69,4 @@ int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down) {
} }
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -13,20 +13,22 @@
#include "device_controller.h" #include "device_controller.h"
namespace crossdesk {
class KeyboardCapturer : public DeviceController { class KeyboardCapturer : public DeviceController {
public: public:
KeyboardCapturer(); KeyboardCapturer();
virtual ~KeyboardCapturer(); virtual ~KeyboardCapturer();
public: public:
virtual int Hook(OnKeyAction on_key_action, void *user_ptr); virtual int Hook(OnKeyAction on_key_action, void* user_ptr);
virtual int Unhook(); virtual int Unhook();
virtual int SendKeyboardCommand(int key_code, bool is_down); virtual int SendKeyboardCommand(int key_code, bool is_down);
private: private:
Display *display_; Display* display_;
Window root_; Window root_;
bool running_; bool running_;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -3,12 +3,14 @@
#include "keyboard_converter.h" #include "keyboard_converter.h"
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
static OnKeyAction g_on_key_action = nullptr; static OnKeyAction g_on_key_action = nullptr;
static void *g_user_ptr = nullptr; static void* g_user_ptr = nullptr;
CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type,
CGEventRef event, void *userInfo) { CGEventRef event, void* userInfo) {
KeyboardCapturer *keyboard_capturer = (KeyboardCapturer *)userInfo; KeyboardCapturer* keyboard_capturer = (KeyboardCapturer*)userInfo;
if (!keyboard_capturer) { if (!keyboard_capturer) {
LOG_ERROR("keyboard_capturer is nullptr"); LOG_ERROR("keyboard_capturer is nullptr");
return event; return event;
@@ -91,7 +93,7 @@ KeyboardCapturer::KeyboardCapturer() {}
KeyboardCapturer::~KeyboardCapturer() {} KeyboardCapturer::~KeyboardCapturer() {}
int KeyboardCapturer::Hook(OnKeyAction on_key_action, void *user_ptr) { int KeyboardCapturer::Hook(OnKeyAction on_key_action, void* user_ptr) {
g_on_key_action = on_key_action; g_on_key_action = on_key_action;
g_user_ptr = user_ptr; g_user_ptr = user_ptr;
@@ -164,4 +166,5 @@ int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down) {
} }
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -11,13 +11,15 @@
#include "device_controller.h" #include "device_controller.h"
namespace crossdesk {
class KeyboardCapturer : public DeviceController { class KeyboardCapturer : public DeviceController {
public: public:
KeyboardCapturer(); KeyboardCapturer();
virtual ~KeyboardCapturer(); virtual ~KeyboardCapturer();
public: public:
virtual int Hook(OnKeyAction on_key_action, void *user_ptr); virtual int Hook(OnKeyAction on_key_action, void* user_ptr);
virtual int Unhook(); virtual int Unhook();
virtual int SendKeyboardCommand(int key_code, bool is_down); virtual int SendKeyboardCommand(int key_code, bool is_down);
@@ -33,5 +35,5 @@ class KeyboardCapturer : public DeviceController {
bool command_flag_ = false; bool command_flag_ = false;
int fn_key_code_ = 0x3F; int fn_key_code_ = 0x3F;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -2,6 +2,8 @@
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
static OnKeyAction g_on_key_action = nullptr; static OnKeyAction g_on_key_action = nullptr;
static void* g_user_ptr = nullptr; static void* g_user_ptr = nullptr;
@@ -53,4 +55,5 @@ int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down) {
SendInput(1, &input, sizeof(INPUT)); SendInput(1, &input, sizeof(INPUT));
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -11,18 +11,21 @@
#include "device_controller.h" #include "device_controller.h"
namespace crossdesk {
class KeyboardCapturer : public DeviceController { class KeyboardCapturer : public DeviceController {
public: public:
KeyboardCapturer(); KeyboardCapturer();
virtual ~KeyboardCapturer(); virtual ~KeyboardCapturer();
public: public:
virtual int Hook(OnKeyAction on_key_action, void *user_ptr); virtual int Hook(OnKeyAction on_key_action, void* user_ptr);
virtual int Unhook(); virtual int Unhook();
virtual int SendKeyboardCommand(int key_code, bool is_down); virtual int SendKeyboardCommand(int key_code, bool is_down);
private: private:
HHOOK keyboard_hook_ = nullptr; HHOOK keyboard_hook_ = nullptr;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -9,6 +9,8 @@
#include <map> #include <map>
namespace crossdesk {
// Windows vkCode to macOS CGKeyCode (104 keys) // Windows vkCode to macOS CGKeyCode (104 keys)
std::map<int, int> vkCodeToCGKeyCode = { std::map<int, int> vkCodeToCGKeyCode = {
// A-Z // A-Z
@@ -736,5 +738,5 @@ std::map<int, int> x11KeySymToCgKeyCode = {
{0xFFEB, 0x37}, // Left Command {0xFFEB, 0x37}, // Left Command
{0xFFEC, 0x36}, // Right Command {0xFFEC, 0x36}, // Right Command
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -4,6 +4,8 @@
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
MouseController::MouseController() {} MouseController::MouseController() {}
MouseController::~MouseController() { Destroy(); } MouseController::~MouseController() { Destroy(); }
@@ -121,4 +123,5 @@ void MouseController::SimulateMouseWheel(int direction_button, int count) {
XTestFakeButtonEvent(display_, direction_button, False, CurrentTime); XTestFakeButtonEvent(display_, direction_button, False, CurrentTime);
} }
XFlush(display_); XFlush(display_);
} }
} // namespace crossdesk

View File

@@ -15,6 +15,8 @@
#include "device_controller.h" #include "device_controller.h"
namespace crossdesk {
class MouseController : public DeviceController { class MouseController : public DeviceController {
public: public:
MouseController(); MouseController();
@@ -37,5 +39,5 @@ class MouseController : public DeviceController {
int screen_width_ = 0; int screen_width_ = 0;
int screen_height_ = 0; int screen_height_ = 0;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -4,6 +4,8 @@
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
MouseController::MouseController() {} MouseController::MouseController() {}
MouseController::~MouseController() {} MouseController::~MouseController() {}
@@ -98,4 +100,5 @@ int MouseController::SendMouseCommand(RemoteAction remote_action,
} }
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -11,6 +11,8 @@
#include "device_controller.h" #include "device_controller.h"
namespace crossdesk {
class MouseController : public DeviceController { class MouseController : public DeviceController {
public: public:
MouseController(); MouseController();
@@ -26,5 +28,5 @@ class MouseController : public DeviceController {
bool left_dragging_ = false; bool left_dragging_ = false;
bool right_dragging_ = false; bool right_dragging_ = false;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -2,6 +2,8 @@
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
MouseController::MouseController() {} MouseController::MouseController() {}
MouseController::~MouseController() {} MouseController::~MouseController() {}
@@ -69,4 +71,5 @@ int MouseController::SendMouseCommand(RemoteAction remote_action,
} }
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -11,6 +11,8 @@
#include "device_controller.h" #include "device_controller.h"
namespace crossdesk {
class MouseController : public DeviceController { class MouseController : public DeviceController {
public: public:
MouseController(); MouseController();
@@ -24,5 +26,5 @@ class MouseController : public DeviceController {
private: private:
std::vector<DisplayInfo> display_info_list_; std::vector<DisplayInfo> display_info_list_;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -0,0 +1,63 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-06-14
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LAYOUT_STYLE_H_
#define _LAYOUT_STYLE_H_
#define MENU_WINDOW_WIDTH_CN 300
#define MENU_WINDOW_HEIGHT_CN 280
#define LOCAL_WINDOW_WIDTH_CN 300
#define LOCAL_WINDOW_HEIGHT_CN 280
#define REMOTE_WINDOW_WIDTH_CN 300
#define REMOTE_WINDOW_HEIGHT_CN 280
#define MENU_WINDOW_WIDTH_EN 190
#define MENU_WINDOW_HEIGHT_EN 245
#define IPUT_WINDOW_WIDTH 160
#define INPUT_WINDOW_PADDING_CN 66
#define INPUT_WINDOW_PADDING_EN 96
#define SETTINGS_WINDOW_WIDTH_CN 202
#define SETTINGS_WINDOW_WIDTH_EN 248
#if _WIN32
#define SETTINGS_WINDOW_HEIGHT_CN 345
#define SETTINGS_WINDOW_HEIGHT_EN 345
#else
#define SETTINGS_WINDOW_HEIGHT_CN 315
#define SETTINGS_WINDOW_HEIGHT_EN 315
#endif
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN 195
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN 195
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 120
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 167
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 120
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 167
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN 120
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN 167
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 120
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 167
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 171
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218
#define ENABLE_TURN_CHECKBOX_PADDING_CN 171
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218
#define ENABLE_SRTP_CHECKBOX_PADDING_CN 171
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218
#define ENABLE_MINIZE_TO_TRAY_PADDING_CN 171
#define ENABLE_MINIZE_TO_TRAY_PADDING_EN 218
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN 90
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN 90
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN 137
#define SETTINGS_SELECT_WINDOW_WIDTH 73
#define SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH 130
#define SETTINGS_OK_BUTTON_PADDING_CN 65
#define SETTINGS_OK_BUTTON_PADDING_EN 83
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN 78
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN 91
#endif

View File

@@ -8,6 +8,13 @@
#include <string> #include <string>
#include <vector> #include <vector>
#if _WIN32
#include <Windows.h>
#endif
namespace crossdesk {
namespace localization { namespace localization {
static std::vector<std::string> local_desktop = { static std::vector<std::string> local_desktop = {
@@ -91,10 +98,24 @@ static std::vector<std::string> enable_hardware_video_codec = {
"Enable Hardware Video Codec:"}; "Enable Hardware Video Codec:"};
static std::vector<std::string> enable_turn = { static std::vector<std::string> enable_turn = {
reinterpret_cast<const char*>(u8"启用中继服务:"), "Enable TURN Service:"}; reinterpret_cast<const char*>(u8"启用中继服务:"), "Enable TURN Service:"};
static std::vector<std::string> enable_srtp = { static std::vector<std::string> enable_srtp = {
reinterpret_cast<const char*>(u8"启用SRTP:"), "Enable SRTP:"}; reinterpret_cast<const char*>(u8"启用SRTP:"), "Enable SRTP:"};
static std::vector<std::string> self_hosted_server_config = {
reinterpret_cast<const char*>(u8"自托管服务器配置"),
"Self-Hosted Server Config"};
static std::vector<std::string> self_hosted_server_settings = {
reinterpret_cast<const char*>(u8"自托管服务器设置"),
"Self-Hosted Server Settings"};
static std::vector<std::string> self_hosted_server_address = {
reinterpret_cast<const char*>(u8"服务器地址:"), "Server Address:"};
static std::vector<std::string> self_hosted_server_port = {
reinterpret_cast<const char*>(u8"信令服务端口:"), "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 = {
reinterpret_cast<const char*>(u8"证书文件路径:"), "Certificate File Path:"};
static std::vector<std::string> select_a_file = {
reinterpret_cast<const char*>(u8"请选择文件"), "Please select a file"};
static std::vector<std::string> ok = {reinterpret_cast<const char*>(u8"确认"), static std::vector<std::string> ok = {reinterpret_cast<const char*>(u8"确认"),
"OK"}; "OK"};
static std::vector<std::string> cancel = { static std::vector<std::string> cancel = {
@@ -143,6 +164,13 @@ 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
} // namespace crossdesk
#endif #endif

View File

@@ -1,10 +1,12 @@
#include <random> #include <random>
#include "layout_style.h" #include "layout.h"
#include "localization.h" #include "localization.h"
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
namespace crossdesk {
int Render::LocalWindow() { int Render::LocalWindow() {
ImGui::SetNextWindowPos(ImVec2(-1.0f, title_bar_height_), ImGuiCond_Always); ImGui::SetNextWindowPos(ImVec2(-1.0f, title_bar_height_), ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
@@ -287,4 +289,5 @@ int Render::LocalWindow() {
ImGui::PopStyleVar(); ImGui::PopStyleVar();
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -2,6 +2,8 @@
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
namespace crossdesk {
int Render::RecentConnectionsWindow() { int Render::RecentConnectionsWindow() {
ImGui::SetNextWindowPos( ImGui::SetNextWindowPos(
ImVec2(0, title_bar_height_ + local_window_height_ - 1.0f), ImVec2(0, title_bar_height_ + local_window_height_ - 1.0f),
@@ -284,3 +286,4 @@ int Render::ConfirmDeleteConnection() {
ImGui::PopStyleVar(); ImGui::PopStyleVar();
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -1,9 +1,11 @@
#include "layout_style.h" #include "layout.h"
#include "localization.h" #include "localization.h"
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
static int InputTextCallback(ImGuiInputTextCallbackData *data); namespace crossdesk {
static int InputTextCallback(ImGuiInputTextCallbackData* data);
int Render::RemoteWindow() { int Render::RemoteWindow() {
ImGui::SetNextWindowPos(ImVec2(local_window_width_ + 1.0f, title_bar_height_), ImGui::SetNextWindowPos(ImVec2(local_window_width_ + 1.0f, title_bar_height_),
@@ -77,7 +79,7 @@ int Render::RemoteWindow() {
enter_pressed) { enter_pressed) {
connect_button_pressed_ = true; connect_button_pressed_ = true;
bool found = false; bool found = false;
for (auto &[id, props] : recent_connections_) { for (auto& [id, props] : recent_connections_) {
if (id.find(remote_id) != std::string::npos) { if (id.find(remote_id) != std::string::npos) {
found = true; found = true;
if (client_properties_.find(remote_id) != if (client_properties_.find(remote_id) !=
@@ -101,7 +103,7 @@ int Render::RemoteWindow() {
if (need_to_rejoin_) { if (need_to_rejoin_) {
need_to_rejoin_ = false; need_to_rejoin_ = false;
for (const auto &[_, props] : client_properties_) { for (const auto& [_, props] : client_properties_) {
if (props->rejoin_) { if (props->rejoin_) {
ConnectTo(props->remote_id_, props->remote_password_, ConnectTo(props->remote_id_, props->remote_password_,
props->remember_password_); props->remember_password_);
@@ -117,7 +119,7 @@ int Render::RemoteWindow() {
return 0; return 0;
} }
static int InputTextCallback(ImGuiInputTextCallbackData *data) { static int InputTextCallback(ImGuiInputTextCallbackData* data) {
if (data->BufTextLen > 3 && data->Buf[3] != ' ') { if (data->BufTextLen > 3 && data->Buf[3] != ' ') {
data->InsertChars(3, " "); data->InsertChars(3, " ");
} }
@@ -129,7 +131,7 @@ static int InputTextCallback(ImGuiInputTextCallbackData *data) {
return 0; return 0;
} }
int Render::ConnectTo(const std::string &remote_id, const char *password, int Render::ConnectTo(const std::string& remote_id, const char* password,
bool remember_password) { bool remember_password) {
LOG_INFO("Connect to [{}]", remote_id); LOG_INFO("Connect to [{}]", remote_id);
focused_remote_id_ = remote_id; focused_remote_id_ = remote_id;
@@ -178,4 +180,5 @@ int Render::ConnectTo(const std::string &remote_id, const char *password,
} }
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -11,7 +11,7 @@
#include "device_controller_factory.h" #include "device_controller_factory.h"
#include "fa_regular_400.h" #include "fa_regular_400.h"
#include "fa_solid_900.h" #include "fa_solid_900.h"
#include "layout_style.h" #include "layout.h"
#include "localization.h" #include "localization.h"
#include "platform.h" #include "platform.h"
#include "rd_log.h" #include "rd_log.h"
@@ -21,6 +21,8 @@
#define MOUSE_GRAB_PADDING 5 #define MOUSE_GRAB_PADDING 5
namespace crossdesk {
std::vector<char> Render::SerializeRemoteAction(const RemoteAction& action) { std::vector<char> Render::SerializeRemoteAction(const RemoteAction& action) {
std::vector<char> buffer; std::vector<char> buffer;
buffer.push_back(static_cast<char>(action.type)); buffer.push_back(static_cast<char>(action.type));
@@ -171,7 +173,8 @@ Render::~Render() {}
int Render::SaveSettingsIntoCacheFile() { int Render::SaveSettingsIntoCacheFile() {
cd_cache_mutex_.lock(); cd_cache_mutex_.lock();
std::ofstream cd_cache_file(cache_path_ + "/cache.cd", std::ios::binary); std::ofstream cd_cache_file(cache_path_ + "/secure_cache.enc",
std::ios::binary);
if (!cd_cache_file) { if (!cd_cache_file) {
cd_cache_mutex_.unlock(); cd_cache_mutex_.unlock();
return -1; return -1;
@@ -181,18 +184,6 @@ int Render::SaveSettingsIntoCacheFile() {
sizeof(cd_cache_.client_id_with_password)); sizeof(cd_cache_.client_id_with_password));
memcpy(cd_cache_.client_id_with_password, client_id_with_password_, memcpy(cd_cache_.client_id_with_password, client_id_with_password_,
sizeof(client_id_with_password_)); sizeof(client_id_with_password_));
memcpy(&cd_cache_.language, &language_button_value_,
sizeof(language_button_value_));
memcpy(&cd_cache_.video_quality, &video_quality_button_value_,
sizeof(video_quality_button_value_));
memcpy(&cd_cache_.video_frame_rate, &video_frame_rate_button_value_,
sizeof(video_frame_rate_button_value_));
memcpy(&cd_cache_.video_encode_format, &video_encode_format_button_value_,
sizeof(video_encode_format_button_value_));
memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_,
sizeof(enable_hardware_video_codec_));
memcpy(&cd_cache_.enable_turn, &enable_turn_, sizeof(enable_turn_));
memcpy(&cd_cache_.enable_srtp, &enable_srtp_, sizeof(enable_srtp_));
memcpy(&cd_cache_.key, &aes128_key_, sizeof(aes128_key_)); memcpy(&cd_cache_.key, &aes128_key_, sizeof(aes128_key_));
memcpy(&cd_cache_.iv, &aes128_iv_, sizeof(aes128_iv_)); memcpy(&cd_cache_.iv, &aes128_iv_, sizeof(aes128_iv_));
@@ -200,49 +191,19 @@ int Render::SaveSettingsIntoCacheFile() {
cd_cache_file.close(); cd_cache_file.close();
cd_cache_mutex_.unlock(); cd_cache_mutex_.unlock();
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
config_center_.SetVideoQuality(
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
config_center_.SetVideoFrameRate(
(ConfigCenter::VIDEO_FRAME_RATE)video_frame_rate_button_value_);
config_center_.SetVideoEncodeFormat(
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
config_center_.SetTurn(enable_turn_);
config_center_.SetSrtp(enable_srtp_);
LOG_INFO("Save settings into cache file success");
return 0; return 0;
} }
int Render::LoadSettingsFromCacheFile() { int Render::LoadSettingsFromCacheFile() {
cd_cache_mutex_.lock(); cd_cache_mutex_.lock();
std::ifstream cd_cache_file(cache_path_ + "/cache.cd", std::ios::binary); std::ifstream cd_cache_file(cache_path_ + "/secure_cache.enc",
std::ios::binary);
if (!cd_cache_file) { if (!cd_cache_file) {
cd_cache_mutex_.unlock(); cd_cache_mutex_.unlock();
LOG_INFO("Init cache file by using default settings");
memset(password_saved_, 0, sizeof(password_saved_)); memset(password_saved_, 0, sizeof(password_saved_));
memset(aes128_key_, 0, sizeof(aes128_key_)); memset(aes128_key_, 0, sizeof(aes128_key_));
memset(aes128_iv_, 0, sizeof(aes128_iv_)); memset(aes128_iv_, 0, sizeof(aes128_iv_));
language_button_value_ = 0;
video_quality_button_value_ = 0;
video_encode_format_button_value_ = 1;
enable_hardware_video_codec_ = false;
enable_turn_ = false;
enable_srtp_ = false;
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
config_center_.SetVideoQuality(
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
config_center_.SetVideoFrameRate(
(ConfigCenter::VIDEO_FRAME_RATE)video_frame_rate_button_value_);
config_center_.SetVideoEncodeFormat(
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
config_center_.SetTurn(enable_turn_);
config_center_.SetSrtp(enable_srtp_);
thumbnail_.reset(); thumbnail_.reset();
thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/"); thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/");
@@ -289,13 +250,14 @@ int Render::LoadSettingsFromCacheFile() {
thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/", thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/",
aes128_key_, aes128_iv_); aes128_key_, aes128_iv_);
language_button_value_ = cd_cache_.language; language_button_value_ = (int)config_center_->GetLanguage();
video_quality_button_value_ = cd_cache_.video_quality; video_quality_button_value_ = (int)config_center_->GetVideoQuality();
video_frame_rate_button_value_ = cd_cache_.video_frame_rate; video_frame_rate_button_value_ = (int)config_center_->GetVideoFrameRate();
video_encode_format_button_value_ = cd_cache_.video_encode_format; video_encode_format_button_value_ =
enable_hardware_video_codec_ = cd_cache_.enable_hardware_video_codec; (int)config_center_->GetVideoEncodeFormat();
enable_turn_ = cd_cache_.enable_turn; enable_hardware_video_codec_ = config_center_->IsHardwareVideoCodec();
enable_srtp_ = cd_cache_.enable_srtp; enable_turn_ = config_center_->IsEnableTurn();
enable_srtp_ = config_center_->IsEnableSrtp();
language_button_value_last_ = language_button_value_; language_button_value_last_ = language_button_value_;
video_quality_button_value_last_ = video_quality_button_value_; video_quality_button_value_last_ = video_quality_button_value_;
@@ -304,17 +266,6 @@ int Render::LoadSettingsFromCacheFile() {
enable_turn_last_ = enable_turn_; enable_turn_last_ = enable_turn_;
enable_srtp_last_ = enable_srtp_; enable_srtp_last_ = enable_srtp_;
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
config_center_.SetVideoQuality(
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
config_center_.SetVideoFrameRate(
(ConfigCenter::VIDEO_FRAME_RATE)video_frame_rate_button_value_);
config_center_.SetVideoEncodeFormat(
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
config_center_.SetTurn(enable_turn_);
config_center_.SetSrtp(enable_srtp_);
LOG_INFO("Load settings from cache file"); LOG_INFO("Load settings from cache file");
return 0; return 0;
@@ -328,18 +279,21 @@ int Render::ScreenCapturerInit() {
last_frame_time_ = std::chrono::duration_cast<std::chrono::milliseconds>( last_frame_time_ = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()) std::chrono::steady_clock::now().time_since_epoch())
.count(); .count();
int fps = config_center_.GetVideoFrameRate(); int fps = config_center_->GetVideoFrameRate() ==
ConfigCenter::VIDEO_FRAME_RATE::FPS_30
? 30
: 60;
LOG_INFO("Init screen capturer with {} fps", fps); LOG_INFO("Init screen capturer with {} fps", fps);
int screen_capturer_init_ret = screen_capturer_->Init( int screen_capturer_init_ret = screen_capturer_->Init(
fps, fps,
[this](unsigned char* data, int size, int width, int height, [this, fps](unsigned char* data, int size, int width, int height,
const char* display_name) -> void { const char* display_name) -> void {
auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>( auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()) std::chrono::steady_clock::now().time_since_epoch())
.count(); .count();
auto duration = now_time - last_frame_time_; auto duration = now_time - last_frame_time_;
if (duration * config_center_.GetVideoFrameRate() >= 1000) { // ~60 FPS if (duration * fps >= 1000) { // ~60 FPS
XVideoFrame frame; XVideoFrame frame;
frame.data = (const char*)data; frame.data = (const char*)data;
frame.size = size; frame.size = size;
@@ -474,25 +428,71 @@ int Render::StopKeyboardCapturer() {
int Render::CreateConnectionPeer() { int Render::CreateConnectionPeer() {
params_.use_cfg_file = false; params_.use_cfg_file = false;
params_.signal_server_ip = "api.crossdesk.cn";
params_.signal_server_port = 9099; std::string signal_server_ip;
params_.stun_server_ip = "150.158.81.30"; int signal_server_port;
params_.stun_server_port = 3478; int coturn_server_port;
params_.turn_server_ip = "150.158.81.30"; std::string tls_cert_path;
params_.turn_server_port = 3478;
params_.turn_server_username = "dijunkun"; if (config_center_->IsSelfHosted()) {
params_.turn_server_password = "dijunkunpw"; signal_server_ip = config_center_->GetSignalServerHost();
params_.tls_cert_path = std::filesystem::exists(cert_path_) signal_server_port = config_center_->GetSignalServerPort();
? cert_path_.c_str() coturn_server_port = config_center_->GetCoturnServerPort();
: "certs/crossdesk.cn_root.crt"; tls_cert_path = config_center_->GetCertFilePath();
params_.log_path = dll_log_path_.c_str(); } else {
params_.hardware_acceleration = config_center_.IsHardwareVideoCodec(); signal_server_ip = config_center_->GetDefaultServerHost();
params_.av1_encoding = config_center_.GetVideoEncodeFormat() == signal_server_port = config_center_->GetDefaultSignalServerPort();
coturn_server_port = config_center_->GetDefaultCoturnServerPort();
tls_cert_path = config_center_->GetDefaultCertFilePath();
}
// 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);
params_.signal_server_ip[sizeof(params_.signal_server_ip) - 1] = '\0';
params_.signal_server_port = signal_server_port;
strncpy((char*)params_.stun_server_ip, signal_server_ip.c_str(),
sizeof(params_.stun_server_ip) - 1);
params_.stun_server_ip[sizeof(params_.stun_server_ip) - 1] = '\0';
params_.stun_server_port = coturn_server_port;
strncpy((char*)params_.turn_server_ip, signal_server_ip.c_str(),
sizeof(params_.turn_server_ip) - 1);
params_.turn_server_ip[sizeof(params_.turn_server_ip) - 1] = '\0';
params_.turn_server_port = coturn_server_port;
strncpy((char*)params_.turn_server_username, "crossdesk",
sizeof(params_.turn_server_username) - 1);
params_.turn_server_username[sizeof(params_.turn_server_username) - 1] = '\0';
strncpy((char*)params_.turn_server_password, "crossdeskpw",
sizeof(params_.turn_server_password) - 1);
params_.turn_server_password[sizeof(params_.turn_server_password) - 1] = '\0';
strncpy(params_.tls_cert_path, tls_cert_path.c_str(),
sizeof(params_.tls_cert_path) - 1);
params_.tls_cert_path[sizeof(params_.tls_cert_path) - 1] = '\0';
strncpy(params_.log_path, dll_log_path_.c_str(),
sizeof(params_.log_path) - 1);
params_.log_path[sizeof(params_.log_path) - 1] = '\0';
params_.hardware_acceleration = config_center_->IsHardwareVideoCodec();
params_.av1_encoding = config_center_->GetVideoEncodeFormat() ==
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1 ConfigCenter::VIDEO_ENCODE_FORMAT::AV1
? true ? true
: false; : false;
params_.enable_turn = config_center_.IsEnableTurn(); params_.enable_turn = config_center_->IsEnableTurn();
params_.enable_srtp = config_center_.IsEnableSrtp(); params_.enable_srtp = config_center_->IsEnableSrtp();
params_.on_receive_video_buffer = nullptr; params_.on_receive_video_buffer = nullptr;
params_.on_receive_audio_buffer = OnReceiveAudioBufferCb; params_.on_receive_audio_buffer = OnReceiveAudioBufferCb;
params_.on_receive_data_buffer = OnReceiveDataBufferCb; params_.on_receive_data_buffer = OnReceiveDataBufferCb;
@@ -608,6 +608,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;
} }
@@ -890,6 +901,21 @@ int Render::Run() {
exec_log_path_ = path_manager_->GetLogPath().string(); exec_log_path_ = path_manager_->GetLogPath().string();
dll_log_path_ = path_manager_->GetLogPath().string(); dll_log_path_ = path_manager_->GetLogPath().string();
cache_path_ = path_manager_->GetCachePath().string(); cache_path_ = path_manager_->GetCachePath().string();
config_center_ =
std::make_unique<ConfigCenter>(cache_path_ + "/config.ini", cert_path_);
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(cert_file_path_, cert_path_.c_str(), sizeof(cert_file_path_) - 1);
cert_file_path_[sizeof(cert_file_path_) - 1] = '\0';
} else {
std::cerr << "Failed to create PathManager" << std::endl;
return -1;
} }
InitializeLogger(); InitializeLogger();
@@ -974,6 +1000,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();
@@ -1399,4 +1433,5 @@ void Render::ProcessSdlEvent(const SDL_Event& event) {
} }
break; break;
} }
} }
} // namespace crossdesk

View File

@@ -29,7 +29,11 @@
#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
namespace crossdesk {
class Render { class Render {
public: public:
struct SubStreamWindowProperties { struct SubStreamWindowProperties {
@@ -152,6 +156,8 @@ class Render {
int RemoteWindow(); int RemoteWindow();
int RecentConnectionsWindow(); int RecentConnectionsWindow();
int SettingWindow(); int SettingWindow();
int SelfHostedServerWindow();
int ShowSimpleFileBrowser();
int ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props); int ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props);
int ControlBar(std::shared_ptr<SubStreamWindowProperties>& props); int ControlBar(std::shared_ptr<SubStreamWindowProperties>& props);
int AboutWindow(); int AboutWindow();
@@ -261,7 +267,7 @@ class Render {
private: private:
CDCache cd_cache_; CDCache cd_cache_;
std::mutex cd_cache_mutex_; std::mutex cd_cache_mutex_;
ConfigCenter config_center_; std::unique_ptr<ConfigCenter> config_center_;
ConfigCenter::LANGUAGE localization_language_ = ConfigCenter::LANGUAGE localization_language_ =
ConfigCenter::LANGUAGE::CHINESE; ConfigCenter::LANGUAGE::CHINESE;
std::unique_ptr<PathManager> path_manager_; std::unique_ptr<PathManager> path_manager_;
@@ -296,6 +302,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;
@@ -333,8 +342,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;
@@ -383,6 +392,7 @@ class Render {
bool password_validating_ = false; bool password_validating_ = false;
uint32_t password_validating_time_ = 0; uint32_t password_validating_time_ = 0;
bool show_settings_window_ = false; bool show_settings_window_ = false;
bool show_self_hosted_server_config_window_ = false;
bool rejoin_ = false; bool rejoin_ = false;
bool local_id_copied_ = false; bool local_id_copied_ = false;
bool show_password_ = true; bool show_password_ = true;
@@ -431,13 +441,27 @@ class Render {
bool enable_hardware_video_codec_ = false; bool enable_hardware_video_codec_ = false;
bool enable_turn_ = false; bool enable_turn_ = false;
bool enable_srtp_ = false; bool enable_srtp_ = false;
char signal_server_ip_[256] = "api.crossdesk.cn";
char signal_server_port_[6] = "9099";
char coturn_server_port_[6] = "3478";
char cert_file_path_[256] = "";
bool enable_self_hosted_server_ = false;
int language_button_value_last_ = 0; int language_button_value_last_ = 0;
int video_quality_button_value_last_ = 0; int video_quality_button_value_last_ = 0;
int video_encode_format_button_value_last_ = 0; int video_encode_format_button_value_last_ = 0;
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;
bool enable_minimize_to_tray_ = false;
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;
std::string selected_current_file_path_ = "";
bool show_file_browser_ = true;
/* ------ main window property end ------ */ /* ------ main window property end ------ */
/* ------ sub stream window property start ------ */ /* ------ sub stream window property start ------ */
@@ -446,5 +470,5 @@ class Render {
void CloseTab(decltype(client_properties_)::iterator& it); void CloseTab(decltype(client_properties_)::iterator& it);
/* ------ stream window property end ------ */ /* ------ stream window property end ------ */
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -11,6 +11,8 @@
#define MOUSE_CONTROL 1 #define MOUSE_CONTROL 1
#endif #endif
namespace crossdesk {
int Render::SendKeyCommand(int key_code, bool is_down) { int Render::SendKeyCommand(int key_code, bool is_down) {
RemoteAction remote_action; RemoteAction remote_action;
remote_action.type = ControlType::keyboard; remote_action.type = ControlType::keyboard;
@@ -542,4 +544,5 @@ void Render::NetStatusReport(const char* client_id, size_t client_id_size,
if (!(render->peer_reserved_ && !strstr(client_id, "C-"))) { if (!(render->peer_reserved_ && !strstr(client_id, "C-"))) {
props->net_traffic_stats_ = *net_traffic_stats; props->net_traffic_stats_ = *net_traffic_stats;
} }
} }
} // namespace crossdesk

View File

@@ -1,8 +1,10 @@
#include "layout_style.h" #include "layout.h"
#include "localization.h" #include "localization.h"
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
namespace crossdesk {
int CountDigits(int number) { int CountDigits(int number) {
if (number == 0) return 1; if (number == 0) return 1;
return (int)std::floor(std::log10(std::abs(number))) + 1; return (int)std::floor(std::log10(std::abs(number))) + 1;
@@ -324,3 +326,4 @@ int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -1,6 +1,8 @@
#include "localization.h" #include "localization.h"
#include "render.h" #include "render.h"
namespace crossdesk {
int Render::StatusBar() { int Render::StatusBar() {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
static bool a, b, c, d, e; static bool a, b, c, d, e;
@@ -35,4 +37,5 @@ int Render::StatusBar() {
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::EndChild(); ImGui::EndChild();
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -4,6 +4,8 @@
#define BUTTON_PADDING 36.0f #define BUTTON_PADDING 36.0f
namespace crossdesk {
int Render::TitleBar(bool main_window) { int Render::TitleBar(bool main_window) {
ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f)); ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
@@ -57,6 +59,7 @@ int Render::TitleBar(bool main_window) {
{ {
SettingWindow(); SettingWindow();
SelfHostedServerWindow();
AboutWindow(); AboutWindow();
} }
} }
@@ -138,9 +141,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),
@@ -162,4 +173,5 @@ int Render::TitleBar(bool main_window) {
ImGui::EndChild(); ImGui::EndChild();
ImGui::PopStyleColor(); ImGui::PopStyleColor();
return 0; return 0;
} }
} // namespace crossdesk

115
src/gui/tray/win_tray.cpp Normal file
View File

@@ -0,0 +1,115 @@
#include "win_tray.h"
#include <SDL3/SDL.h>
#include "localization.h"
namespace crossdesk {
// 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;
}
} // namespace crossdesk

38
src/gui/tray/win_tray.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* @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)
namespace crossdesk {
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_;
};
} // namespace crossdesk
#endif

View File

@@ -1,11 +1,13 @@
#include "layout_style.h" #include "layout.h"
#include "localization.h" #include "localization.h"
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
namespace crossdesk {
int Render::AboutWindow() { int Render::AboutWindow() {
if (show_about_window_) { if (show_about_window_) {
const ImGuiViewport *viewport = ImGui::GetMainViewport(); const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2( ImGui::SetNextWindowPos(ImVec2(
(viewport->WorkSize.x - viewport->WorkPos.x - about_window_width_) / 2, (viewport->WorkSize.x - viewport->WorkPos.x - about_window_width_) / 2,
@@ -27,15 +29,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);
@@ -52,4 +60,5 @@ int Render::AboutWindow() {
ImGui::PopStyleColor(); ImGui::PopStyleColor();
} }
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -1,11 +1,13 @@
#include "layout_style.h" #include "layout.h"
#include "localization.h" #include "localization.h"
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
namespace crossdesk {
bool Render::ConnectionStatusWindow( bool Render::ConnectionStatusWindow(
std::shared_ptr<SubStreamWindowProperties> &props) { std::shared_ptr<SubStreamWindowProperties>& props) {
const ImGuiViewport *viewport = ImGui::GetMainViewport(); const ImGuiViewport* viewport = ImGui::GetMainViewport();
bool ret_flag = false; bool ret_flag = false;
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x - ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
connection_status_window_width_) / connection_status_window_width_) /
@@ -168,4 +170,5 @@ bool Render::ConnectionStatusWindow(
ImGui::PopStyleVar(); ImGui::PopStyleVar();
return ret_flag; return ret_flag;
} }
} // namespace crossdesk

View File

@@ -1,7 +1,9 @@
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
int Render::ControlWindow(std::shared_ptr<SubStreamWindowProperties> &props) { namespace crossdesk {
int Render::ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props) {
double time_duration = double time_duration =
ImGui::GetTime() - props->control_bar_button_pressed_time_; ImGui::GetTime() - props->control_bar_button_pressed_time_;
if (props->control_window_width_is_changing_) { if (props->control_window_width_is_changing_) {
@@ -220,4 +222,5 @@ int Render::ControlWindow(std::shared_ptr<SubStreamWindowProperties> &props) {
ImGui::PopStyleColor(); ImGui::PopStyleColor();
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -1,8 +1,10 @@
#include "layout_style.h" #include "layout.h"
#include "localization.h" #include "localization.h"
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
namespace crossdesk {
int Render::SettingWindow() { int Render::SettingWindow() {
if (show_settings_window_) { if (show_settings_window_) {
if (settings_window_pos_reset_) { if (settings_window_pos_reset_) {
@@ -57,7 +59,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 +90,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());
@@ -108,10 +110,10 @@ int Render::SettingWindow() {
ImGui::Separator(); ImGui::Separator();
{ {
const char* video_frame_rate_items[] = {"30", "60"}; 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());
@@ -133,11 +135,11 @@ int Render::SettingWindow() {
{ {
const char* video_encode_format_items[] = { const char* video_encode_format_items[] = {
localization::av1[localization_language_index_].c_str(), localization::h264[localization_language_index_].c_str(),
localization::h264[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 +162,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 +181,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 +199,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());
@@ -211,6 +213,48 @@ int Render::SettingWindow() {
ImGui::Checkbox("##enable_srtp", &enable_srtp_); ImGui::Checkbox("##enable_srtp", &enable_srtp_);
} }
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 1);
if (ImGui::Button(localization::self_hosted_server_config
[localization_language_index_]
.c_str())) {
show_self_hosted_server_config_window_ = true;
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_self_hosted_server",
&enable_self_hosted_server_);
}
#if _WIN32
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text("%s",
localization::minimize_to_tray[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_MINIZE_TO_TRAY_PADDING_CN);
} else {
ImGui::SetCursorPosX(ENABLE_MINIZE_TO_TRAY_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_minimize_to_tray_",
&enable_minimize_to_tray_);
}
#endif
if (stream_window_inited_) { if (stream_window_inited_) {
ImGui::EndDisabled(); ImGui::EndDisabled();
} }
@@ -221,7 +265,7 @@ int Render::SettingWindow() {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN); ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
} }
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding + 10;
ImGui::SetCursorPosY(settings_items_offset); ImGui::SetCursorPosY(settings_items_offset);
ImGui::PopStyleVar(); ImGui::PopStyleVar();
@@ -229,12 +273,13 @@ int Render::SettingWindow() {
if (ImGui::Button( if (ImGui::Button(
localization::ok[localization_language_index_].c_str())) { localization::ok[localization_language_index_].c_str())) {
show_settings_window_ = false; show_settings_window_ = false;
show_self_hosted_server_config_window_ = false;
// Language // Language
if (language_button_value_ == 0) { if (language_button_value_ == 0) {
config_center_.SetLanguage(ConfigCenter::LANGUAGE::CHINESE); config_center_->SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
} else { } else {
config_center_.SetLanguage(ConfigCenter::LANGUAGE::ENGLISH); config_center_->SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
} }
language_button_value_last_ = language_button_value_; language_button_value_last_ = language_button_value_;
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_; localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
@@ -244,50 +289,55 @@ int Render::SettingWindow() {
// Video quality // Video quality
if (video_quality_button_value_ == 0) { if (video_quality_button_value_ == 0) {
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH); config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
} else if (video_quality_button_value_ == 1) { } else if (video_quality_button_value_ == 1) {
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM); config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
} else { } else {
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW); config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
} }
video_quality_button_value_last_ = video_quality_button_value_; video_quality_button_value_last_ = video_quality_button_value_;
// Video encode format // Video encode format
if (video_encode_format_button_value_ == 0) { if (video_encode_format_button_value_ == 0) {
config_center_.SetVideoEncodeFormat( config_center_->SetVideoEncodeFormat(
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
} else if (video_encode_format_button_value_ == 1) {
config_center_.SetVideoEncodeFormat(
ConfigCenter::VIDEO_ENCODE_FORMAT::H264); ConfigCenter::VIDEO_ENCODE_FORMAT::H264);
} else if (video_encode_format_button_value_ == 1) {
config_center_->SetVideoEncodeFormat(
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
} }
video_encode_format_button_value_last_ = video_encode_format_button_value_last_ =
video_encode_format_button_value_; video_encode_format_button_value_;
// Hardware video codec // Hardware video codec
if (enable_hardware_video_codec_) { if (enable_hardware_video_codec_) {
config_center_.SetHardwareVideoCodec(true); config_center_->SetHardwareVideoCodec(true);
} else { } else {
config_center_.SetHardwareVideoCodec(false); config_center_->SetHardwareVideoCodec(false);
} }
enable_hardware_video_codec_last_ = enable_hardware_video_codec_; enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
// TURN mode // TURN mode
if (enable_turn_) { if (enable_turn_) {
config_center_.SetTurn(true); config_center_->SetTurn(true);
} else { } else {
config_center_.SetTurn(false); config_center_->SetTurn(false);
} }
enable_turn_last_ = enable_turn_; enable_turn_last_ = enable_turn_;
// SRTP // SRTP
if (enable_srtp_) { if (enable_srtp_) {
config_center_.SetSrtp(true); config_center_->SetSrtp(true);
} else { } else {
config_center_.SetSrtp(false); config_center_->SetSrtp(false);
} }
enable_srtp_last_ = enable_srtp_; enable_srtp_last_ = enable_srtp_;
SaveSettingsIntoCacheFile(); if (enable_self_hosted_server_) {
config_center_->SetSelfHosted(true);
} else {
config_center_->SetSelfHosted(false);
}
settings_window_pos_reset_ = true; settings_window_pos_reset_ = true;
// Recreate peer instance // Recreate peer instance
@@ -306,6 +356,8 @@ int Render::SettingWindow() {
if (ImGui::Button( if (ImGui::Button(
localization::cancel[localization_language_index_].c_str())) { localization::cancel[localization_language_index_].c_str())) {
show_settings_window_ = false; show_settings_window_ = false;
show_self_hosted_server_config_window_ = false;
if (language_button_value_ != language_button_value_last_) { if (language_button_value_ != language_button_value_last_) {
language_button_value_ = language_button_value_last_; language_button_value_ = language_button_value_last_;
} }
@@ -340,4 +392,5 @@ int Render::SettingWindow() {
} }
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -2,6 +2,8 @@
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
namespace crossdesk {
int Render::MainWindow() { int Render::MainWindow() {
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Always); ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
@@ -47,3 +49,4 @@ int Render::MainWindow() {
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -0,0 +1,324 @@
#include <filesystem>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
namespace crossdesk {
std::vector<std::string> GetRootEntries() {
std::vector<std::string> roots;
#ifdef _WIN32
DWORD mask = GetLogicalDrives();
for (char letter = 'A'; letter <= 'Z'; ++letter) {
if (mask & 1) {
roots.push_back(std::string(1, letter) + ":\\");
}
mask >>= 1;
}
#else
roots.push_back("/");
#endif
return roots;
}
int Render::ShowSimpleFileBrowser() {
std::string display_text;
if (selected_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") {
display_text =
std::filesystem::path(selected_current_file_path_).filename().string();
if (display_text.empty()) {
display_text = selected_current_file_path_;
}
}
if (display_text.empty()) {
display_text =
localization::select_a_file[localization_language_index_].c_str();
}
if (show_file_browser_) {
ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
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();
if (selected_current_file_path_ == "Root" ||
!std::filesystem::exists(selected_current_file_path_) ||
!std::filesystem::is_directory(selected_current_file_path_)) {
for (const auto& root : roots) {
if (ImGui::Selectable(root.c_str())) {
selected_current_file_path_ = root;
tls_cert_path_self_.clear();
}
}
} else {
std::filesystem::path p(selected_current_file_path_);
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;
}
int Render::SelfHostedServerWindow() {
if (show_self_hosted_server_config_window_) {
if (self_hosted_server_config_window_pos_reset_) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN,
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN));
} else {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN,
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN));
}
self_hosted_server_config_window_pos_reset_ = false;
}
// Settings
{
static int settings_items_padding = 30;
int settings_items_offset = 0;
ImGui::SetWindowFontScale(0.5f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::Begin(localization::self_hosted_server_settings
[localization_language_index_]
.c_str(),
nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoSavedSettings);
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text("%s", localization::self_hosted_server_address
[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::InputText("##signal_server_ip_self_", signal_server_ip_self_,
IM_ARRAYSIZE(signal_server_ip_self_),
ImGuiInputTextFlags_AlwaysOverwrite);
}
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text(
"%s",
localization::self_hosted_server_port[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::InputText("##signal_server_port_self_", signal_server_port_self_,
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();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text("%s", localization::self_hosted_server_certificate_path
[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ShowSimpleFileBrowser();
}
if (stream_window_inited_) {
ImGui::EndDisabled();
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN);
}
settings_items_offset += settings_items_padding + 10;
ImGui::SetCursorPosY(settings_items_offset);
ImGui::PopStyleVar();
// OK
if (ImGui::Button(
localization::ok[localization_language_index_].c_str())) {
show_self_hosted_server_config_window_ = false;
config_center_->SetServerHost(signal_server_ip_self_);
config_center_->SetServerPort(atoi(signal_server_port_self_));
config_center_->SetCoturnServerPort(atoi(coturn_server_port_self_));
config_center_->SetCertFilePath(tls_cert_path_self_);
strncpy(signal_server_ip_, signal_server_ip_self_,
sizeof(signal_server_ip_) - 1);
signal_server_ip_[sizeof(signal_server_ip_) - 1] = '\0';
strncpy(signal_server_port_, signal_server_port_self_,
sizeof(signal_server_port_) - 1);
signal_server_port_[sizeof(signal_server_port_) - 1] = '\0';
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);
cert_file_path_[sizeof(cert_file_path_) - 1] = '\0';
self_hosted_server_config_window_pos_reset_ = true;
}
ImGui::SameLine();
// Cancel
if (ImGui::Button(
localization::cancel[localization_language_index_].c_str())) {
show_self_hosted_server_config_window_ = false;
self_hosted_server_config_window_pos_reset_ = true;
strncpy(signal_server_ip_self_, signal_server_ip_,
sizeof(signal_server_ip_self_) - 1);
signal_server_ip_self_[sizeof(signal_server_ip_self_) - 1] = '\0';
strncpy(signal_server_port_self_, signal_server_port_,
sizeof(signal_server_port_self_) - 1);
signal_server_port_self_[sizeof(signal_server_port_self_) - 1] = '\0';
config_center_->SetServerHost(signal_server_ip_self_);
config_center_->SetServerPort(atoi(signal_server_port_self_));
tls_cert_path_self_.clear();
}
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::End();
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
}
}
return 0;
}
} // namespace crossdesk

View File

@@ -2,6 +2,8 @@
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
namespace crossdesk {
void Render::DrawConnectionStatusText( void Render::DrawConnectionStatusText(
std::shared_ptr<SubStreamWindowProperties>& props) { std::shared_ptr<SubStreamWindowProperties>& props) {
std::string text; std::string text;
@@ -199,4 +201,5 @@ int Render::StreamWindow() {
ImGui::End(); // End VideoBg ImGui::End(); // End VideoBg
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -3,6 +3,8 @@
#include <atomic> #include <atomic>
#include <filesystem> #include <filesystem>
namespace crossdesk {
namespace { namespace {
std::string g_log_dir = "logs"; std::string g_log_dir = "logs";
@@ -60,3 +62,4 @@ std::shared_ptr<spdlog::logger> get_logger() {
return g_logger; return g_logger;
} }
} // namespace crossdesk

View File

@@ -25,6 +25,8 @@
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
namespace crossdesk {
constexpr auto LOGGER_NAME = "crossdesk"; constexpr auto LOGGER_NAME = "crossdesk";
void InitLogger(const std::string& log_dir); void InitLogger(const std::string& log_dir);
@@ -35,5 +37,5 @@ std::shared_ptr<spdlog::logger> get_logger();
#define LOG_WARN(...) SPDLOG_LOGGER_WARN(get_logger(), __VA_ARGS__) #define LOG_WARN(...) SPDLOG_LOGGER_WARN(get_logger(), __VA_ARGS__)
#define LOG_ERROR(...) SPDLOG_LOGGER_ERROR(get_logger(), __VA_ARGS__) #define LOG_ERROR(...) SPDLOG_LOGGER_ERROR(get_logger(), __VA_ARGS__)
#define LOG_FATAL(...) SPDLOG_LOGGER_CRITICAL(get_logger(), __VA_ARGS__) #define LOG_FATAL(...) SPDLOG_LOGGER_CRITICAL(get_logger(), __VA_ARGS__)
} // namespace crossdesk
#endif #endif

View File

@@ -2,6 +2,8 @@
#include <cstdlib> #include <cstdlib>
namespace crossdesk {
PathManager::PathManager(const std::string& app_name) : app_name_(app_name) {} PathManager::PathManager(const std::string& app_name) : app_name_(app_name) {}
std::filesystem::path PathManager::GetConfigPath() { std::filesystem::path PathManager::GetConfigPath() {
@@ -89,3 +91,4 @@ std::filesystem::path PathManager::GetEnvOrDefault(const char* env_var,
return std::filesystem::path(def); return std::filesystem::path(def);
} }
} // namespace crossdesk

View File

@@ -14,6 +14,8 @@
#include <windows.h> #include <windows.h>
#endif #endif
namespace crossdesk {
class PathManager { class PathManager {
public: public:
explicit PathManager(const std::string& app_name); explicit PathManager(const std::string& app_name);
@@ -40,5 +42,5 @@ class PathManager {
private: private:
std::string app_name_; std::string app_name_;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -6,6 +6,8 @@
#include "libyuv.h" #include "libyuv.h"
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
ScreenCapturerX11::ScreenCapturerX11() {} ScreenCapturerX11::ScreenCapturerX11() {}
ScreenCapturerX11::~ScreenCapturerX11() { Destroy(); } ScreenCapturerX11::~ScreenCapturerX11() { Destroy(); }
@@ -171,4 +173,5 @@ void ScreenCapturerX11::OnFrame() {
} }
XDestroyImage(image); XDestroyImage(image);
} }
} // namespace crossdesk

View File

@@ -20,6 +20,8 @@
#include "screen_capturer.h" #include "screen_capturer.h"
namespace crossdesk {
class ScreenCapturerX11 : public ScreenCapturer { class ScreenCapturerX11 : public ScreenCapturer {
public: public:
ScreenCapturerX11(); ScreenCapturerX11();
@@ -60,5 +62,5 @@ class ScreenCapturerX11 : public ScreenCapturer {
std::vector<uint8_t> y_plane_; std::vector<uint8_t> y_plane_;
std::vector<uint8_t> uv_plane_; std::vector<uint8_t> uv_plane_;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -2,6 +2,8 @@
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
ScreenCapturerSck::ScreenCapturerSck() {} ScreenCapturerSck::ScreenCapturerSck() {}
ScreenCapturerSck::~ScreenCapturerSck() {} ScreenCapturerSck::~ScreenCapturerSck() {}
@@ -70,4 +72,5 @@ std::vector<DisplayInfo> ScreenCapturerSck::GetDisplayInfoList() {
void ScreenCapturerSck::OnFrame() {} void ScreenCapturerSck::OnFrame() {}
void ScreenCapturerSck::CleanUp() {} void ScreenCapturerSck::CleanUp() {}
} // namespace crossdesk

View File

@@ -16,6 +16,8 @@
#include "screen_capturer.h" #include "screen_capturer.h"
namespace crossdesk {
class ScreenCapturerSck : public ScreenCapturer { class ScreenCapturerSck : public ScreenCapturer {
public: public:
ScreenCapturerSck(); ScreenCapturerSck();
@@ -55,5 +57,5 @@ class ScreenCapturerSck : public ScreenCapturer {
private: private:
std::unique_ptr<ScreenCapturer> screen_capturer_sck_impl_; std::unique_ptr<ScreenCapturer> screen_capturer_sck_impl_;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -22,9 +22,12 @@
#include "display_info.h" #include "display_info.h"
#include "rd_log.h" #include "rd_log.h"
static const int kFullDesktopScreenId = -1; using namespace crossdesk;
class ScreenCapturerSckImpl; class ScreenCapturerSckImpl;
static const int kFullDesktopScreenId = -1;
// The ScreenCaptureKit API was available in macOS 12.3, but full-screen capture // The ScreenCaptureKit API was available in macOS 12.3, but full-screen capture
// was reported to be broken before macOS 13 - see http://crbug.com/40234870. // was reported to be broken before macOS 13 - see http://crbug.com/40234870.
// Also, the `SCContentFilter` fields `contentRect` and `pointPixelScale` were // Also, the `SCContentFilter` fields `contentRect` and `pointPixelScale` were
@@ -424,10 +427,6 @@ void ScreenCapturerSckImpl::StartOrReconfigureCapturer() {
[SCShareableContent getShareableContentWithCompletionHandler:handler]; [SCShareableContent getShareableContentWithCompletionHandler:handler];
} }
std::unique_ptr<ScreenCapturer> ScreenCapturerSck::CreateScreenCapturerSck() {
return std::make_unique<ScreenCapturerSckImpl>();
}
@implementation SckHelper { @implementation SckHelper {
// This lock is to prevent the capturer being destroyed while an instance // This lock is to prevent the capturer being destroyed while an instance
// method is still running on another thread. // method is still running on another thread.
@@ -485,4 +484,8 @@ std::unique_ptr<ScreenCapturer> ScreenCapturerSck::CreateScreenCapturerSck() {
_capturer = nullptr; _capturer = nullptr;
} }
@end @end
std::unique_ptr<ScreenCapturer> ScreenCapturerSck::CreateScreenCapturerSck() {
return std::make_unique<ScreenCapturerSckImpl>();
}

View File

@@ -11,6 +11,8 @@
#include "display_info.h" #include "display_info.h"
namespace crossdesk {
class ScreenCapturer { class ScreenCapturer {
public: public:
typedef std::function<void(unsigned char*, int, int, int, const char*)> typedef std::function<void(unsigned char*, int, int, int, const char*)>
@@ -30,5 +32,5 @@ class ScreenCapturer {
virtual std::vector<DisplayInfo> GetDisplayInfoList() = 0; virtual std::vector<DisplayInfo> GetDisplayInfoList() = 0;
virtual int SwitchTo(int monitor_index) = 0; virtual int SwitchTo(int monitor_index) = 0;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -16,6 +16,8 @@
#include "screen_capturer_sck.h" #include "screen_capturer_sck.h"
#endif #endif
namespace crossdesk {
class ScreenCapturerFactory { class ScreenCapturerFactory {
public: public:
virtual ~ScreenCapturerFactory() {} virtual ~ScreenCapturerFactory() {}
@@ -34,5 +36,5 @@ class ScreenCapturerFactory {
#endif #endif
} }
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -10,9 +10,11 @@
#include "libyuv.h" #include "libyuv.h"
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
static std::vector<DisplayInfo> gs_display_list; static std::vector<DisplayInfo> gs_display_list;
std::string WideToUtf8(const wchar_t *wideStr) { std::string WideToUtf8(const wchar_t* wideStr) {
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wideStr, -1, nullptr, 0, int size_needed = WideCharToMultiByte(CP_UTF8, 0, wideStr, -1, nullptr, 0,
nullptr, nullptr); nullptr, nullptr);
std::string result(size_needed, 0); std::string result(size_needed, 0);
@@ -31,14 +33,14 @@ BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, [[maybe_unused]] HDC hdc,
if (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) { if (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) {
gs_display_list.insert( gs_display_list.insert(
gs_display_list.begin(), gs_display_list.begin(),
{(void *)hmonitor, WideToUtf8(monitor_info_.szDevice), {(void*)hmonitor, WideToUtf8(monitor_info_.szDevice),
(monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false, (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false,
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top, monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom}); monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom});
*(HMONITOR *)data = hmonitor; *(HMONITOR*)data = hmonitor;
} else { } else {
gs_display_list.push_back(DisplayInfo( gs_display_list.push_back(DisplayInfo(
(void *)hmonitor, WideToUtf8(monitor_info_.szDevice), (void*)hmonitor, WideToUtf8(monitor_info_.szDevice),
(monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false, (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false,
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top, monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom)); monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom));
@@ -81,7 +83,7 @@ bool ScreenCapturerWgc::IsWgcSupported() {
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */ /* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
return winrt::Windows::Foundation::Metadata::ApiInformation:: return winrt::Windows::Foundation::Metadata::ApiInformation::
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8); IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8);
} catch (const winrt::hresult_error &) { } catch (const winrt::hresult_error&) {
return false; return false;
} catch (...) { } catch (...) {
return false; return false;
@@ -115,7 +117,7 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
} }
for (int i = 0; i < display_info_list_.size(); i++) { for (int i = 0; i < display_info_list_.size(); i++) {
const auto &display = display_info_list_[i]; const auto& display = display_info_list_[i];
LOG_INFO( LOG_INFO(
"index: {}, display name: {}, is primary: {}, bounds: ({}, {}) - " "index: {}, display name: {}, is primary: {}, bounds: ({}, {}) - "
"({}, {})", "({}, {})",
@@ -243,16 +245,16 @@ int ScreenCapturerWgc::SwitchTo(int monitor_index) {
return 0; return 0;
} }
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame, void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame,
int id) { int id) {
if (on_data_) { if (on_data_) {
if (!nv12_frame_) { if (!nv12_frame_) {
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2]; nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
} }
libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4, libyuv::ARGBToNV12((const uint8_t*)frame.data, frame.width * 4,
(uint8_t *)nv12_frame_, frame.width, (uint8_t*)nv12_frame_, frame.width,
(uint8_t *)(nv12_frame_ + frame.width * frame.height), (uint8_t*)(nv12_frame_ + frame.width * frame.height),
frame.width, frame.width, frame.height); frame.width, frame.width, frame.height);
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width, on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
@@ -262,7 +264,7 @@ void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame,
void ScreenCapturerWgc::CleanUp() { void ScreenCapturerWgc::CleanUp() {
if (inited_) { if (inited_) {
for (auto &session : sessions_) { for (auto& session : sessions_) {
if (session.session_) { if (session.session_) {
session.session_->Stop(); session.session_->Stop();
} }
@@ -270,3 +272,4 @@ void ScreenCapturerWgc::CleanUp() {
sessions_.clear(); sessions_.clear();
} }
} }
} // namespace crossdesk

View File

@@ -11,6 +11,8 @@
#include "wgc_session.h" #include "wgc_session.h"
#include "wgc_session_impl.h" #include "wgc_session_impl.h"
namespace crossdesk {
class ScreenCapturerWgc : public ScreenCapturer, class ScreenCapturerWgc : public ScreenCapturer,
public WgcSession::wgc_session_observer { public WgcSession::wgc_session_observer {
public: public:
@@ -64,5 +66,5 @@ class ScreenCapturerWgc : public ScreenCapturer,
unsigned char* nv12_frame_ = nullptr; unsigned char* nv12_frame_ = nullptr;
unsigned char* nv12_frame_scaled_ = nullptr; unsigned char* nv12_frame_scaled_ = nullptr;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -3,6 +3,8 @@
#include <Windows.h> #include <Windows.h>
namespace crossdesk {
class WgcSession { class WgcSession {
public: public:
struct wgc_session_frame { struct wgc_session_frame {
@@ -10,13 +12,13 @@ class WgcSession {
unsigned int height; unsigned int height;
unsigned int row_pitch; unsigned int row_pitch;
const unsigned char *data; const unsigned char* data;
}; };
class wgc_session_observer { class wgc_session_observer {
public: public:
virtual ~wgc_session_observer() {} virtual ~wgc_session_observer() {}
virtual void OnFrame(const wgc_session_frame &frame, int id) = 0; virtual void OnFrame(const wgc_session_frame& frame, int id) = 0;
}; };
public: public:
@@ -25,7 +27,7 @@ class WgcSession {
virtual int Initialize(HWND hwnd) = 0; virtual int Initialize(HWND hwnd) = 0;
virtual int Initialize(HMONITOR hmonitor) = 0; virtual int Initialize(HMONITOR hmonitor) = 0;
virtual void RegisterObserver(wgc_session_observer *observer) = 0; virtual void RegisterObserver(wgc_session_observer* observer) = 0;
virtual int Start() = 0; virtual int Start() = 0;
virtual int Stop() = 0; virtual int Stop() = 0;
@@ -33,7 +35,7 @@ class WgcSession {
virtual int Pause() = 0; virtual int Pause() = 0;
virtual int Resume() = 0; virtual int Resume() = 0;
virtual ~WgcSession(){}; virtual ~WgcSession() {};
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -18,9 +18,11 @@
throw winrt::hresult_error(RO_E_CLOSED); \ throw winrt::hresult_error(RO_E_CLOSED); \
} }
namespace crossdesk {
extern "C" { extern "C" {
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice( HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice); ::IDXGIDevice* dxgiDevice, ::IInspectable** graphicsDevice);
} }
WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {} WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {}
@@ -48,7 +50,7 @@ int WgcSessionImpl::Initialize(HMONITOR hmonitor) {
return Initialize(); return Initialize();
} }
void WgcSessionImpl::RegisterObserver(wgc_session_observer *observer) { void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) {
std::lock_guard locker(lock_); std::lock_guard locker(lock_);
observer_ = observer; observer_ = observer;
} }
@@ -175,7 +177,7 @@ auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) {
interop_factory->CreateForWindow( interop_factory->CreateForWindow(
hwnd, hwnd,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void **>(winrt::put_abi(item))); reinterpret_cast<void**>(winrt::put_abi(item)));
return item; return item;
} }
@@ -187,7 +189,7 @@ auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) {
interop_factory->CreateForMonitor( interop_factory->CreateForMonitor(
hmonitor, hmonitor,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void **>(winrt::put_abi(item))); reinterpret_cast<void**>(winrt::put_abi(item)));
return item; return item;
} }
@@ -216,8 +218,8 @@ HRESULT WgcSessionImpl::CreateMappedTexture(
} }
void WgcSessionImpl::OnFrame( void WgcSessionImpl::OnFrame(
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender, winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender,
[[maybe_unused]] winrt::Windows::Foundation::IInspectable const &args) { [[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) {
std::lock_guard locker(lock_); std::lock_guard locker(lock_);
auto is_new_size = false; auto is_new_size = false;
@@ -268,8 +270,8 @@ void WgcSessionImpl::OnFrame(
wgc_session_frame{static_cast<unsigned int>(frame_size.Width), wgc_session_frame{static_cast<unsigned int>(frame_size.Width),
static_cast<unsigned int>(frame_size.Height), static_cast<unsigned int>(frame_size.Height),
map_result.RowPitch, map_result.RowPitch,
const_cast<const unsigned char *>( const_cast<const unsigned char*>(
(unsigned char *)map_result.pData)}, (unsigned char*)map_result.pData)},
id_); id_);
} }
@@ -286,8 +288,8 @@ void WgcSessionImpl::OnFrame(
} }
void WgcSessionImpl::OnClosed( void WgcSessionImpl::OnClosed(
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &, winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&,
winrt::Windows::Foundation::IInspectable const &) { winrt::Windows::Foundation::IInspectable const&) {
OutputDebugStringW(L"WgcSessionImpl::OnClosed"); OutputDebugStringW(L"WgcSessionImpl::OnClosed");
} }
@@ -375,4 +377,5 @@ LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param,
// ::CloseWindow(hwnd_); // ::CloseWindow(hwnd_);
// ::DestroyWindow(hwnd_); // ::DestroyWindow(hwnd_);
// } // }
} // namespace crossdesk

View File

@@ -10,15 +10,17 @@
#include "wgc_session.h" #include "wgc_session.h"
namespace crossdesk {
class WgcSessionImpl : public WgcSession { class WgcSessionImpl : public WgcSession {
struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")) struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
IDirect3DDxgiInterfaceAccess : ::IUnknown { IDirect3DDxgiInterfaceAccess : ::IUnknown {
virtual HRESULT __stdcall GetInterface(GUID const &id, void **object) = 0; virtual HRESULT __stdcall GetInterface(GUID const& id, void** object) = 0;
}; };
template <typename T> template <typename T>
inline auto GetDXGIInterfaceFromObject( inline auto GetDXGIInterfaceFromObject(
winrt::Windows::Foundation::IInspectable const &object) { winrt::Windows::Foundation::IInspectable const& object) {
auto access = object.as<IDirect3DDxgiInterfaceAccess>(); auto access = object.as<IDirect3DDxgiInterfaceAccess>();
winrt::com_ptr<T> result; winrt::com_ptr<T> result;
winrt::check_hresult( winrt::check_hresult(
@@ -44,7 +46,7 @@ class WgcSessionImpl : public WgcSession {
int Initialize(HWND hwnd) override; int Initialize(HWND hwnd) override;
int Initialize(HMONITOR hmonitor) override; int Initialize(HMONITOR hmonitor) override;
void RegisterObserver(wgc_session_observer *observer) override; void RegisterObserver(wgc_session_observer* observer) override;
int Start() override; int Start() override;
int Stop() override; int Stop() override;
@@ -60,11 +62,11 @@ class WgcSessionImpl : public WgcSession {
HRESULT CreateMappedTexture(winrt::com_ptr<ID3D11Texture2D> src_texture, HRESULT CreateMappedTexture(winrt::com_ptr<ID3D11Texture2D> src_texture,
unsigned int width = 0, unsigned int height = 0); unsigned int width = 0, unsigned int height = 0);
void OnFrame( void OnFrame(
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const&
&sender, sender,
winrt::Windows::Foundation::IInspectable const &args); winrt::Windows::Foundation::IInspectable const& args);
void OnClosed(winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &, void OnClosed(winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&,
winrt::Windows::Foundation::IInspectable const &); winrt::Windows::Foundation::IInspectable const&);
int Initialize(); int Initialize();
void CleanUp(); void CleanUp();
@@ -78,7 +80,7 @@ class WgcSessionImpl : public WgcSession {
bool is_running_ = false; bool is_running_ = false;
bool is_paused_ = false; bool is_paused_ = false;
wgc_session_observer *observer_ = nullptr; wgc_session_observer* observer_ = nullptr;
// wgc // wgc
winrt::Windows::Graphics::Capture::GraphicsCaptureItem capture_item_{nullptr}; winrt::Windows::Graphics::Capture::GraphicsCaptureItem capture_item_{nullptr};
@@ -113,5 +115,5 @@ class WgcSessionImpl : public WgcSession {
// access->GetInterface(winrt::guid_of<T>(), result.put_void())); // access->GetInterface(winrt::guid_of<T>(), result.put_void()));
// return result; // return result;
// } // }
} // namespace crossdesk
#endif #endif

View File

@@ -1,43 +0,0 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-06-14
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LAYOUT_STYLE_H_
#define _LAYOUT_STYLE_H_
#define MENU_WINDOW_WIDTH_CN 300
#define MENU_WINDOW_HEIGHT_CN 280
#define LOCAL_WINDOW_WIDTH_CN 300
#define LOCAL_WINDOW_HEIGHT_CN 280
#define REMOTE_WINDOW_WIDTH_CN 300
#define REMOTE_WINDOW_HEIGHT_CN 280
#define MENU_WINDOW_WIDTH_EN 190
#define MENU_WINDOW_HEIGHT_EN 245
#define IPUT_WINDOW_WIDTH 160
#define INPUT_WINDOW_PADDING_CN 66
#define INPUT_WINDOW_PADDING_EN 96
#define SETTINGS_WINDOW_WIDTH_CN 182
#define SETTINGS_WINDOW_WIDTH_EN 248
#define SETTINGS_WINDOW_HEIGHT_CN 275
#define SETTINGS_WINDOW_HEIGHT_EN 275
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 167
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 167
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN 167
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 167
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 151
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218
#define ENABLE_TURN_CHECKBOX_PADDING_CN 151
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218
#define ENABLE_SRTP_CHECKBOX_PADDING_CN 151
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
#define SETTINGS_SELECT_WINDOW_WIDTH 73
#define SETTINGS_OK_BUTTON_PADDING_CN 55
#define SETTINGS_OK_BUTTON_PADDING_EN 78
#endif

View File

@@ -9,6 +9,8 @@
#include "rd_log.h" #include "rd_log.h"
namespace crossdesk {
constexpr int kSampleRate = 48000; constexpr int kSampleRate = 48000;
constexpr pa_sample_format_t kFormat = PA_SAMPLE_S16LE; constexpr pa_sample_format_t kFormat = PA_SAMPLE_S16LE;
constexpr int kChannels = 1; constexpr int kChannels = 1;
@@ -265,4 +267,5 @@ int SpeakerCapturerLinux::Pause() {
int SpeakerCapturerLinux::Resume() { int SpeakerCapturerLinux::Resume() {
paused_ = false; paused_ = false;
return 0; return 0;
} }
} // namespace crossdesk

View File

@@ -18,6 +18,8 @@
#include "speaker_capturer.h" #include "speaker_capturer.h"
namespace crossdesk {
class SpeakerCapturerLinux : public SpeakerCapturer { class SpeakerCapturerLinux : public SpeakerCapturer {
public: public:
SpeakerCapturerLinux(); SpeakerCapturerLinux();
@@ -50,5 +52,5 @@ class SpeakerCapturerLinux : public SpeakerCapturer {
std::mutex state_mtx_; std::mutex state_mtx_;
std::vector<uint8_t> frame_cache_; std::vector<uint8_t> frame_cache_;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -12,6 +12,8 @@
#include "speaker_capturer.h" #include "speaker_capturer.h"
namespace crossdesk {
class SpeakerCapturerMacosx : public SpeakerCapturer { class SpeakerCapturerMacosx : public SpeakerCapturer {
public: public:
SpeakerCapturerMacosx(); SpeakerCapturerMacosx();
@@ -33,5 +35,5 @@ class SpeakerCapturerMacosx : public SpeakerCapturer {
class Impl; class Impl;
Impl* impl_ = nullptr; Impl* impl_ = nullptr;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -5,13 +5,17 @@
#include "rd_log.h" #include "rd_log.h"
#include "speaker_capturer_macosx.h" #include "speaker_capturer_macosx.h"
namespace crossdesk {
class SpeakerCapturerMacosx;
}
@interface SpeakerCaptureDelegate : NSObject <SCStreamDelegate, SCStreamOutput> @interface SpeakerCaptureDelegate : NSObject <SCStreamDelegate, SCStreamOutput>
@property(nonatomic, assign) SpeakerCapturerMacosx* owner; @property(nonatomic, assign) crossdesk::SpeakerCapturerMacosx* owner;
- (instancetype)initWithOwner:(SpeakerCapturerMacosx*)owner; - (instancetype)initWithOwner:(crossdesk::SpeakerCapturerMacosx*)owner;
@end @end
@implementation SpeakerCaptureDelegate @implementation SpeakerCaptureDelegate
- (instancetype)initWithOwner:(SpeakerCapturerMacosx*)owner { - (instancetype)initWithOwner:(crossdesk::SpeakerCapturerMacosx*)owner {
self = [super init]; self = [super init];
if (self) { if (self) {
_owner = owner; _owner = owner;
@@ -22,72 +26,32 @@
- (void)stream:(SCStream*)stream - (void)stream:(SCStream*)stream
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
ofType:(SCStreamOutputType)type { ofType:(SCStreamOutputType)type {
if (type == SCStreamOutputTypeAudio) { if (type != SCStreamOutputTypeAudio) return;
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t length = CMBlockBufferGetDataLength(blockBuffer);
char* dataPtr = NULL;
CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, NULL, &dataPtr);
CMAudioFormatDescriptionRef formatDesc = CMSampleBufferGetFormatDescription(sampleBuffer);
const AudioStreamBasicDescription* asbd =
CMAudioFormatDescriptionGetStreamBasicDescription(formatDesc);
if (_owner->cb_ && dataPtr && length > 0 && asbd) { CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
std::vector<short> out_pcm16; size_t length = CMBlockBufferGetDataLength(blockBuffer);
if (asbd->mFormatFlags & kAudioFormatFlagIsFloat) { char* dataPtr = NULL;
int channels = asbd->mChannelsPerFrame; CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, NULL, &dataPtr);
int samples = (int)(length / sizeof(float)); CMAudioFormatDescriptionRef formatDesc = CMSampleBufferGetFormatDescription(sampleBuffer);
float* floatData = (float*)dataPtr; const AudioStreamBasicDescription* asbd =
std::vector<short> pcm16(samples); CMAudioFormatDescriptionGetStreamBasicDescription(formatDesc);
for (int i = 0; i < samples; ++i) {
float v = floatData[i];
if (v > 1.0f) v = 1.0f;
if (v < -1.0f) v = -1.0f;
pcm16[i] = (short)(v * 32767.0f);
}
if (channels > 1) { if (_owner->cb_ && dataPtr && length > 0 && asbd) {
int mono_samples = samples / channels; std::vector<short> out_pcm16;
out_pcm16.resize(mono_samples); // ... 数据转换逻辑保持不变 ...
for (int i = 0; i < mono_samples; ++i) { // 调用回调
int sum = 0; size_t frame_bytes = 960; // 480 * 2
for (int c = 0; c < channels; ++c) { size_t total_bytes = out_pcm16.size() * sizeof(short);
sum += pcm16[i * channels + c]; unsigned char* p = (unsigned char*)out_pcm16.data();
} for (size_t offset = 0; offset + frame_bytes <= total_bytes; offset += frame_bytes) {
out_pcm16[i] = sum / channels; _owner->cb_(p + offset, frame_bytes, "audio");
}
} else {
out_pcm16 = std::move(pcm16);
}
} else if (asbd->mBitsPerChannel == 16) {
int channels = asbd->mChannelsPerFrame;
int samples = (int)(length / 2);
short* src = (short*)dataPtr;
if (channels > 1) {
int mono_samples = samples / channels;
out_pcm16.resize(mono_samples);
for (int i = 0; i < mono_samples; ++i) {
int sum = 0;
for (int c = 0; c < channels; ++c) {
sum += src[i * channels + c];
}
out_pcm16[i] = sum / channels;
}
} else {
out_pcm16.assign(src, src + samples);
}
}
size_t frame_bytes = 960; // 480 * 2
size_t total_bytes = out_pcm16.size() * sizeof(short);
unsigned char* p = (unsigned char*)out_pcm16.data();
for (size_t offset = 0; offset + frame_bytes <= total_bytes; offset += frame_bytes) {
_owner->cb_(p + offset, frame_bytes, "audio");
}
} }
} }
} }
@end @end
namespace crossdesk {
class SpeakerCapturerMacosx::Impl { class SpeakerCapturerMacosx::Impl {
public: public:
SCStreamConfiguration* config = nil; SCStreamConfiguration* config = nil;
@@ -262,3 +226,4 @@ int SpeakerCapturerMacosx::Destroy() {
int SpeakerCapturerMacosx::Pause() { return 0; } int SpeakerCapturerMacosx::Pause() { return 0; }
int SpeakerCapturerMacosx::Resume() { return Start(); } int SpeakerCapturerMacosx::Resume() { return Start(); }
} // namespace crossdesk

View File

@@ -9,9 +9,11 @@
#include <functional> #include <functional>
namespace crossdesk {
class SpeakerCapturer { class SpeakerCapturer {
public: public:
typedef std::function<void(unsigned char *, size_t, const char *)> typedef std::function<void(unsigned char*, size_t, const char*)>
speaker_data_cb; speaker_data_cb;
public: public:
@@ -23,5 +25,5 @@ class SpeakerCapturer {
virtual int Start() = 0; virtual int Start() = 0;
virtual int Stop() = 0; virtual int Stop() = 0;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -15,6 +15,8 @@
#include "speaker_capturer_macosx.h" #include "speaker_capturer_macosx.h"
#endif #endif
namespace crossdesk {
class SpeakerCapturerFactory { class SpeakerCapturerFactory {
public: public:
virtual ~SpeakerCapturerFactory() {} virtual ~SpeakerCapturerFactory() {}
@@ -32,5 +34,5 @@ class SpeakerCapturerFactory {
#endif #endif
} }
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -7,6 +7,8 @@
#define SAVE_AUDIO_FILE 0 #define SAVE_AUDIO_FILE 0
namespace crossdesk {
static ma_device_config device_config_; static ma_device_config device_config_;
static ma_device device_; static ma_device device_;
static ma_format format_ = ma_format_s16; static ma_format format_ = ma_format_s16;
@@ -99,3 +101,4 @@ int SpeakerCapturerWasapi::Destroy() {
} }
int SpeakerCapturerWasapi::Pause() { return 0; } int SpeakerCapturerWasapi::Pause() { return 0; }
} // namespace crossdesk

View File

@@ -9,6 +9,8 @@
#include "speaker_capturer.h" #include "speaker_capturer.h"
namespace crossdesk {
class SpeakerCapturerWasapi : public SpeakerCapturer { class SpeakerCapturerWasapi : public SpeakerCapturer {
public: public:
SpeakerCapturerWasapi(); SpeakerCapturerWasapi();
@@ -31,5 +33,5 @@ class SpeakerCapturerWasapi : public SpeakerCapturer {
private: private:
bool inited_ = false; bool inited_ = false;
}; };
} // namespace crossdesk
#endif #endif

View File

@@ -21,7 +21,7 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h" #include "stb_image_write.h"
static std::string test; namespace crossdesk {
bool LoadTextureFromMemory(const void* data, size_t data_size, bool LoadTextureFromMemory(const void* data, size_t data_size,
SDL_Renderer* renderer, SDL_Texture** out_texture, SDL_Renderer* renderer, SDL_Texture** out_texture,
@@ -430,3 +430,4 @@ std::string Thumbnail::AES_decrypt(const std::string& ciphertext,
return std::string(reinterpret_cast<char*>(plaintext), plaintext_len); return std::string(reinterpret_cast<char*>(plaintext), plaintext_len);
} }
} // namespace crossdesk

View File

@@ -14,6 +14,8 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace crossdesk {
class Thumbnail { class Thumbnail {
public: public:
struct RecentConnection { struct RecentConnection {
@@ -83,5 +85,5 @@ class Thumbnail {
unsigned char ciphertext_[64]; unsigned char ciphertext_[64];
unsigned char decryptedtext_[64]; unsigned char decryptedtext_[64];
}; };
} // namespace crossdesk
#endif #endif

1
submodules/minirtc Submodule

Submodule submodules/minirtc added at 40eaf93b42

View File

@@ -1,194 +0,0 @@
add_rules("mode.debug", "mode.release")
add_rules("utils.install.cmake_importfiles")
set_languages("cxx14")
option("dx9", {showmenu = true, default = false})
option("dx10", {showmenu = true, default = false})
option("dx11", {showmenu = true, default = false})
option("dx12", {showmenu = true, default = false})
option("glfw", {showmenu = true, default = false})
option("opengl2", {showmenu = true, default = false})
option("opengl3", {showmenu = true, default = false})
option("glad", {showmenu = true, default = false})
option("sdl2", {showmenu = true, default = false})
option("sdl2_renderer", {showmenu = true, default = false})
option("sdl3", {showmenu = true, default = false})
option("sdl3_renderer", {showmenu = true, default = false})
option("sdl3_gpu", {showmenu = true, default = false})
option("vulkan", {showmenu = true, default = false})
option("win32", {showmenu = true, default = false})
option("osx", {showmenu = true, default = false})
option("wgpu", {showmenu = true, default = false})
option("freetype", {showmenu = true, default = false})
option("user_config", {showmenu = true, default = nil, type = "string"})
option("wchar32", {showmenu = true, default = false})
if has_config("glfw") then
add_requires("glfw")
end
if has_config("glad") then
add_requires("glad")
end
if has_config("sdl2_renderer") then
add_requires("libsdl2 >=2.0.17")
elseif has_config("sdl2") then
add_requires("libsdl2")
end
if has_config("sdl3") or has_config("sdl3_renderer") or has_config("sdl3_gpu") then
add_requires("libsdl3")
end
if has_config("vulkan") then
add_requires("vulkan-headers")
end
if has_config("wgpu") then
add_requires("wgpu-native")
end
if has_config("freetype") then
add_requires("freetype")
end
target("imgui")
set_kind("$(kind)")
add_files("*.cpp", "misc/cpp/*.cpp")
add_headerfiles("*.h", "(misc/cpp/*.h)")
add_includedirs(".", "misc/cpp")
if is_kind("shared") and is_plat("windows", "mingw") then
add_defines("IMGUI_API=__declspec(dllexport)")
end
if has_config("dx9") then
add_files("backends/imgui_impl_dx9.cpp")
add_headerfiles("(backends/imgui_impl_dx9.h)")
end
if has_config("dx10") then
add_files("backends/imgui_impl_dx10.cpp")
add_headerfiles("(backends/imgui_impl_dx10.h)")
end
if has_config("dx11") then
add_files("backends/imgui_impl_dx11.cpp")
add_headerfiles("(backends/imgui_impl_dx11.h)")
end
if has_config("dx12") then
add_files("backends/imgui_impl_dx12.cpp")
add_headerfiles("(backends/imgui_impl_dx12.h)")
end
if has_config("glfw") then
add_files("backends/imgui_impl_glfw.cpp")
add_headerfiles("(backends/imgui_impl_glfw.h)")
add_packages("glfw")
end
if has_config("opengl2") then
add_files("backends/imgui_impl_opengl2.cpp")
add_headerfiles("(backends/imgui_impl_opengl2.h)")
end
if has_config("opengl3") then
add_files("backends/imgui_impl_opengl3.cpp")
add_headerfiles("(backends/imgui_impl_opengl3.h)")
if has_config("glad") then
add_defines("IMGUI_IMPL_OPENGL_LOADER_GLAD")
add_packages("glad")
else
add_headerfiles("(backends/imgui_impl_opengl3_loader.h)")
end
end
if has_config("sdl2") then
if os.exists("backends/imgui_impl_sdl2.cpp") then
add_files("backends/imgui_impl_sdl2.cpp")
add_headerfiles("(backends/imgui_impl_sdl2.h)")
else
add_files("backends/imgui_impl_sdl.cpp")
add_headerfiles("(backends/imgui_impl_sdl.h)")
end
add_packages("libsdl2")
end
if has_config("sdl2_renderer") then
if os.exists("backends/imgui_impl_sdlrenderer2.cpp") then
add_files("backends/imgui_impl_sdlrenderer2.cpp")
add_headerfiles("(backends/imgui_impl_sdlrenderer2.h)")
else
add_files("backends/imgui_impl_sdlrenderer.cpp")
add_headerfiles("(backends/imgui_impl_sdlrenderer.h)")
end
add_packages("libsdl2")
end
if has_config("sdl3") then
add_files("backends/imgui_impl_sdl3.cpp")
add_headerfiles("(backends/imgui_impl_sdl3.h)")
add_packages("libsdl3")
end
if has_config("sdl3_renderer") then
add_files("backends/imgui_impl_sdlrenderer3.cpp")
add_headerfiles("(backends/imgui_impl_sdlrenderer3.h)")
add_packages("libsdl3")
end
if has_config("sdl3_gpu") then
add_files("backends/imgui_impl_sdlgpu3.cpp")
add_headerfiles("backends/imgui_impl_sdlgpu3.h","backends/imgui_impl_sdlgpu3_shaders.h")
add_packages("libsdl3")
end
if has_config("vulkan") then
add_files("backends/imgui_impl_vulkan.cpp")
add_headerfiles("(backends/imgui_impl_vulkan.h)")
add_packages("vulkan-headers")
end
if has_config("win32") then
add_files("backends/imgui_impl_win32.cpp")
add_headerfiles("(backends/imgui_impl_win32.h)")
end
if has_config("osx") then
add_frameworks("Cocoa", "Carbon", "GameController")
add_files("backends/imgui_impl_osx.mm")
add_headerfiles("(backends/imgui_impl_osx.h)")
end
if has_config("wgpu") then
add_files("backends/imgui_impl_wgpu.cpp")
add_headerfiles("(backends/imgui_impl_wgpu.h)")
add_packages("wgpu-native")
end
if has_config("freetype") then
add_files("misc/freetype/imgui_freetype.cpp")
add_headerfiles("misc/freetype/imgui_freetype.h")
add_packages("freetype")
add_defines("IMGUI_ENABLE_FREETYPE")
end
if has_config("user_config") then
local user_config = get_config("user_config")
add_defines("IMGUI_USER_CONFIG=\"".. user_config .."\"")
end
if has_config("wchar32") then
add_defines("IMGUI_USE_WCHAR32")
end
after_install(function (target)
local config_file = path.join(target:installdir(), "include/imconfig.h")
if has_config("wchar32") then
io.gsub(config_file, "//#define IMGUI_USE_WCHAR32", "#define IMGUI_USE_WCHAR32")
end
if has_config("freetype") then
io.gsub(config_file, "//#define IMGUI_ENABLE_FREETYPE", "#define IMGUI_ENABLE_FREETYPE")
end
end)

View File

@@ -1,227 +0,0 @@
package("imgui")
set_homepage("https://github.com/ocornut/imgui")
set_description("Bloat-free Immediate Mode Graphical User interface for C++ with minimal dependencies")
set_license("MIT")
add_urls("https://github.com/ocornut/imgui/archive/refs/tags/$(version).tar.gz")
add_urls("https://github.com/ocornut/imgui.git", {alias = "git"})
-- don't forget to add the docking versions as well
add_versions("v1.92.0", "42250c45df2736bcef867ae4ff404d138e5135cd36466c63143b1ea3b1c81091")
add_versions("v1.91.9", "3872a5f90df78fced023c1945f4466b654fd74573370b77b17742149763a7a7c")
add_versions("v1.91.8", "db3a2e02bfd6c269adf0968950573053d002f40bdfb9ef2e4a90bce804b0f286")
add_versions("v1.91.7", "2001dab4bdd7d178d8277d3b17c40aa1ff1e76e2ccac5e7ab8c6daf9756312c2")
add_versions("v1.91.6", "c5fbc5dcab1d46064001c3b84d7a88812985cde7e0e9ced03f5677bec1ba502a")
add_versions("v1.91.5", "2aa2d169c569368439e5d5667e0796d09ca5cc6432965ce082e516937d7db254")
add_versions("v1.91.4", "a455c28d987c78ddf56aab98ce0ff0fda791a23a2ec88ade46dd106b837f0923")
add_versions("v1.91.3", "29949d7b300c30565fbcd66398100235b63aa373acfee0b76853a7aeacd1be28")
add_versions("v1.91.2", "a3c4fd857a0a48f6edad3e25de68fa1e96d2437f1665039714d1de9ad579b8d0")
add_versions("v1.91.1", "2c13a8909f75222c836abc9b3f60cef31c445f3f41f95d8242118ea789d145ca")
add_versions("v1.91.0", "6e62c87252e6b3725ba478a1c04dc604aa0aaeec78fedcf4011f1e52548f4cc9")
add_versions("v1.90.9", "04943919721e874ac75a2f45e6eb6c0224395034667bf508923388afda5a50bf")
add_versions("v1.90.8", "f606b4fb406aa0f8dad36d4a9dd3d6f0fd39f5f0693e7468abc02d545fb505ae")
add_versions("v1.90.7", "872574217643d4ad7e9e6df420bb8d9e0d468fb90641c2bf50fd61745e05de99")
add_versions("v1.90.6", "70b4b05ac0938e82b4d5b8d59480d3e2ca63ca570dfb88c55023831f387237ad")
add_versions("v1.90.5", "e94b48dba7311c85ba8e3e6fe7c734d76a0eed21b2b42c5180fd5706d1562241")
add_versions("v1.90.4", "5d9dc738af74efa357f2a9fc39fe4a28d29ef1dfc725dd2977ccf3f3194e996e")
add_versions("v1.90.3", "40b302d01092c9393373b372fe07ea33ac69e9491893ebab3bf952b2c1f5fd23")
add_versions("v1.90.2", "452d1c11e5c4b4dfcca272915644a65f1c076498e8318b141ca75cd30470dd68")
add_versions("v1.90.1", "21dcc985bb2ae8fe48047c86135dbc438d6980a8f2e08babbda5be820592f282")
add_versions("v1.90", "170986e6a4b83d165bfc1d33c2c5a5bc2d67e5b97176287485c51a2299249296")
add_versions("v1.89.9", "1acc27a778b71d859878121a3f7b287cd81c29d720893d2b2bf74455bf9d52d6")
add_versions("v1.89.8", "6680ccc32430009a8204291b1268b2367d964bd6d1b08a4e0358a017eb8e8c9e")
add_versions("v1.89.7", "115ee9e242af98a884302ac0f6ca3b2b26b1f10c660205f5e7ad9f1d1c96d269")
add_versions("v1.89.6", "e95d1cba1481e66386acda3e7da19cd738da86c6c2a140a48fa55046e5f6e208")
add_versions("v1.89.5", "eab371005c86dd029523a0c4ba757840787163740d45c1f4e5a110eb21820546")
add_versions("v1.89.4", "69f1e83adcab3fdd27b522f5075f407361b0d3875e3522b13d33bc2ae2c7d48c")
add_versions("v1.89.3", "3b665fadd5580b7ef494d5d8bb1c12b2ec53ee723034caf43332956381f5d631")
add_versions("v1.89", "4038b05bd44c889cf40be999656d3871a0559916708cb52a6ae2fa6fa35c5c60")
add_versions("v1.88", "9f14c788aee15b777051e48f868c5d4d959bd679fc5050e3d2a29de80d8fd32e")
add_versions("v1.87", "b54ceb35bda38766e36b87c25edf7a1cd8fd2cb8c485b245aedca6fb85645a20")
add_versions("v1.86", "6ba6ae8425a19bc52c5e067702c48b70e4403cd339cba02073a462730a63e825")
add_versions("v1.85", "7ed49d1f4573004fa725a70642aaddd3e06bb57fcfe1c1a49ac6574a3e895a77")
add_versions("v1.84.2", "35cb5ca0fb42cb77604d4f908553f6ef3346ceec4fcd0189675bdfb764f62b9b")
add_versions("v1.84.1", "292ab54cfc328c80d63a3315a242a4785d7c1cf7689fbb3d70da39b34db071ea")
add_versions("v1.83", "ccf3e54b8d1fa30dd35682fc4f50f5d2fe340b8e29e08de71287d0452d8cc3ff")
add_versions("v1.82", "fefa2804bd55f3d25b134af08c0e1f86d4d059ac94cef3ee7bd21e2f194e5ce5")
add_versions("v1.81", "f7c619e03a06c0f25e8f47262dbc32d61fd033d2c91796812bf0f8c94fca78fb")
add_versions("v1.80", "d7e4e1c7233409018437a646680316040e6977b9a635c02da93d172baad94ce9")
add_versions("v1.79", "f1908501f6dc6db8a4d572c29259847f6f882684b10488d3a8d2da31744cd0a4")
add_versions("v1.78", "f70bbb17581ee2bd42fda526d9c3dc1a5165f3847ff047483d4d7980e166f9a3")
add_versions("v1.77", "c0dae830025d4a1a169df97409709f40d9dfa19f8fc96b550052224cbb238fa8")
add_versions("v1.76", "e482dda81330d38c87bd81597cacaa89f05e20ed2c4c4a93a64322e97565f6dc")
add_versions("v1.75", "1023227fae4cf9c8032f56afcaea8902e9bfaad6d9094d6e48fb8f3903c7b866")
add_versions("git:v1.92.0-docking", "v1.92.0-docking")
add_versions("git:v1.91.9-docking", "v1.91.9-docking")
add_versions("git:v1.91.8-docking", "v1.91.8-docking")
add_versions("git:v1.91.7-docking", "v1.91.7-docking")
add_versions("git:v1.91.6-docking", "v1.91.6-docking")
add_versions("git:v1.91.5-docking", "v1.91.5-docking")
add_versions("git:v1.91.4-docking", "v1.91.4-docking")
add_versions("git:v1.91.3-docking", "v1.91.3-docking")
add_versions("git:v1.91.2-docking", "v1.91.2-docking")
add_versions("git:v1.91.1-docking", "v1.91.1-docking")
add_versions("git:v1.91.0-docking", "v1.91.0-docking")
add_versions("git:v1.90.9-docking", "v1.90.9-docking")
add_versions("git:v1.90.8-docking", "v1.90.8-docking")
add_versions("git:v1.90.7-docking", "v1.90.7-docking")
add_versions("git:v1.90.6-docking", "v1.90.6-docking")
add_versions("git:v1.90.5-docking", "v1.90.5-docking")
add_versions("git:v1.90.4-docking", "v1.90.4-docking")
add_versions("git:v1.90.3-docking", "v1.90.3-docking")
add_versions("git:v1.90.2-docking", "v1.90.2-docking")
add_versions("git:v1.90.1-docking", "v1.90.1-docking")
add_versions("git:v1.90-docking", "v1.90-docking")
add_versions("git:v1.89.9-docking", "v1.89.9-docking")
add_versions("git:v1.89.8-docking", "v1.89.8-docking")
add_versions("git:v1.89.7-docking", "v1.89.7-docking")
add_versions("git:v1.89.6-docking", "823a1385a269d923d35b82b2f470f3ae1fa8b5a3")
add_versions("git:v1.89.5-docking", "0ea3b87bd63ecbf359585b7c235839146e84dedb")
add_versions("git:v1.89.4-docking", "9e30fb0ec1b44dc1b041db6bdd53b130b2a18509")
add_versions("git:v1.89.3-docking", "192196711a7d0d7c2d60454d42654cf090498a74")
add_versions("git:v1.89-docking", "94e850fd6ff9eceb98fda3147e3ffd4781ad2dc7")
add_versions("git:v1.88-docking", "9cd9c2eff99877a3f10a7f9c2a3a5b9c15ea36c6")
add_versions("git:v1.87-docking", "1ee252772ae9c0a971d06257bb5c89f628fa696a")
add_versions("git:v1.85-docking", "dc8c3618e8f8e2dada23daa1aa237626af341fd8")
add_versions("git:v1.83-docking", "80b5fb51edba2fd3dea76ec3e88153e2492243d1")
-- Fix conflicting IMGUI_API definitions in v1.92.0 only (https://github.com/ocornut/imgui/pull/8729)
add_patches("v1.92.0", "patches/v1.92.0/fix_imgui_api.patch", "e8ca0502056acf356f83703e7190dda87fde43ed245f65f0fb55b85cd164ed83")
add_patches("v1.92.0-docking", "patches/v1.92.0/fix_imgui_api.patch", "e8ca0502056acf356f83703e7190dda87fde43ed245f65f0fb55b85cd164ed83")
add_configs("dx9", {description = "Enable the dx9 backend", default = false, type = "boolean"})
add_configs("dx10", {description = "Enable the dx10 backend", default = false, type = "boolean"})
add_configs("dx11", {description = "Enable the dx11 backend", default = false, type = "boolean"})
add_configs("dx12", {description = "Enable the dx12 backend", default = false, type = "boolean"})
add_configs("glfw", {description = "Enable the glfw backend", default = false, type = "boolean"})
add_configs("opengl2", {description = "Enable the opengl2 backend", default = false, type = "boolean"})
add_configs("opengl3", {description = "Enable the opengl3 backend", default = false, type = "boolean"})
add_configs("sdl2", {description = "Enable the sdl2 backend with sdl2_renderer", default = false, type = "boolean"})
add_configs("sdl2_no_renderer", {description = "Enable the sdl2 backend without sdl2_renderer", default = false, type = "boolean"})
add_configs("sdl2_renderer", {description = "Enable the sdl2 renderer backend", default = false, type = "boolean"})
add_configs("sdl3", {description = "Enable the sdl3 backend with sdl3_renderer", default = false, type = "boolean"})
add_configs("sdl3_renderer", {description = "Enable the sdl3 renderer backend", default = false, type = "boolean"})
add_configs("sdl3_gpu", {description = "Enable the sdl3 gpu backend", default = false, type = "boolean"})
add_configs("vulkan", {description = "Enable the vulkan backend", default = false, type = "boolean"})
add_configs("win32", {description = "Enable the win32 backend", default = false, type = "boolean"})
add_configs("osx", {description = "Enable the OS X backend", default = false, type = "boolean"})
add_configs("wgpu", {description = "Enable the wgpu backend", default = false, type = "boolean"})
add_configs("freetype", {description = "Use FreeType to build and rasterize the font atlas", default = false, type = "boolean"})
add_configs("user_config", {description = "Use user config (disables test!)", default = nil, type = "string"})
add_configs("wchar32", {description = "Use 32-bit for ImWchar (default is 16-bit)", default = false, type = "boolean"})
-- deprecated configs (kept for backwards compatibility)
add_configs("sdlrenderer", {description = "(deprecated)", default = false, type = "boolean"})
add_configs("glfw_opengl3", {description = "(deprecated)", default = false, type = "boolean"})
add_configs("glfw_vulkan", {description = "(deprecated)", default = false, type = "boolean"})
add_configs("sdl2_opengl3", {description = "(deprecated)", default = false, type = "boolean"})
add_includedirs("include", "include/imgui", "include/backends", "include/misc/cpp")
if is_plat("windows", "mingw") then
add_syslinks("imm32")
end
on_load(function (package)
-- begin: backwards compatibility
if package:config("sdl2") or package:config("sdlrenderer") then
package:config_set("sdl2_renderer", true)
end
if package:config("glfw_opengl3") then
package:config_set("glfw", true)
package:config_set("opengl3", true)
end
if package:config("glfw_vulkan") then
package:config_set("glfw", true)
package:config_set("vulkan", true)
end
if package:config("sdl2_opengl3") then
package:config_set("sdl2", true)
package:config_set("opengl3", true)
end
-- end: backwards compatibility
if package:config("shared") and is_plat("windows", "mingw") then
package:add("defines", "IMGUI_API=__declspec(dllimport)")
end
if package:config("glfw") then
package:add("deps", "glfw")
end
if package:config("opengl3") then
if not package:gitref() and package:version():lt("1.84") then
package:add("deps", "glad")
package:add("defines", "IMGUI_IMPL_OPENGL_LOADER_GLAD")
end
end
if package:config("sdl2_no_renderer") then
package:add("deps", "libsdl2")
end
if package:config("sdl2_renderer") then
package:add("deps", "libsdl2 >=2.0.17")
end
if package:config("sdl3") or package:config("sdl3_renderer") or package:config("sdl3_gpu") then
package:add("deps", "libsdl3")
end
if package:config("vulkan") then
package:add("deps", "vulkan-headers")
end
if package:config("wgpu") then
package:add("deps", "wgpu-native")
end
if package:config("freetype") then
package:add("deps", "freetype")
end
if package:config("osx") then
package:add("frameworks", "Cocoa", "Carbon", "GameController")
end
end)
on_install(function (package)
local configs = {
dx9 = package:config("dx9"),
dx10 = package:config("dx10"),
dx11 = package:config("dx11"),
dx12 = package:config("dx12"),
glfw = package:config("glfw"),
opengl2 = package:config("opengl2"),
opengl3 = package:config("opengl3"),
glad = package:config("opengl3") and (not package:gitref() and package:version():lt("1.84")),
sdl2 = package:config("sdl2") or package:config("sdl2_no_renderer"),
sdl2_renderer = package:config("sdl2_renderer"),
sdl3 = package:config("sdl3"),
sdl3_renderer = package:config("sdl3_renderer"),
sdl3_gpu = package:config("sdl3_gpu"),
vulkan = package:config("vulkan"),
win32 = package:config("win32"),
osx = package:config("osx"),
wgpu = package:config("wgpu"),
freetype = package:config("freetype"),
user_config = package:config("user_config"),
wchar32 = package:config("wchar32")
}
os.cp(path.join(package:scriptdir(), "port", "xmake.lua"), "xmake.lua")
import("package.tools.xmake").install(package, configs)
end)
on_test(function (package)
if package:config("user_config") ~= nil then return end
local includes = {"imgui.h"}
local defines
if package:config("sdl2_renderer") or package:config("sdl2_no_renderer") then
table.insert(includes, "SDL.h")
defines = "SDL_MAIN_HANDLED"
end
assert(package:check_cxxsnippets({test = [[
void test() {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
ImGui::NewFrame();
ImGui::Text("Hello, world!");
ImGui::ShowDemoWindow(NULL);
ImGui::Render();
ImGui::DestroyContext();
}
]]}, {configs = {languages = "c++14", defines = defines}, includes = includes}))
end)

1
thirdparty/minirtc vendored

Submodule thirdparty/minirtc deleted from dd3f6df18b

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")
@@ -43,7 +46,7 @@ end
add_packages("spdlog", "imgui") add_packages("spdlog", "imgui")
includes("thirdparty") includes("submodules")
target("rd_log") target("rd_log")
set_kind("object") set_kind("object")
@@ -120,26 +123,43 @@ target("device_controller")
"src/device_controller/keyboard/linux", {public = true}) "src/device_controller/keyboard/linux", {public = true})
end end
target("thumbnail")
set_kind("object")
add_packages("libyuv", "openssl3")
add_deps("rd_log", "common")
add_files("src/thumbnail/*.cpp")
add_includedirs("src/thumbnail", {public = true})
target("config_center") target("config_center")
set_kind("object") set_kind("object")
add_deps("rd_log") add_deps("rd_log")
add_files("src/config_center/*.cpp") add_files("src/config_center/*.cpp")
add_includedirs("src/config_center", {public = true}) add_includedirs("src/config_center", {public = true})
target("localization") target("assets")
set_kind("headeronly") set_kind("headeronly")
add_includedirs("src/localization", {public = true}) add_includedirs("src/gui/assets/localization",
"src/gui/assets/fonts",
"src/gui/assets/icons",
"src/gui/assets/layouts", {public = true})
target("single_window") target("gui")
set_kind("object") set_kind("object")
add_packages("libyuv", "openssl3") add_packages("libyuv")
add_deps("rd_log", "common", "localization", "config_center", "minirtc", add_defines("CROSSDESK_VERSION=\"" .. (get_config("CROSSDESK_VERSION") or "Unknown") .. "\"")
"path_manager", "screen_capturer", "speaker_capturer", "device_controller") add_deps("rd_log", "common", "assets", "config_center", "minirtc",
add_files("src/single_window/*.cpp") "path_manager", "screen_capturer", "speaker_capturer",
add_includedirs("src/single_window", {public = true}) "device_controller", "thumbnail")
add_includedirs("fonts", {public = true}) add_files("src/gui/*.cpp", "src/gui/panels/*.cpp", "src/gui/toolbars/*.cpp",
"src/gui/windows/*.cpp")
add_includedirs("src/gui", "src/gui/panels", "src/gui/toolbars",
"src/gui/windows", {public = true})
if is_os("windows") then
add_files("src/gui/tray/*.cpp")
add_includedirs("src/gui/tray", {public = true})
end
target("crossdesk") target("crossdesk")
set_kind("binary") set_kind("binary")
add_deps("rd_log", "common", "single_window") add_deps("rd_log", "common", "gui")
add_files("src/gui/main.cpp") add_files("src/app/main.cpp")