Compare commits

...

56 Commits

Author SHA1 Message Date
dijunkun
5cc31e5ba3 [fix] fix self-hosted server configuration being reset when disabling self-hosted mode 2025-12-08 18:27:18 +08:00
dijunkun
74fe9bebf5 [fix] fix server settings window height 2025-12-08 17:42:02 +08:00
dijunkun
1f6a2182be [fix] resolve tab bar dragging issue 2025-12-06 18:46:04 +08:00
dijunkun
1a883f0d6c [fix] fix menu BeginPopup & EndPopup pairing 2025-12-05 17:53:17 +08:00
dijunkun
a560b4ca70 [feat] make thumbnail save asynchronous to prevent UI blocking 2025-12-05 17:07:55 +08:00
dijunkun
46f45ed216 [fix] fix tab close button not working in stream window 2025-12-05 15:18:35 +08:00
dijunkun
5c23f1c5e8 [fix] correct tab bar scaling and layout 2025-12-05 15:17:07 +08:00
dijunkun
70ae02549f [fix] correct title bar display 2025-12-05 04:28:33 +08:00
dijunkun
68de995c64 [fix] correct hardware codec setting item display 2025-12-05 00:57:33 +08:00
dijunkun
ed5ddb96fd [refactor] update notification window rendering for high-DPI scaling support 2025-12-04 19:00:37 +08:00
dijunkun
436dfafc2a [refactor] update about window rendering for high-DPI scaling support 2025-12-04 17:51:03 +08:00
dijunkun
5221b193e5 [refactor] update settings window rendering for high-DPI scaling support 2025-12-04 17:23:14 +08:00
dijunkun
fafced23c2 [refactor] update stream window rendering for high-DPI scaling support 2025-12-04 02:11:06 +08:00
dijunkun
1e48b645ca [refactor] update recent connections panel rendering for high-DPI scaling support 2025-12-04 00:14:09 +08:00
dijunkun
49ed0200e7 [refactor] update connection status window rendering for high-DPI scaling support 2025-12-03 21:54:10 +08:00
dijunkun
24873afe64 [refactor] update remote peer panel rendering for high-DPI scaling support 2025-12-03 21:17:11 +08:00
dijunkun
d21e1bd422 [refactor] update remote peer panel rendering for high-DPI scaling support 2025-12-03 17:36:19 +08:00
dijunkun
be044c248b [refactor] update local peer panel rendering for high-DPI scaling support 2025-12-03 17:12:35 +08:00
dijunkun
49cbbc3363 [refactor] update status bar rendering for high-DPI scaling support 2025-12-03 12:45:01 +08:00
dijunkun
1e20cb806b [refactor] update title bar rendering for high-DPI scaling support 2025-12-03 04:20:49 +08:00
dijunkun
2e52818f6f fix: correct array deletion and improve state management in WGC screen capturer 2025-12-01 23:06:46 +08:00
dijunkun
b50f386713 [fix] fix system_chinese_font_ usage to avoid dangling font pointer after closing stream window 2025-12-01 23:03:45 +08:00
dijunkun
280e011ae4 [fix] update RecentConnectionsWindow layout 2025-12-01 17:16:28 +08:00
dijunkun
8d09bf53c3 [fix] fix UpdateNotificationWindow dpi scaling 2025-12-01 13:52:39 +08:00
dijunkun
131b4f1795 [fix] resolve compilation errors on Linux 2025-12-01 13:07:36 +08:00
dijunkun
7d3ecf789d [fix] fix control bar dpi scaling 2025-12-01 11:30:35 +08:00
dijunkun
37797bf873 [fix] fix DPI scaling issues 2025-12-01 04:54:30 +08:00
dijunkun
91d42b6561 [fix] macOS: fix audio capture, refs #29 2025-11-30 17:13:02 +08:00
dijunkun
feb9f2f460 [revert] revert to the pre-lock version 2025-11-28 11:44:08 +08:00
Junkun Di
9c1753c78c [chroe] add issue templates 2025-11-28 11:34:22 +08:00
dijunkun
7370ff5b30 [chore] add HelloGitHub badge 2025-11-28 11:10:41 +08:00
dijunkun
f6eda34dbd [fix] fix dead lock during connecting 2025-11-28 10:02:57 +08:00
dijunkun
5d9a0a3ea5 [fix] fix dead lock during peer init 2025-11-28 09:32:21 +08:00
dijunkun
3d8249bffa [fix] use lock and null pointer checks to prevent crashes 2025-11-28 00:41:29 +08:00
dijunkun
82f32cbe8f [fix] fix incorrect peer reference in AddVideoStream call 2025-11-27 23:53:27 +08:00
dijunkun
56da2f99f3 [chore] update Linux log path configuration 2025-11-27 23:20:14 +08:00
dijunkun
e6c72fe558 [feat] remove screen capture and accessibility permission when reinstall 2025-11-27 17:35:15 +08:00
dijunkun
a964c6bbf5 [fix] call log after log initialzation, refs #36, #29 2025-11-27 16:09:08 +08:00
dijunkun
239da373d0 [feat] attempt to rejoin once per second 2025-11-27 04:20:29 +08:00
dijunkun
217cfb091d [feat] display version info on startup 2025-11-27 04:18:08 +08:00
dijunkun
3c3c7b9ae0 [fix] fix crash when screen recording permission is not granted on macOS, refs #29 2025-11-27 03:32:16 +08:00
dijunkun
f14bdb7fe8 [feat] optimize UpdateNotificationWindow UI appearance 2025-11-27 03:07:55 +08:00
dijunkun
c0a98f97c3 [feat] optimize RequestPermissionWindow UI appearance 2025-11-27 02:40:33 +08:00
dijunkun
0ab6686eb8 [feat] use DrawToggleSwitch to request permission 2025-11-26 23:32:04 +08:00
dijunkun
76b475450b [feat] request macOS system permissions by showing a prompt on startup 2025-11-26 18:18:40 +08:00
dijunkun
5d1e1b5667 [fix] remove permissions on uninstall and request permissions during installation for MacOS 2025-11-26 16:11:49 +08:00
dijunkun
c3b8b1374a [chore] update README 2025-11-25 16:30:14 +08:00
dijunkun
7c940d6b15 [ci] fix tag error in update-version-json.yml 2025-11-25 03:30:28 +08:00
dijunkun
86501b05dd [feat] show notification window if there is a new version avaliable 2025-11-25 01:20:16 +08:00
dijunkun
01ebed9b37 [ci] upload release notes to version.json 2025-11-25 01:19:12 +08:00
dijunkun
2188adb1f1 [chore] disable CROSSDESK_DEBUG in keyboard and mouse control 2025-11-24 16:47:33 +08:00
dijunkun
51409e16c8 [chore] update README 2025-11-23 01:54:54 +08:00
dijunkun
6ca3b58ae2 [fix] update MiniRTC to resolve occasional crash during connection shutdown, refs #29 2025-11-23 01:47:48 +08:00
dijunkun
692e176e34 [ci] use github.sha instead of hashFiles for xmake dependency cache 2025-11-23 00:35:26 +08:00
dijunkun
4fb7acaa61 [feat] set enable TURN by default 2025-11-23 00:02:04 +08:00
dijunkun
c0d6429a54 [fix] resolve missing mouse cursor display when web client connects to Linux devices, refs #30 2025-11-22 23:57:49 +08:00
45 changed files with 3569 additions and 1846 deletions

35
.github/ISSUE_TEMPLATE/问题反馈.md vendored Normal file
View File

@@ -0,0 +1,35 @@
---
name: 问题反馈
about: Create a report to help us improve
title: ''
labels: bug
assignees: kunkundi
---
**描述问题**
清晰简洁地描述遇到的错误。
**复现步骤**
复现该问题的步骤:
1. 前往 '...'
2. 点击 '....'
3. 出现错误
**预期行为**
清晰简洁地描述你期望发生的行为。
**截图**
如果适用,请添加截图以帮助说明问题。
**桌面端信息(请填写以下内容):**
- 操作系统: [例如 Windows 11]
- 版本: [例如 v1.1.10]
**移动端信息(请填写以下内容):**
- 设备: [例如 iPhone 17]
- 操作系统: [例如 iOS 26.1]
- 浏览器: [例如 系统浏览器、Safari]
**补充信息**
在此添加与问题相关的其他上下文内容。

View File

@@ -174,7 +174,7 @@ jobs:
uses: actions/cache@v4
with:
path: ~/.xmake/packages
key: ${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}-${{ hashFiles('**/xmake.lua') }}
key: ${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-xmake-deps-${{ matrix.cache-key }}-
@@ -237,7 +237,7 @@ jobs:
uses: actions/cache@v4
with:
path: D:\xmake_global\.xmake\packages
key: ${{ runner.os }}-xmake-deps-intel-${{ hashFiles('**/xmake.lua') }}
key: ${{ runner.os }}-xmake-deps-intel-${{ github.sha }}
restore-keys: |
${{ runner.os }}-xmake-deps-intel-
@@ -408,6 +408,9 @@ jobs:
{
"version": "${{ steps.version.outputs.VERSION_ONLY }}",
"releaseDate": "${{ steps.version.outputs.BUILD_DATE_ISO }}",
"releaseName": "",
"releaseNotes": "",
"tagName": "${{ steps.version.outputs.VERSION_WITH_V }}",
"downloads": {
"windows-x64": {
"url": "https://downloads.crossdesk.cn/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe",

View File

@@ -0,0 +1,140 @@
name: Update version.json from Release
on:
release:
types: [published, edited]
permissions:
contents: write
jobs:
update-version-json:
name: Update version.json with release information
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version from tag
id: version
run: |
TAG_NAME="${{ github.event.release.tag_name }}"
VERSION_ONLY="${TAG_NAME#v}"
echo "TAG_NAME=${TAG_NAME}" >> $GITHUB_OUTPUT
echo "VERSION_ONLY=${VERSION_ONLY}" >> $GITHUB_OUTPUT
# Extract date from tag if available (format: v1.2.3-20251113-abc)
if [[ "${TAG_NAME}" =~ -([0-9]{8})- ]]; then
DATE_STR="${BASH_REMATCH[1]}"
BUILD_DATE_ISO="${DATE_STR:0:4}-${DATE_STR:4:2}-${DATE_STR:6:2}"
else
# Use release published date
BUILD_DATE_ISO=$(echo "${{ github.event.release.published_at }}" | cut -d'T' -f1)
fi
echo "BUILD_DATE_ISO=${BUILD_DATE_ISO}" >> $GITHUB_OUTPUT
- name: Install jq
run: sudo apt-get update && sudo apt-get install -y jq
- name: Get release information
id: release_info
run: |
# Use jq to properly escape JSON
RELEASE_BODY="${{ github.event.release.body }}"
RELEASE_NAME="${{ github.event.release.name }}"
# Handle empty values
if [ -z "$RELEASE_BODY" ]; then
RELEASE_BODY=""
fi
if [ -z "$RELEASE_NAME" ]; then
RELEASE_NAME=""
fi
# Save to temporary files for proper handling
echo -n "$RELEASE_BODY" > /tmp/release_body.txt
echo -n "$RELEASE_NAME" > /tmp/release_name.txt
# Use jq to escape JSON strings
RELEASE_BODY_JSON=$(jq -Rs . < /tmp/release_body.txt)
RELEASE_NAME_JSON=$(jq -Rs . < /tmp/release_name.txt)
echo "RELEASE_BODY=${RELEASE_BODY_JSON}" >> $GITHUB_OUTPUT
echo "RELEASE_NAME=${RELEASE_NAME_JSON}" >> $GITHUB_OUTPUT
- name: Download current version.json from server
id: download_version
continue-on-error: true
run: |
# Try to download current version.json from server
curl -f -s "https://version.crossdesk.cn/version.json" -o version.json || echo "Failed to download, will create new one"
- name: Generate or update version.json
run: |
# If version.json exists, try to preserve downloads section
if [ -f version.json ] && jq -e '.downloads' version.json > /dev/null 2>&1; then
EXISTING_DOWNLOADS=$(jq -c '.downloads' version.json)
if [ "$EXISTING_DOWNLOADS" != "null" ] && [ "$EXISTING_DOWNLOADS" != "{}" ]; then
DOWNLOADS_JSON="$EXISTING_DOWNLOADS"
else
DOWNLOADS_JSON=""
fi
else
DOWNLOADS_JSON=""
fi
# If downloads is empty, use default structure
if [ -z "$DOWNLOADS_JSON" ]; then
DOWNLOADS_JSON=$(cat << DOWNLOADS_EOF
{
"windows-x64": {
"url": "https://downloads.crossdesk.cn/crossdesk-win-x64-${{ steps.version.outputs.TAG_NAME }}.exe",
"filename": "crossdesk-win-x64-${{ steps.version.outputs.TAG_NAME }}.exe"
},
"macos-x64": {
"url": "https://downloads.crossdesk.cn/crossdesk-macos-x64-${{ steps.version.outputs.TAG_NAME }}.pkg",
"filename": "crossdesk-macos-x64-${{ steps.version.outputs.TAG_NAME }}.pkg"
},
"macos-arm64": {
"url": "https://downloads.crossdesk.cn/crossdesk-macos-arm64-${{ steps.version.outputs.TAG_NAME }}.pkg",
"filename": "crossdesk-macos-arm64-${{ steps.version.outputs.TAG_NAME }}.pkg"
},
"linux-amd64": {
"url": "https://downloads.crossdesk.cn/crossdesk-linux-amd64-${{ steps.version.outputs.TAG_NAME }}.deb",
"filename": "crossdesk-linux-amd64-${{ steps.version.outputs.TAG_NAME }}.deb"
},
"linux-arm64": {
"url": "https://downloads.crossdesk.cn/crossdesk-linux-arm64-${{ steps.version.outputs.TAG_NAME }}.deb",
"filename": "crossdesk-linux-arm64-${{ steps.version.outputs.TAG_NAME }}.deb"
}
}
DOWNLOADS_EOF
)
fi
# Generate version.json using cat and heredoc
cat > version.json << EOF
{
"version": "${{ steps.version.outputs.VERSION_ONLY }}",
"releaseDate": "${{ steps.version.outputs.BUILD_DATE_ISO }}",
"releaseName": ${{ steps.release_info.outputs.RELEASE_NAME }},
"releaseNotes": ${{ steps.release_info.outputs.RELEASE_BODY }},
"tagName": "${{ steps.version.outputs.TAG_NAME }}",
"downloads": ${DOWNLOADS_JSON}
}
EOF
cat version.json
- name: Upload version.json to server
uses: burnett01/rsync-deployments@5.2
with:
switches: -avzr --delete
path: version.json
remote_path: /var/www/html/version/
remote_host: ${{ secrets.SERVER_HOST }}
remote_user: ${{ secrets.SERVER_USER }}
remote_key: ${{ secrets.SERVER_KEY }}

4
.gitignore vendored
View File

@@ -1,10 +1,10 @@
# Xmake cache
.xmake/
build/
certs/
# MacOS Cache
.DS_Store
# VSCode cache
.vscode
continuous-desk.code-workspace
.vscode

View File

@@ -1,5 +1,6 @@
# CrossDesk
<a href="https://hellogithub.com/repository/kunkundi/crossdesk" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=55d41367570345f1838e02fd12be7961&claim_uid=cb0OpZRrBuGVAfL&theme=small" alt="FeaturedHelloGitHub" /></a>
[![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/web-client)
@@ -61,7 +62,7 @@ CrossDesk 是 [MiniRTC](https://github.com/kunkundi/minirtc.git) 实时音视频
Linux环境下需安装以下包
```
sudo apt-get install -y software-properties-common git curl unzip build-essential libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxv-dev libxtst-dev libasound2-dev libsndio-dev libxcb-shm0-dev libasound2-dev libpulse-dev
sudo apt-get install -y software-properties-common git curl unzip build-essential libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxfixes-dev libxv-dev libxtst-dev libasound2-dev libsndio-dev libxcb-shm0-dev libasound2-dev libpulse-dev
```
编译
@@ -315,10 +316,10 @@ Generation complete. Deployment files::
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>
@@ -331,5 +332,8 @@ Generation complete. Deployment files::
7. 勾选使用**自托管服务器配置**,点击确认配置生效。<br><br>
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/1e455dc3-4087-4f37-a544-1ff9f8789383" /><br><br>
### Web 客户端
详情见项目 [CrossDesk Web Client](https://github.com/kunkundi/crossdesk-web-client)。
# 常见问题
见 [常见问题](https://github.com/kunkundi/crossdesk/blob/self-hosted-server/docs/FAQ.md) 。

View File

@@ -1,5 +1,6 @@
# CrossDesk
<a href="https://hellogithub.com/repository/kunkundi/crossdesk" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=55d41367570345f1838e02fd12be7961&claim_uid=cb0OpZRrBuGVAfL&theme=small" alt="FeaturedHelloGitHub" /></a>
[![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/web-client)
@@ -64,7 +65,7 @@ Requirements:
Following packages need to be installed on Linux:
```
sudo apt-get install -y software-properties-common git curl unzip build-essential libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxv-dev libxtst-dev libasound2-dev libsndio-dev libxcb-shm0-dev libasound2-dev libpulse-dev
sudo apt-get install -y software-properties-common git curl unzip build-essential libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxfixes-dev libxv-dev libxtst-dev libasound2-dev libsndio-dev libxcb-shm0-dev libasound2-dev libpulse-dev
```
Build:
@@ -323,10 +324,10 @@ Generation complete. Deployment files::
Server certificate: crossdesk.cn_bundle.crt
```
#### Server Side
### Server Side
Place **crossdesk.cn.key** and **crossdesk.cn_bundle.crt** into the **/path/to/your/certs** directory.
#### Client Side
### 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>
@@ -339,5 +340,8 @@ Place **crossdesk.cn.key** and **crossdesk.cn_bundle.crt** into the **/path/to/y
4. Check the option to use **Self-Hosted Server Configuration**.<br><br>
<img width="600" height="160" alt="image" src="https://github.com/user-attachments/assets/1e455dc3-4087-4f37-a544-1ff9f8789383" /><br><br>
### Web Client
See [CrossDesk Web Client](https://github.com/kunkundi/crossdesk-web-client)。
# FAQ
See [FAQ](https://github.com/kunkundi/crosssesk/blob/self-hosted-server/docs/FAQ.md) .

49
scripts/macosx/pkg_arm64.sh Normal file → Executable file
View File

@@ -86,29 +86,66 @@ pkgbuild \
--component "${APP_BUNDLE}" \
build_pkg_temp/${APP_NAME}-component.pkg
mkdir -p scripts
mkdir -p build_pkg_scripts
cat > scripts/postinstall <<'EOF'
cat > build_pkg_scripts/postinstall <<'EOF'
#!/bin/bash
set -e
IDENTIFIER="cn.crossdesk.app"
# 获取当前登录用户
USER_HOME=$( /usr/bin/stat -f "%Su" /dev/console )
HOME_DIR=$( /usr/bin/dscl . -read /Users/$USER_HOME NFSHomeDirectory | awk '{print $2}' )
# 复制证书文件
DEST="$HOME_DIR/Library/Application Support/CrossDesk/certs"
mkdir -p "$DEST"
cp -R "/Library/Application Support/CrossDesk/certs/"* "$DEST/"
# 清除应用的权限授权,以便重新授权
# 使用 tccutil 重置录屏权限和辅助功能权限
if command -v tccutil >/dev/null 2>&1; then
# 重置录屏权限
tccutil reset ScreenCapture "$IDENTIFIER" 2>/dev/null || true
# 重置辅助功能权限
tccutil reset Accessibility "$IDENTIFIER" 2>/dev/null || true
# 重置摄像头权限(如果需要)
tccutil reset Camera "$IDENTIFIER" 2>/dev/null || true
# 重置麦克风权限(如果需要)
tccutil reset Microphone "$IDENTIFIER" 2>/dev/null || true
fi
# 为所有用户清除权限(可选,如果需要)
# 遍历所有用户目录并清除权限
for USER_DIR in /Users/*; do
if [ -d "$USER_DIR" ] && [ "$USER_DIR" != "/Users/Shared" ]; then
USER_NAME=$(basename "$USER_DIR")
# 跳过系统用户
if [ "$USER_NAME" != "Shared" ] && [ -d "$USER_DIR/Library" ]; then
# 删除 TCC 数据库中的相关条目(需要管理员权限)
TCC_DB="$USER_DIR/Library/Application Support/com.apple.TCC/TCC.db"
if [ -f "$TCC_DB" ]; then
# 使用 sqlite3 删除相关权限记录(如果可用)
if command -v sqlite3 >/dev/null 2>&1; then
sqlite3 "$TCC_DB" "DELETE FROM access WHERE client='$IDENTIFIER' AND service IN ('kTCCServiceScreenCapture', 'kTCCServiceAccessibility');" 2>/dev/null || true
fi
fi
fi
fi
done
exit 0
EOF
chmod +x scripts/postinstall
chmod +x build_pkg_scripts/postinstall
pkgbuild \
--root "${CERTS_SOURCE}" \
--identifier "${IDENTIFIER}.certs" \
--version "${APP_VERSION}" \
--install-location "/Library/Application Support/CrossDesk/certs" \
--scripts scripts \
--scripts build_pkg_scripts \
build_pkg_temp/${APP_NAME}-certs.pkg
productbuild \
@@ -118,7 +155,7 @@ productbuild \
echo "PKG package created: ${PKG_NAME}"
rm -rf build_pkg_temp scripts ${APP_BUNDLE}
rm -rf build_pkg_temp build_pkg_scripts ${APP_BUNDLE}
echo "PKG package created successfully."
echo "package ${APP_BUNDLE}"

49
scripts/macosx/pkg_x64.sh Normal file → Executable file
View File

@@ -86,29 +86,66 @@ pkgbuild \
--component "${APP_BUNDLE}" \
build_pkg_temp/${APP_NAME}-component.pkg
mkdir -p scripts
mkdir -p build_pkg_scripts
cat > scripts/postinstall <<'EOF'
cat > build_pkg_scripts/postinstall <<'EOF'
#!/bin/bash
set -e
IDENTIFIER="cn.crossdesk.app"
# 获取当前登录用户
USER_HOME=$( /usr/bin/stat -f "%Su" /dev/console )
HOME_DIR=$( /usr/bin/dscl . -read /Users/$USER_HOME NFSHomeDirectory | awk '{print $2}' )
# 复制证书文件
DEST="$HOME_DIR/Library/Application Support/CrossDesk/certs"
mkdir -p "$DEST"
cp -R "/Library/Application Support/CrossDesk/certs/"* "$DEST/"
# 清除应用的权限授权,以便重新授权
# 使用 tccutil 重置录屏权限和辅助功能权限
if command -v tccutil >/dev/null 2>&1; then
# 重置录屏权限
tccutil reset ScreenCapture "$IDENTIFIER" 2>/dev/null || true
# 重置辅助功能权限
tccutil reset Accessibility "$IDENTIFIER" 2>/dev/null || true
# 重置摄像头权限(如果需要)
tccutil reset Camera "$IDENTIFIER" 2>/dev/null || true
# 重置麦克风权限(如果需要)
tccutil reset Microphone "$IDENTIFIER" 2>/dev/null || true
fi
# 为所有用户清除权限(可选,如果需要)
# 遍历所有用户目录并清除权限
for USER_DIR in /Users/*; do
if [ -d "$USER_DIR" ] && [ "$USER_DIR" != "/Users/Shared" ]; then
USER_NAME=$(basename "$USER_DIR")
# 跳过系统用户
if [ "$USER_NAME" != "Shared" ] && [ -d "$USER_DIR/Library" ]; then
# 删除 TCC 数据库中的相关条目(需要管理员权限)
TCC_DB="$USER_DIR/Library/Application Support/com.apple.TCC/TCC.db"
if [ -f "$TCC_DB" ]; then
# 使用 sqlite3 删除相关权限记录(如果可用)
if command -v sqlite3 >/dev/null 2>&1; then
sqlite3 "$TCC_DB" "DELETE FROM access WHERE client='$IDENTIFIER' AND service IN ('kTCCServiceScreenCapture', 'kTCCServiceAccessibility');" 2>/dev/null || true
fi
fi
fi
fi
done
exit 0
EOF
chmod +x scripts/postinstall
chmod +x build_pkg_scripts/postinstall
pkgbuild \
--root "${CERTS_SOURCE}" \
--identifier "${IDENTIFIER}.certs" \
--version "${APP_VERSION}" \
--install-location "/Library/Application Support/CrossDesk/certs" \
--scripts scripts \
--scripts build_pkg_scripts \
build_pkg_temp/${APP_NAME}-certs.pkg
productbuild \
@@ -118,7 +155,7 @@ productbuild \
echo "PKG package created: ${PKG_NAME}"
rm -rf build_pkg_temp scripts ${APP_BUNDLE}
rm -rf build_pkg_temp build_pkg_scripts ${APP_BUNDLE}
echo "PKG package created successfully."
echo "package ${APP_BUNDLE}"

View File

@@ -41,16 +41,43 @@ int ConfigCenter::Load() {
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_);
const char* signal_server_host_value =
ini_.GetValue(section_, "signal_server_host", nullptr);
if (signal_server_host_value != nullptr &&
strlen(signal_server_host_value) > 0) {
signal_server_host_ = signal_server_host_value;
} else {
signal_server_host_ = "";
}
const char* signal_server_port_value =
ini_.GetValue(section_, "signal_server_port", nullptr);
if (signal_server_port_value != nullptr &&
strlen(signal_server_port_value) > 0) {
signal_server_port_ =
static_cast<int>(ini_.GetLongValue(section_, "signal_server_port", 0));
} else {
signal_server_port_ = 0;
}
const char* coturn_server_port_value =
ini_.GetValue(section_, "coturn_server_port", nullptr);
if (coturn_server_port_value != nullptr &&
strlen(coturn_server_port_value) > 0) {
coturn_server_port_ =
static_cast<int>(ini_.GetLongValue(section_, "coturn_server_port", 0));
} else {
coturn_server_port_ = 0;
}
const char* cert_file_path_value =
ini_.GetValue(section_, "cert_file_path", nullptr);
if (cert_file_path_value != nullptr && strlen(cert_file_path_value) > 0) {
cert_file_path_ = cert_file_path_value;
} else {
cert_file_path_ = "";
}
enable_autostart_ =
ini_.GetBoolValue(section_, "enable_autostart", enable_autostart_);
enable_daemon_ = ini_.GetBoolValue(section_, "enable_daemon", enable_daemon_);
@@ -71,11 +98,18 @@ int ConfigCenter::Save() {
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_);
// only save when self hosted
if (enable_self_hosted_) {
ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
ini_.SetLongValue(section_, "signal_server_port",
static_cast<long>(signal_server_port_));
ini_.SetLongValue(section_, "coturn_server_port",
static_cast<long>(coturn_server_port_));
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
}
ini_.SetBoolValue(section_, "enable_autostart", enable_autostart_);
ini_.SetBoolValue(section_, "enable_daemon", enable_daemon_);
ini_.SetBoolValue(section_, "enable_minimize_to_tray",
@@ -210,6 +244,43 @@ int ConfigCenter::SetCertFilePath(const std::string& cert_file_path) {
int ConfigCenter::SetSelfHosted(bool enable_self_hosted) {
enable_self_hosted_ = enable_self_hosted;
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
// load from config if self hosted is enabled
if (enable_self_hosted_) {
const char* signal_server_host_value =
ini_.GetValue(section_, "signal_server_host", nullptr);
if (signal_server_host_value != nullptr &&
strlen(signal_server_host_value) > 0) {
signal_server_host_ = signal_server_host_value;
}
const char* signal_server_port_value =
ini_.GetValue(section_, "signal_server_port", nullptr);
if (signal_server_port_value != nullptr &&
strlen(signal_server_port_value) > 0) {
signal_server_port_ = static_cast<int>(
ini_.GetLongValue(section_, "signal_server_port", 0));
}
const char* coturn_server_port_value =
ini_.GetValue(section_, "coturn_server_port", nullptr);
if (coturn_server_port_value != nullptr &&
strlen(coturn_server_port_value) > 0) {
coturn_server_port_ = static_cast<int>(
ini_.GetLongValue(section_, "coturn_server_port", 0));
}
const char* cert_file_path_value =
ini_.GetValue(section_, "cert_file_path", nullptr);
if (cert_file_path_value != nullptr && strlen(cert_file_path_value) > 0) {
cert_file_path_ = cert_file_path_value;
}
ini_.SetValue(section_, "signal_server_host", signal_server_host_.c_str());
ini_.SetLongValue(section_, "signal_server_port",
static_cast<long>(signal_server_port_));
ini_.SetLongValue(section_, "coturn_server_port",
static_cast<long>(coturn_server_port_));
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;

View File

@@ -70,7 +70,6 @@ class ConfigCenter {
private:
std::string config_path_;
std::string cert_file_path_;
CSimpleIniA ini_;
const char* section_ = "Settings";
@@ -79,14 +78,15 @@ class ConfigCenter {
VIDEO_FRAME_RATE video_frame_rate_ = VIDEO_FRAME_RATE::FPS_60;
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::H264;
bool hardware_video_codec_ = false;
bool enable_turn_ = false;
bool enable_turn_ = true;
bool enable_srtp_ = false;
std::string signal_server_host_ = "api.crossdesk.cn";
std::string signal_server_host_ = "";
std::string signal_server_host_default_ = "api.crossdesk.cn";
int signal_server_port_ = 9099;
int signal_server_port_ = 0;
int server_port_default_ = 9099;
int coturn_server_port_ = 3478;
int coturn_server_port_ = 0;
int coturn_server_port_default_ = 3478;
std::string cert_file_path_ = "";
std::string cert_file_path_default_ = "";
bool enable_self_hosted_ = false;
bool enable_minimize_to_tray_ = false;

File diff suppressed because it is too large Load Diff

View File

@@ -7,71 +7,81 @@
#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
#include "render.h"
#define MENU_WINDOW_WIDTH_CN 300 * dpi_scale_
#define MENU_WINDOW_HEIGHT_CN 280 * dpi_scale_
#define LOCAL_WINDOW_WIDTH_CN 300 * dpi_scale_
#define LOCAL_WINDOW_HEIGHT_CN 280 * dpi_scale_
#define REMOTE_WINDOW_WIDTH_CN 300 * dpi_scale_
#define REMOTE_WINDOW_HEIGHT_CN 280 * dpi_scale_
#define MENU_WINDOW_WIDTH_EN 190 * dpi_scale_
#define MENU_WINDOW_HEIGHT_EN 245 * dpi_scale_
#define IPUT_WINDOW_WIDTH 160 * dpi_scale_
#define INPUT_WINDOW_PADDING_CN 66 * dpi_scale_
#define INPUT_WINDOW_PADDING_EN 96 * dpi_scale_
#define SETTINGS_WINDOW_WIDTH_CN 202 * dpi_scale_
#define SETTINGS_WINDOW_WIDTH_EN 248 * dpi_scale_
#if USE_CUDA
#if _WIN32
#define SETTINGS_WINDOW_HEIGHT_CN 405
#define SETTINGS_WINDOW_HEIGHT_EN 405
#define SETTINGS_WINDOW_HEIGHT_CN 405 * dpi_scale_
#define SETTINGS_WINDOW_HEIGHT_EN 405 * dpi_scale_
#else
#define SETTINGS_WINDOW_HEIGHT_CN 375
#define SETTINGS_WINDOW_HEIGHT_EN 375
#define SETTINGS_WINDOW_HEIGHT_CN 375 * dpi_scale_
#define SETTINGS_WINDOW_HEIGHT_EN 375 * dpi_scale_
#endif
#else
#if _WIN32
#define SETTINGS_WINDOW_HEIGHT_CN 375
#define SETTINGS_WINDOW_HEIGHT_EN 375
#define SETTINGS_WINDOW_HEIGHT_CN 375 * dpi_scale_
#define SETTINGS_WINDOW_HEIGHT_EN 375 * dpi_scale_
#else
#define SETTINGS_WINDOW_HEIGHT_CN 345
#define SETTINGS_WINDOW_HEIGHT_EN 345
#define SETTINGS_WINDOW_HEIGHT_CN 345 * dpi_scale_
#define SETTINGS_WINDOW_HEIGHT_EN 345 * dpi_scale_
#endif
#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_AUTOSTART_PADDING_CN 171
#define ENABLE_AUTOSTART_PADDING_EN 218
#define ENABLE_DAEMON_PADDING_CN 171
#define ENABLE_DAEMON_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
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228 * dpi_scale_
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275 * dpi_scale_
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN 195 * dpi_scale_
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN 195 * dpi_scale_
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 120 * dpi_scale_
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 167 * dpi_scale_
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 120 * dpi_scale_
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 167 * dpi_scale_
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN 120 * dpi_scale_
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN 167 * dpi_scale_
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 120 * dpi_scale_
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 167 * dpi_scale_
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 171 * dpi_scale_
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218 * dpi_scale_
#define ENABLE_TURN_CHECKBOX_PADDING_CN 171 * dpi_scale_
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218 * dpi_scale_
#define ENABLE_SRTP_CHECKBOX_PADDING_CN 171 * dpi_scale_
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218 * dpi_scale_
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171 * dpi_scale_
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218 * dpi_scale_
#define ENABLE_AUTOSTART_PADDING_CN 171 * dpi_scale_
#define ENABLE_AUTOSTART_PADDING_EN 218 * dpi_scale_
#define ENABLE_DAEMON_PADDING_CN 171 * dpi_scale_
#define ENABLE_DAEMON_PADDING_EN 218 * dpi_scale_
#define ENABLE_MINIZE_TO_TRAY_PADDING_CN 171 * dpi_scale_
#define ENABLE_MINIZE_TO_TRAY_PADDING_EN 218 * dpi_scale_
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN 90 * dpi_scale_
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137 * dpi_scale_
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN 90 * dpi_scale_
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN 137 * dpi_scale_
#define SETTINGS_SELECT_WINDOW_WIDTH 73 * dpi_scale_
#define SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH 130 * dpi_scale_
#define SETTINGS_OK_BUTTON_PADDING_CN 65 * dpi_scale_
#define SETTINGS_OK_BUTTON_PADDING_EN 83 * dpi_scale_
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN 78 * dpi_scale_
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN 91 * dpi_scale_
#define UPDATE_NOTIFICATION_OK_BUTTON_PADDING_CN 162 * dpi_scale_
#define UPDATE_NOTIFICATION_OK_BUTTON_PADDING_EN 146 * dpi_scale_
#define UPDATE_NOTIFICATION_RESERVED_HEIGHT 120 * dpi_scale_
#define REQUEST_PERMISSION_WINDOW_WIDTH_CN 130 * dpi_scale_
#define REQUEST_PERMISSION_WINDOW_HEIGHT_CN 125 * dpi_scale_
#define REQUEST_PERMISSION_WINDOW_WIDTH_EN 260 * dpi_scale_
#define REQUEST_PERMISSION_WINDOW_HEIGHT_EN 125 * dpi_scale_
#define REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_CN 90 * dpi_scale_
#define REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_EN 210 * dpi_scale_
#endif

View File

@@ -0,0 +1,91 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-06-14
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LAYOUT_STYLE_H_
#define _LAYOUT_STYLE_H_
#include "render.h"
#define TITLE_BAR_HEIGHT 0.0625f
#define TITLE_BAR_BUTTON_WIDTH 0.0625f
#define TITLE_BAR_BUTTON_HEIGHT 0.0625f
#define STATUS_BAR_HEIGHT 0.05f
#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 USE_CUDA
#if _WIN32
#define SETTINGS_WINDOW_HEIGHT_CN 405
#define SETTINGS_WINDOW_HEIGHT_EN 405
#else
#define SETTINGS_WINDOW_HEIGHT_CN 375
#define SETTINGS_WINDOW_HEIGHT_EN 375
#endif
#else
#if _WIN32
#define SETTINGS_WINDOW_HEIGHT_CN 375
#define SETTINGS_WINDOW_HEIGHT_EN 375
#else
#define SETTINGS_WINDOW_HEIGHT_CN 345
#define SETTINGS_WINDOW_HEIGHT_EN 345
#endif
#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_AUTOSTART_PADDING_CN 171
#define ENABLE_AUTOSTART_PADDING_EN 218
#define ENABLE_DAEMON_PADDING_CN 171
#define ENABLE_DAEMON_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
#define UPDATE_NOTIFICATION_OK_BUTTON_PADDING_CN 162
#define UPDATE_NOTIFICATION_OK_BUTTON_PADDING_EN 146
#define UPDATE_NOTIFICATION_RESERVED_HEIGHT 120
#define REQUEST_PERMISSION_WINDOW_WIDTH_CN 130
#define REQUEST_PERMISSION_WINDOW_HEIGHT_CN 125
#define REQUEST_PERMISSION_WINDOW_WIDTH_EN 260
#define REQUEST_PERMISSION_WINDOW_HEIGHT_EN 125
#define REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_CN 90
#define REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_EN 210
#endif

View File

@@ -158,12 +158,18 @@ static std::vector<std::string> no_such_id = {
static std::vector<std::string> about = {
reinterpret_cast<const char*>(u8"关于"), "About"};
static std::vector<std::string> notification = {
reinterpret_cast<const char*>(u8"通知"), "Notification"};
static std::vector<std::string> new_version_available = {
reinterpret_cast<const char*>(u8"新版本可用"), "New Version Available"};
static std::vector<std::string> version = {
reinterpret_cast<const char*>(u8"版本"), "Version"};
static std::vector<std::string> release_date = {
reinterpret_cast<const char*>(u8"发布日期: "), "Release Date: "};
static std::vector<std::string> access_website = {
reinterpret_cast<const char*>(u8"访问官网: "), "Access Website: "};
static std::vector<std::string> update = {
reinterpret_cast<const char*>(u8"更新"), "Update"};
static std::vector<std::string> confirm_delete_connection = {
reinterpret_cast<const char*>(u8"确认删除此连接"),
@@ -182,6 +188,19 @@ static std::vector<std::string> minimize_to_tray = {
"Minimize to system tray when exit:"};
static std::vector<LPCWSTR> exit_program = {L"退出", L"Exit"};
#endif
#ifdef __APPLE__
static std::vector<std::string> request_permissions = {
reinterpret_cast<const char*>(u8"权限请求"), "Request Permissions"};
static std::vector<std::string> screen_recording_permission = {
reinterpret_cast<const char*>(u8"屏幕录制权限"),
"Screen Recording Permission"};
static std::vector<std::string> accessibility_permission = {
reinterpret_cast<const char*>(u8"辅助功能权限"),
"Accessibility Permission"};
static std::vector<std::string> permission_required_message = {
reinterpret_cast<const char*>(u8"该应用需要授权以下权限:"),
"The application requires the following permissions:"};
#endif
} // namespace localization
} // namespace crossdesk
#endif

View File

@@ -1,6 +1,6 @@
#include <random>
#include "layout.h"
#include "layout_relative.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
@@ -8,20 +8,27 @@
namespace crossdesk {
int Render::LocalWindow() {
ImGui::SetNextWindowPos(ImVec2(-1.0f, title_bar_height_), ImGuiCond_Always);
ImGuiIO& io = ImGui::GetIO();
float local_window_width = io.DisplaySize.x * 0.5f;
float local_window_height =
io.DisplaySize.y * (1 - TITLE_BAR_HEIGHT - STATUS_BAR_HEIGHT);
float local_window_button_width = io.DisplaySize.x * 0.046f;
float local_window_button_height = io.DisplaySize.y * 0.075f;
ImGui::SetNextWindowPos(ImVec2(0.0f, io.DisplaySize.y * TITLE_BAR_HEIGHT),
ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
ImGui::BeginChild("LocalDesktopWindow",
ImVec2(local_window_width_, local_window_height_),
ImVec2(local_window_width, local_window_height),
ImGuiChildFlags_None,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + main_window_text_y_padding_);
ImGui::Indent(main_child_window_x_padding_);
ImGui::SetCursorPos(
ImVec2(io.DisplaySize.x * 0.045f, io.DisplaySize.y * 0.02f));
ImGui::TextColored(
ImVec4(0.0f, 0.0f, 0.0f, 0.5f), "%s",
@@ -30,18 +37,16 @@ int Render::LocalWindow() {
ImGui::Spacing();
{
ImGui::SetNextWindowPos(
ImVec2(main_child_window_x_padding_,
title_bar_height_ + main_child_window_y_padding_),
ImVec2(io.DisplaySize.x * 0.045f, io.DisplaySize.y * 0.15f),
ImGuiCond_Always);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(239.0f / 255, 240.0f / 255,
242.0f / 255, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::BeginChild(
"LocalDesktopWindow_1",
ImVec2(local_child_window_width_, local_child_window_height_),
"LocalDesktopPanel",
ImVec2(local_window_width * 0.8f, local_window_height * 0.43f),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
@@ -52,7 +57,7 @@ int Render::LocalWindow() {
ImGui::Spacing();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
ImGui::SetNextItemWidth(io.DisplaySize.x * 0.25f);
ImGui::SetWindowFontScale(1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
@@ -76,7 +81,8 @@ int Render::LocalWindow() {
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(ICON_FA_COPY, ImVec2(22, 38))) {
if (ImGui::Button(ICON_FA_COPY, ImVec2(local_window_button_width,
local_window_button_height))) {
local_id_copied_ = true;
ImGui::SetClipboardText(client_id_);
copy_start_time_ = ImGui::GetTime();
@@ -86,17 +92,10 @@ int Render::LocalWindow() {
double time_duration = ImGui::GetTime() - copy_start_time_;
if (local_id_copied_ && time_duration < 1.0f) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
notification_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
notification_window_height_) /
2));
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
ImGui::SetNextWindowSize(
ImVec2(notification_window_width_, notification_window_height_));
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
ImGui::PushStyleColor(
ImGuiCol_WindowBg,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f - (float)time_duration));
@@ -115,7 +114,7 @@ int Render::LocalWindow() {
[localization_language_index_];
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.5f);
ImGui::SetCursorPosY(window_height * 0.4f);
ImGui::PushStyleColor(ImGuiCol_Text,
ImVec4(0, 0, 0, 1.0f - (float)time_duration));
ImGui::Text("%s", text.c_str());
@@ -134,7 +133,7 @@ int Render::LocalWindow() {
localization::password[localization_language_index_].c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
ImGui::SetNextItemWidth(io.DisplaySize.x * 0.25f);
ImGui::Spacing();
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
@@ -156,51 +155,34 @@ int Render::LocalWindow() {
ImGui::SetWindowFontScale(0.5f);
auto l_x = ImGui::GetCursorScreenPos().x;
auto l_y = ImGui::GetCursorScreenPos().y;
if (ImGui::Button(ICON_FA_EYE, ImVec2(22, 38))) {
if (ImGui::Button(
show_password_ ? ICON_FA_EYE : ICON_FA_EYE_SLASH,
ImVec2(local_window_button_width, local_window_button_height))) {
show_password_ = !show_password_;
}
if (!show_password_) {
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 12.5f),
ImVec2(l_x + 20.3f, l_y + 26.5f),
IM_COL32(239, 240, 242, 255), 2.0f);
draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 11.0f),
ImVec2(l_x + 20.3f, l_y + 25.0f),
IM_COL32(0, 0, 0, 255), 1.5f);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_PEN, ImVec2(22, 38))) {
if (ImGui::Button(ICON_FA_PEN, ImVec2(local_window_button_width,
local_window_button_height))) {
show_reset_password_window_ = true;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor(3);
if (show_reset_password_window_) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
connection_status_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
connection_status_window_height_) /
2));
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
connection_status_window_height_));
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
ImGui::SetNextWindowSize(
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::Begin("ResetPasswordWindow", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
@@ -215,9 +197,9 @@ int Render::LocalWindow() {
ImGui::SetCursorPosY(window_height * 0.2f);
ImGui::Text("%s", text.c_str());
ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f);
ImGui::SetCursorPosX(window_width * 0.33f);
ImGui::SetCursorPosY(window_height * 0.4f);
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
ImGui::SetNextItemWidth(window_width * 0.33f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);

View File

@@ -1,3 +1,4 @@
#include "layout_relative.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
@@ -5,25 +6,25 @@
namespace crossdesk {
int Render::RecentConnectionsWindow() {
ImGui::SetNextWindowPos(
ImVec2(0, title_bar_height_ + local_window_height_ - 1.0f),
ImGuiCond_Always);
ImGuiIO& io = ImGui::GetIO();
float recent_connection_window_width = io.DisplaySize.x;
float recent_connection_window_height =
io.DisplaySize.y * (0.46f - STATUS_BAR_HEIGHT);
ImGui::SetNextWindowPos(ImVec2(0, io.DisplaySize.y * 0.55f),
ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::BeginChild(
"RecentConnectionsWindow",
ImVec2(main_window_width_default_,
main_window_height_default_ - title_bar_height_ -
local_window_height_ - status_bar_height_ + 1.0f),
ImVec2(recent_connection_window_width, recent_connection_window_height),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + main_window_text_y_padding_);
ImGui::Indent(main_child_window_x_padding_);
ImGui::SetCursorPos(
ImVec2(io.DisplaySize.x * 0.045f, io.DisplaySize.y * 0.02f));
ImGui::TextColored(
ImVec4(0.0f, 0.0f, 0.0f, 0.5f), "%s",
@@ -37,31 +38,40 @@ int Render::RecentConnectionsWindow() {
}
int Render::ShowRecentConnections() {
ImGui::SetCursorPosX(25.0f);
ImVec2 sub_window_pos = ImGui::GetCursorPos();
std::map<std::string, ImVec2> sub_containers_pos;
ImGuiIO& io = ImGui::GetIO();
float recent_connection_panel_width = io.DisplaySize.x * 0.912f;
float recent_connection_panel_height = io.DisplaySize.y * 0.29f;
float recent_connection_image_height = recent_connection_panel_height * 0.6f;
float recent_connection_image_width = recent_connection_image_height * 16 / 9;
float recent_connection_sub_container_width =
recent_connection_image_width_ + 16.0f;
recent_connection_image_width * 1.2f;
float recent_connection_sub_container_height =
recent_connection_image_height_ + 36.0f;
recent_connection_image_height * 1.4f;
float recent_connection_button_width = recent_connection_image_width * 0.15f;
float recent_connection_button_height =
recent_connection_image_height * 0.25f;
float recent_connection_dummy_button_width =
recent_connection_image_width - 2 * recent_connection_button_width;
ImGui::SetCursorPos(
ImVec2(io.DisplaySize.x * 0.045f, io.DisplaySize.y * 0.1f));
std::map<std::string, ImVec2> sub_containers_pos;
ImGui::PushStyleColor(ImGuiCol_ChildBg,
ImVec4(239.0f / 255, 240.0f / 255, 242.0f / 255, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::BeginChild("RecentConnectionsContainer",
ImVec2(main_window_width_default_ - 50.0f, 145.0f),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_AlwaysHorizontalScrollbar |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse);
ImGui::BeginChild(
"RecentConnectionsContainer",
ImVec2(recent_connection_panel_width, recent_connection_panel_height),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_AlwaysHorizontalScrollbar |
ImGuiWindowFlags_NoScrollWithMouse);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
size_t recent_connections_count = recent_connections_.size();
int count = 0;
float button_width = 22;
float button_height = 22;
for (auto& it : recent_connections_) {
sub_containers_pos[it.first] = ImGui::GetCursorPos();
std::string recent_connection_sub_window_name =
@@ -71,11 +81,8 @@ int Render::ShowRecentConnections() {
ImVec2(recent_connection_sub_container_width,
recent_connection_sub_container_height),
ImGuiChildFlags_None,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoScrollbar);
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
std::string connection_info = it.first;
// remote id length is 9
@@ -114,14 +121,16 @@ int Render::ShowRecentConnections() {
it.second.remote_host_name = "unknown";
}
ImVec2 image_screen_pos = ImVec2(ImGui::GetCursorScreenPos().x + 5.0f,
ImGui::GetCursorScreenPos().y + 5.0f);
ImVec2 image_screen_pos = ImVec2(
ImGui::GetCursorScreenPos().x + recent_connection_image_width * 0.04f,
ImGui::GetCursorScreenPos().y + recent_connection_image_height * 0.08f);
ImVec2 image_pos =
ImVec2(ImGui::GetCursorPosX() + 5.0f, ImGui::GetCursorPosY() + 5.0f);
ImVec2(ImGui::GetCursorPosX() + recent_connection_image_width * 0.05f,
ImGui::GetCursorPosY() + recent_connection_image_height * 0.08f);
ImGui::SetCursorPos(image_pos);
ImGui::Image((ImTextureID)(intptr_t)it.second.texture,
ImVec2((float)recent_connection_image_width_,
(float)recent_connection_image_height_));
ImGui::Image(
(ImTextureID)(intptr_t)it.second.texture,
ImVec2(recent_connection_image_width, recent_connection_image_height));
// remote id display button
{
@@ -130,16 +139,17 @@ int Render::ShowRecentConnections() {
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0.2f));
ImVec2 dummy_button_pos =
ImVec2(image_pos.x, image_pos.y + recent_connection_image_height_);
ImVec2(image_pos.x, image_pos.y + recent_connection_image_height);
std::string dummy_button_name = "##DummyButton" + it.second.remote_id;
ImGui::SetCursorPos(dummy_button_pos);
ImGui::SetWindowFontScale(0.6f);
ImGui::Button(dummy_button_name.c_str(),
ImVec2(recent_connection_image_width_ - 2 * button_width,
button_height));
ImVec2(recent_connection_dummy_button_width,
recent_connection_button_height));
ImGui::SetWindowFontScale(1.0f);
ImGui::SetCursorPos(
ImVec2(dummy_button_pos.x + 2.0f, dummy_button_pos.y + 1.0f));
ImGui::SetCursorPos(ImVec2(
dummy_button_pos.x + recent_connection_dummy_button_width * 0.05f,
dummy_button_pos.y + recent_connection_button_height * 0.05f));
ImGui::SetWindowFontScale(0.65f);
ImGui::Text("%s", it.second.remote_id.c_str());
ImGui::SetWindowFontScale(1.0f);
@@ -162,16 +172,18 @@ int Render::ShowRecentConnections() {
ImGui::SetWindowFontScale(0.5f);
// trash button
{
ImVec2 trash_can_button_pos = ImVec2(
image_pos.x + recent_connection_image_width_ - 2 * button_width,
image_pos.y + recent_connection_image_height_);
ImVec2 trash_can_button_pos =
ImVec2(image_pos.x + recent_connection_image_width -
2 * recent_connection_button_width,
image_pos.y + recent_connection_image_height);
ImGui::SetCursorPos(trash_can_button_pos);
std::string trash_can = ICON_FA_TRASH_CAN;
std::string recent_connection_delete_button_name =
trash_can + "##RecentConnectionDelete" +
std::to_string(trash_can_button_pos.x);
if (ImGui::Button(recent_connection_delete_button_name.c_str(),
ImVec2(button_width, button_height))) {
ImVec2(recent_connection_button_width,
recent_connection_button_height))) {
show_confirm_delete_connection_ = true;
delete_connection_name_ = it.first;
}
@@ -187,14 +199,16 @@ int Render::ShowRecentConnections() {
// connect button
{
ImVec2 connect_button_pos =
ImVec2(image_pos.x + recent_connection_image_width_ - button_width,
image_pos.y + recent_connection_image_height_);
ImVec2(image_pos.x + recent_connection_image_width -
recent_connection_button_width,
image_pos.y + recent_connection_image_height);
ImGui::SetCursorPos(connect_button_pos);
std::string connect = ICON_FA_ARROW_RIGHT_LONG;
std::string connect_to_this_connection_button_name =
connect + "##ConnectionTo" + it.first;
if (ImGui::Button(connect_to_this_connection_button_name.c_str(),
ImVec2(button_width, button_height))) {
ImVec2(recent_connection_button_width,
recent_connection_button_height))) {
ConnectTo(it.second.remote_id, it.second.password.c_str(),
it.second.remember_password);
}
@@ -206,17 +220,20 @@ int Render::ShowRecentConnections() {
if (count != recent_connections_count - 1) {
ImVec2 line_start =
ImVec2(image_screen_pos.x + recent_connection_image_width_ + 20.0f,
ImVec2(image_screen_pos.x + recent_connection_image_width * 1.19f,
image_screen_pos.y);
ImVec2 line_end = ImVec2(
image_screen_pos.x + recent_connection_image_width_ + 20.0f,
image_screen_pos.y + recent_connection_image_height_ + button_height);
ImVec2 line_end =
ImVec2(image_screen_pos.x + recent_connection_image_width * 1.19f,
image_screen_pos.y + recent_connection_image_height +
recent_connection_button_height);
ImGui::GetWindowDrawList()->AddLine(line_start, line_end,
IM_COL32(0, 0, 0, 122), 1.0f);
}
count++;
ImGui::SameLine(0, count != recent_connections_count ? 26.0f : 0.0f);
ImGui::SameLine(0, count != recent_connections_count
? (recent_connection_image_width * 0.165f)
: 0.0f);
}
ImGui::EndChild();
@@ -229,32 +246,30 @@ int Render::ShowRecentConnections() {
}
int Render::ConfirmDeleteConnection() {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
connection_status_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
connection_status_window_height_) /
2));
ImGuiIO& io = ImGui::GetIO();
ImGui::SetNextWindowPos(
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
ImGui::SetNextWindowSize(
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
connection_status_window_height_));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::Begin("ConfirmDeleteConnectionWindow", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
auto connection_status_window_width = ImGui::GetWindowSize().x;
auto connection_status_window_height = ImGui::GetWindowSize().y;
std::string text =
localization::confirm_delete_connection[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 6 / 19);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
ImGui::SetCursorPosX(connection_status_window_width * 0.33f);
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
// ok
ImGui::SetWindowFontScale(0.5f);
@@ -273,12 +288,9 @@ int Render::ConfirmDeleteConnection() {
show_confirm_delete_connection_ = false;
}
auto window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.2f);
ImGui::SetCursorPosX((connection_status_window_width - text_width) * 0.5f);
ImGui::SetCursorPosY(connection_status_window_height * 0.2f);
ImGui::Text("%s", text.c_str());
ImGui::SetWindowFontScale(1.0f);

View File

@@ -1,4 +1,4 @@
#include "layout.h"
#include "layout_relative.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
@@ -8,21 +8,28 @@ namespace crossdesk {
static int InputTextCallback(ImGuiInputTextCallbackData* data);
int Render::RemoteWindow() {
ImGui::SetNextWindowPos(ImVec2(local_window_width_ + 1.0f, title_bar_height_),
ImGuiCond_Always);
ImGuiIO& io = ImGui::GetIO();
float remote_window_width = io.DisplaySize.x * 0.5f;
float remote_window_height =
io.DisplaySize.y * (1 - TITLE_BAR_HEIGHT - STATUS_BAR_HEIGHT);
float remote_window_arrow_button_width = io.DisplaySize.x * 0.1f;
float remote_window_arrow_button_height = io.DisplaySize.y * 0.078f;
ImGui::SetNextWindowPos(
ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * TITLE_BAR_HEIGHT),
ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
ImGui::BeginChild("RemoteDesktopWindow",
ImVec2(remote_window_width_, remote_window_height_),
ImVec2(remote_window_width, remote_window_height),
ImGuiChildFlags_None,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + main_window_text_y_padding_);
ImGui::Indent(main_child_window_x_padding_ - 1.0f);
ImGui::SetCursorPos(
ImVec2(io.DisplaySize.x * 0.057f, io.DisplaySize.y * 0.02f));
ImGui::TextColored(
ImVec4(0.0f, 0.0f, 0.0f, 0.5f), "%s",
@@ -31,8 +38,7 @@ int Render::RemoteWindow() {
ImGui::Spacing();
{
ImGui::SetNextWindowPos(
ImVec2(local_window_width_ + main_child_window_x_padding_ - 1.0f,
title_bar_height_ + main_child_window_y_padding_),
ImVec2(io.DisplaySize.x * 0.557f, io.DisplaySize.y * 0.15f),
ImGuiCond_Always);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(239.0f / 255, 240.0f / 255,
242.0f / 255, 1.0f));
@@ -40,10 +46,9 @@ int Render::RemoteWindow() {
ImGui::BeginChild(
"RemoteDesktopWindow_1",
ImVec2(remote_child_window_width_, remote_child_window_height_),
ImVec2(remote_window_width * 0.8f, remote_window_height * 0.43f),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
@@ -53,7 +58,7 @@ int Render::RemoteWindow() {
"%s", localization::remote_id[localization_language_index_].c_str());
ImGui::Spacing();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
ImGui::SetNextItemWidth(io.DisplaySize.x * 0.25f);
ImGui::SetWindowFontScale(1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
if (re_enter_remote_id_) {
@@ -75,38 +80,65 @@ int Render::RemoteWindow() {
remote_id.erase(remove_if(remote_id.begin(), remote_id.end(),
static_cast<int (*)(int)>(&isspace)),
remote_id.end());
if (ImGui::Button(ICON_FA_ARROW_RIGHT_LONG, ImVec2(55, 38)) ||
if (ImGui::Button(ICON_FA_ARROW_RIGHT_LONG,
ImVec2(remote_window_arrow_button_width,
remote_window_arrow_button_height)) ||
enter_pressed) {
connect_button_pressed_ = true;
bool found = false;
std::string target_remote_id;
std::string target_password;
bool should_connect = false;
bool already_connected = false;
for (auto& [id, props] : recent_connections_) {
if (id.find(remote_id) != std::string::npos) {
found = true;
if (client_properties_.find(remote_id) !=
client_properties_.end()) {
if (!client_properties_[remote_id]->connection_established_) {
ConnectTo(props.remote_id, props.password.c_str(), false);
target_remote_id = props.remote_id;
target_password = props.password;
{
// std::shared_lock lock(client_properties_mutex_);
if (client_properties_.find(remote_id) !=
client_properties_.end()) {
if (!client_properties_[remote_id]->connection_established_) {
should_connect = true;
} else {
already_connected = true;
}
} else {
// todo: show warning message
LOG_INFO("Already connected to [{}]", remote_id);
should_connect = true;
}
} else {
ConnectTo(props.remote_id, props.password.c_str(), false);
}
if (should_connect) {
ConnectTo(target_remote_id, target_password.c_str(), false);
} else if (already_connected) {
LOG_INFO("Already connected to [{}]", remote_id);
}
break;
}
}
if (!found) {
ConnectTo(remote_id, "", false);
}
}
// check every 1 second for rejoin
if (need_to_rejoin_) {
need_to_rejoin_ = false;
for (const auto& [_, props] : client_properties_) {
if (props->rejoin_) {
ConnectTo(props->remote_id_, props->remote_password_,
props->remember_password_);
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_rejoin_check_time_)
.count();
if (elapsed >= 1000) {
last_rejoin_check_time_ = now;
need_to_rejoin_ = false;
// std::shared_lock lock(client_properties_mutex_);
for (const auto& [_, props] : client_properties_) {
if (props->rejoin_) {
ConnectTo(props->remote_id_, props->remote_password_,
props->remember_password_);
}
}
}
}
@@ -136,33 +168,61 @@ int Render::ConnectTo(const std::string& remote_id, const char* password,
LOG_INFO("Connect to [{}]", remote_id);
focused_remote_id_ = remote_id;
if (client_properties_.find(remote_id) == client_properties_.end()) {
client_properties_[remote_id] =
std::make_shared<SubStreamWindowProperties>();
auto props = client_properties_[remote_id];
props->local_id_ = "C-" + std::string(client_id_);
props->remote_id_ = remote_id;
memcpy(&props->params_, &params_, sizeof(Params));
props->params_.user_id = props->local_id_.c_str();
props->peer_ = CreatePeer(&props->params_);
// std::shared_lock shared_lock(client_properties_mutex_);
bool exists =
(client_properties_.find(remote_id) != client_properties_.end());
// shared_lock.unlock();
for (auto& display_info : display_info_list_) {
AddVideoStream(peer_, display_info.name.c_str());
}
AddAudioStream(props->peer_, props->audio_label_.c_str());
AddDataStream(props->peer_, props->data_label_.c_str());
if (!exists) {
PeerPtr* peer_to_init = nullptr;
std::string local_id;
if (props->peer_) {
LOG_INFO("[{}] Create peer instance successful", props->local_id_);
Init(props->peer_);
LOG_INFO("[{}] Peer init finish", props->local_id_);
} else {
LOG_INFO("Create peer [{}] instance failed", props->local_id_);
{
// std::unique_lock unique_lock(client_properties_mutex_);
if (client_properties_.find(remote_id) == client_properties_.end()) {
client_properties_[remote_id] =
std::make_shared<SubStreamWindowProperties>();
auto props = client_properties_[remote_id];
props->local_id_ = "C-" + std::string(client_id_);
props->remote_id_ = remote_id;
memcpy(&props->params_, &params_, sizeof(Params));
props->params_.user_id = props->local_id_.c_str();
props->peer_ = CreatePeer(&props->params_);
props->control_window_width_ = title_bar_height_ * 8.0f;
props->control_window_height_ = title_bar_height_ * 1.3f;
props->control_window_min_width_ = title_bar_height_ * 0.65f;
props->control_window_min_height_ = title_bar_height_ * 1.3f;
props->control_window_max_width_ = title_bar_height_ * 8.0f;
props->control_window_max_height_ = title_bar_height_ * 6.0f;
if (!props->peer_) {
LOG_INFO("Create peer [{}] instance failed", props->local_id_);
return -1;
}
for (auto& display_info : display_info_list_) {
AddVideoStream(props->peer_, display_info.name.c_str());
}
AddAudioStream(props->peer_, props->audio_label_.c_str());
AddDataStream(props->peer_, props->data_label_.c_str());
props->connection_status_ = ConnectionStatus::Connecting;
peer_to_init = props->peer_;
local_id = props->local_id_;
}
}
props->connection_status_ = ConnectionStatus::Connecting;
if (peer_to_init) {
LOG_INFO("[{}] Create peer instance successful", local_id);
Init(peer_to_init);
LOG_INFO("[{}] Peer init finish", local_id);
}
}
int ret = -1;
// std::shared_lock read_lock(client_properties_mutex_);
auto props = client_properties_[remote_id];
if (!props->connection_established_) {
props->remember_password_ = remember_password;
@@ -174,14 +234,17 @@ int Render::ConnectTo(const std::string& remote_id, const char* password,
}
std::string remote_id_with_pwd = remote_id + "@" + password;
ret = JoinConnection(props->peer_, remote_id_with_pwd.c_str());
if (0 == ret) {
props->rejoin_ = false;
} else {
props->rejoin_ = true;
need_to_rejoin_ = true;
if (props->peer_) {
ret = JoinConnection(props->peer_, remote_id_with_pwd.c_str());
if (0 == ret) {
props->rejoin_ = false;
} else {
props->rejoin_ = true;
need_to_rejoin_ = true;
}
}
}
// read_lock.unlock();
return 0;
}

View File

@@ -6,12 +6,13 @@
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include "OPPOSans_Regular.h"
#include "device_controller_factory.h"
#include "fa_regular_400.h"
#include "fa_solid_900.h"
#include "layout.h"
#include "layout_relative.h"
#include "localization.h"
#include "platform.h"
#include "rd_log.h"
@@ -20,8 +21,6 @@
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
#define MOUSE_GRAB_PADDING 5
namespace crossdesk {
std::vector<char> Render::SerializeRemoteAction(const RemoteAction& action) {
@@ -133,9 +132,39 @@ SDL_HitTestResult Render::HitTestCallback(SDL_Window* window,
int window_width, window_height;
SDL_GetWindowSize(window, &window_width, &window_height);
if (area->y < 30 && area->y > MOUSE_GRAB_PADDING &&
area->x < window_width - 120 && area->x > MOUSE_GRAB_PADDING &&
!render->is_tab_bar_hovered_) {
// check if curosor is in tab bar
if (render->stream_window_inited_ && render->stream_window_created_ &&
!render->fullscreen_button_pressed_ && render->stream_ctx_) {
ImGuiContext* prev_ctx = ImGui::GetCurrentContext();
ImGui::SetCurrentContext(render->stream_ctx_);
ImGuiWindow* tab_bar_window = ImGui::FindWindowByName("TabBar");
if (tab_bar_window && tab_bar_window->Active) {
ImGuiIO& io = ImGui::GetIO();
float scale_x = io.DisplayFramebufferScale.x;
float scale_y = io.DisplayFramebufferScale.y;
float tab_bar_x = tab_bar_window->Pos.x * scale_x;
float tab_bar_y = tab_bar_window->Pos.y * scale_y;
float tab_bar_width = tab_bar_window->Size.x * scale_x;
float tab_bar_height = tab_bar_window->Size.y * scale_y;
ImGui::SetCurrentContext(prev_ctx);
if (area->x >= tab_bar_x && area->x <= tab_bar_x + tab_bar_width &&
area->y >= tab_bar_y && area->y <= tab_bar_y + tab_bar_height) {
return SDL_HITTEST_NORMAL;
}
} else {
ImGui::SetCurrentContext(prev_ctx);
}
}
float mouse_grab_padding = render->title_bar_button_width_ * 0.16f;
if (area->y < render->title_bar_button_width_ &&
area->y > mouse_grab_padding &&
area->x < window_width - render->title_bar_button_width_ * 4.0f &&
area->x > mouse_grab_padding) {
return SDL_HITTEST_DRAGGABLE;
}
@@ -143,32 +172,32 @@ SDL_HitTestResult Render::HitTestCallback(SDL_Window* window,
// return SDL_HITTEST_NORMAL;
// }
if (area->y < MOUSE_GRAB_PADDING) {
if (area->x < MOUSE_GRAB_PADDING) {
if (area->y < mouse_grab_padding) {
if (area->x < mouse_grab_padding) {
return SDL_HITTEST_RESIZE_TOPLEFT;
} else if (area->x > window_width - MOUSE_GRAB_PADDING) {
} else if (area->x > window_width - mouse_grab_padding) {
return SDL_HITTEST_RESIZE_TOPRIGHT;
} else {
return SDL_HITTEST_RESIZE_TOP;
}
} else if (area->y > window_height - MOUSE_GRAB_PADDING) {
if (area->x < MOUSE_GRAB_PADDING) {
} else if (area->y > window_height - mouse_grab_padding) {
if (area->x < mouse_grab_padding) {
return SDL_HITTEST_RESIZE_BOTTOMLEFT;
} else if (area->x > window_width - MOUSE_GRAB_PADDING) {
} else if (area->x > window_width - mouse_grab_padding) {
return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
} else {
return SDL_HITTEST_RESIZE_BOTTOM;
}
} else if (area->x < MOUSE_GRAB_PADDING) {
} else if (area->x < mouse_grab_padding) {
return SDL_HITTEST_RESIZE_LEFT;
} else if (area->x > window_width - MOUSE_GRAB_PADDING) {
} else if (area->x > window_width - mouse_grab_padding) {
return SDL_HITTEST_RESIZE_RIGHT;
}
return SDL_HITTEST_NORMAL;
}
Render::Render() {}
Render::Render() : last_rejoin_check_time_(std::chrono::steady_clock::now()) {}
Render::~Render() {}
@@ -207,7 +236,7 @@ int Render::LoadSettingsFromCacheFile() {
memset(aes128_iv_, 0, sizeof(aes128_iv_));
thumbnail_.reset();
thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/");
thumbnail_ = std::make_shared<Thumbnail>(cache_path_ + "/thumbnails/");
thumbnail_->GetKeyAndIv(aes128_key_, aes128_iv_);
thumbnail_->DeleteAllFilesInDirectory();
@@ -248,7 +277,7 @@ int Render::LoadSettingsFromCacheFile() {
memcpy(aes128_iv_, cd_cache_.iv, sizeof(cd_cache_.iv));
thumbnail_.reset();
thumbnail_ = std::make_unique<Thumbnail>(cache_path_ + "/thumbnails/",
thumbnail_ = std::make_shared<Thumbnail>(cache_path_ + "/thumbnails/",
aes128_key_, aes128_iv_);
language_button_value_ = (int)config_center_->GetLanguage();
@@ -365,13 +394,16 @@ int Render::StartSpeakerCapturer() {
if (speaker_capturer_) {
speaker_capturer_->Start();
start_speaker_capturer_ = true;
}
return 0;
}
int Render::StopSpeakerCapturer() {
if (speaker_capturer_) {
speaker_capturer_->Stop();
start_speaker_capturer_ = false;
}
return 0;
@@ -459,14 +491,22 @@ int Render::CreateConnectionPeer() {
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';
int signal_port = config_center_->GetSignalServerPort();
if (signal_port > 0) {
strncpy(signal_server_port_self_, std::to_string(signal_port).c_str(),
sizeof(signal_server_port_self_) - 1);
signal_server_port_self_[sizeof(signal_server_port_self_) - 1] = '\0';
} else {
signal_server_port_self_[0] = '\0';
}
int coturn_port = config_center_->GetCoturnServerPort();
if (coturn_port > 0) {
strncpy(coturn_server_port_self_, std::to_string(coturn_port).c_str(),
sizeof(coturn_server_port_self_) - 1);
coturn_server_port_self_[sizeof(coturn_server_port_self_) - 1] = '\0';
} else {
coturn_server_port_self_[0] = '\0';
}
tls_cert_path_self_ = config_center_->GetCertFilePath();
// peer config
@@ -612,21 +652,38 @@ int Render::CreateMainWindow() {
ImGui::SetCurrentContext(main_ctx_);
if (!SDL_CreateWindowAndRenderer(
"Remote Desk", (int)main_window_width_default_,
(int)main_window_height_default_,
"Remote Desk", (int)main_window_width_, (int)main_window_height_,
SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_BORDERLESS |
SDL_WINDOW_HIDDEN,
&main_window_, &main_renderer_)) {
LOG_ERROR("Error creating main_window_ and main_renderer_: {}",
SDL_GetError());
LOG_ERROR("Error creating MainWindow and MainRenderer: {}", SDL_GetError());
return -1;
}
float dpi_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
if (std::abs(dpi_scale_ - dpi_scale) > 0.01f) {
dpi_scale_ = dpi_scale;
main_window_width_ = (int)(main_window_width_ * dpi_scale_);
main_window_height_ = (int)(main_window_height_ * dpi_scale_);
stream_window_width_ = (int)(stream_window_width_ * dpi_scale_);
stream_window_height_ = (int)(stream_window_height_ * dpi_scale_);
SDL_SetWindowSize(main_window_, (int)main_window_width_,
(int)main_window_height_);
}
SDL_SetWindowResizable(main_window_, false);
// for window region action
SDL_SetWindowHitTest(main_window_, HitTestCallback, this);
SetupFontAndStyle(true);
ImGuiStyle& style = ImGui::GetStyle();
style.ScaleAllSizes(dpi_scale_);
style.FontScaleDpi = dpi_scale_;
#if _WIN32
SDL_PropertiesID props = SDL_GetWindowProperties(main_window_);
HWND main_hwnd = (HWND)SDL_GetPointerProperty(
@@ -638,6 +695,9 @@ int Render::CreateMainWindow() {
localization_language_index_);
#endif
ImGui_ImplSDL3_InitForSDLRenderer(main_window_, main_renderer_);
ImGui_ImplSDLRenderer3_Init(main_renderer_);
return 0;
}
@@ -671,8 +731,8 @@ int Render::CreateStreamWindow() {
ImGui::SetCurrentContext(stream_ctx_);
if (!SDL_CreateWindowAndRenderer(
"Stream window", (int)stream_window_width_default_,
(int)stream_window_height_default_,
"Stream window", (int)stream_window_width_,
(int)stream_window_height_,
SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_BORDERLESS,
&stream_window_, &stream_renderer_)) {
LOG_ERROR("Error creating stream_window_ and stream_renderer_: {}",
@@ -687,6 +747,15 @@ int Render::CreateStreamWindow() {
// for window region action
SDL_SetWindowHitTest(stream_window_, HitTestCallback, this);
SetupFontAndStyle(false);
ImGuiStyle& style = ImGui::GetStyle();
style.ScaleAllSizes(dpi_scale_);
style.FontScaleDpi = dpi_scale_;
ImGui_ImplSDL3_InitForSDLRenderer(stream_window_, stream_renderer_);
ImGui_ImplSDLRenderer3_Init(stream_renderer_);
// change props->stream_render_rect_
SDL_Event event;
event.type = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED;
@@ -696,6 +765,9 @@ int Render::CreateStreamWindow() {
stream_window_created_ = true;
just_created_ = true;
stream_window_inited_ = true;
LOG_INFO("Stream window inited");
return 0;
}
@@ -720,7 +792,9 @@ int Render::DestroyStreamWindow() {
return 0;
}
int Render::SetupFontAndStyle() {
int Render::SetupFontAndStyle(bool main_window) {
float font_size = 32.0f;
// Setup Dear ImGui style
ImGuiIO& io = ImGui::GetIO();
@@ -730,39 +804,80 @@ int Render::SetupFontAndStyle() {
ImFontConfig config;
config.FontDataOwnedByAtlas = false;
io.Fonts->AddFontFromMemoryTTF(OPPOSans_Regular_ttf, OPPOSans_Regular_ttf_len,
32.0f, &config,
font_size, &config,
io.Fonts->GetGlyphRangesChineseFull());
config.MergeMode = true;
static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len, 30.0f,
&config, icon_ranges);
io.Fonts->Build();
ImGui::StyleColorsLight();
return 0;
}
int Render::SetupMainWindow() {
if (!main_ctx_) {
LOG_ERROR("Main context is null");
return -1;
// Load system Chinese font as fallback
config.MergeMode = false;
config.FontDataOwnedByAtlas = false;
if (main_window) {
main_windows_system_chinese_font_ = nullptr;
} else {
stream_windows_system_chinese_font_ = nullptr;
}
ImGui::SetCurrentContext(main_ctx_);
#if defined(_WIN32)
// Windows: Try Microsoft YaHei (微软雅黑) first, then SimSun (宋体)
const char* font_paths[] = {"C:/Windows/Fonts/msyh.ttc",
"C:/Windows/Fonts/msyhbd.ttc",
"C:/Windows/Fonts/simsun.ttc", nullptr};
#elif defined(__APPLE__)
// macOS: Try PingFang SC first, then STHeiti
const char* font_paths[] = {"/System/Library/Fonts/PingFang.ttc",
"/System/Library/Fonts/STHeiti Light.ttc",
"/System/Library/Fonts/STHeiti Medium.ttc",
nullptr};
#else
// Linux: Try common Chinese fonts
const char* font_paths[] = {
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
"/usr/share/fonts/truetype/arphic/uming.ttc",
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", nullptr};
#endif
SetupFontAndStyle();
for (int i = 0; font_paths[i] != nullptr; i++) {
std::ifstream font_file(font_paths[i], std::ios::binary);
if (font_file.good()) {
font_file.close();
if (main_window) {
main_windows_system_chinese_font_ =
io.Fonts->AddFontFromFileTTF(font_paths[i], font_size, &config,
io.Fonts->GetGlyphRangesChineseFull());
if (main_windows_system_chinese_font_ != nullptr) {
LOG_INFO("Loaded system Chinese font: {}", font_paths[i]);
break;
}
} else {
stream_windows_system_chinese_font_ =
io.Fonts->AddFontFromFileTTF(font_paths[i], font_size, &config,
io.Fonts->GetGlyphRangesChineseFull());
if (stream_windows_system_chinese_font_ != nullptr) {
LOG_INFO("Loaded system Chinese font: {}", font_paths[i]);
break;
}
}
}
}
SDL_GetWindowSizeInPixels(main_window_, &main_window_width_real_,
&main_window_height_real_);
main_window_dpi_scaling_w_ = main_window_width_real_ / main_window_width_;
main_window_dpi_scaling_h_ = main_window_width_real_ / main_window_width_;
SDL_SetRenderScale(main_renderer_, main_window_dpi_scaling_w_,
main_window_dpi_scaling_h_);
LOG_INFO("Use dpi scaling [{}x{}] for main window",
main_window_dpi_scaling_w_, main_window_dpi_scaling_h_);
// If no system font found, use default font
if (main_window) {
if (main_windows_system_chinese_font_ == nullptr) {
main_windows_system_chinese_font_ = io.Fonts->AddFontDefault(&config);
LOG_WARN("System Chinese font not found, using default font");
}
} else {
if (stream_windows_system_chinese_font_ == nullptr) {
stream_windows_system_chinese_font_ = io.Fonts->AddFontDefault(&config);
LOG_WARN("System Chinese font not found, using default font");
}
}
ImGui_ImplSDL3_InitForSDLRenderer(main_window_, main_renderer_);
ImGui_ImplSDLRenderer3_Init(main_renderer_);
ImGui::StyleColorsLight();
return 0;
}
@@ -776,42 +891,6 @@ int Render::DestroyMainWindowContext() {
return 0;
}
int Render::SetupStreamWindow() {
if (stream_window_inited_) {
return 0;
}
if (!stream_ctx_) {
LOG_ERROR("Stream context is null");
return -1;
}
ImGui::SetCurrentContext(stream_ctx_);
SetupFontAndStyle();
SDL_GetWindowSizeInPixels(stream_window_, &stream_window_width_real_,
&stream_window_height_real_);
stream_window_dpi_scaling_w_ =
stream_window_width_real_ / stream_window_width_;
stream_window_dpi_scaling_h_ =
stream_window_width_real_ / stream_window_width_;
SDL_SetRenderScale(stream_renderer_, stream_window_dpi_scaling_w_,
stream_window_dpi_scaling_h_);
LOG_INFO("Use dpi scaling [{}x{}] for stream window",
stream_window_dpi_scaling_w_, stream_window_dpi_scaling_h_);
ImGui_ImplSDL3_InitForSDLRenderer(stream_window_, stream_renderer_);
ImGui_ImplSDLRenderer3_Init(stream_renderer_);
stream_window_inited_ = true;
LOG_INFO("Stream window inited");
return 0;
}
int Render::DestroyStreamWindowContext() {
stream_window_inited_ = false;
ImGui::SetCurrentContext(stream_ctx_);
@@ -833,25 +912,36 @@ int Render::DrawMainWindow() {
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
ImGuiIO& io = ImGui::GetIO();
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(main_window_width_, main_window_height_default_),
ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y),
ImGuiCond_Always);
ImGui::Begin("MainRender", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoDocking);
ImGui::PopStyleColor();
TitleBar(true);
MainWindow();
UpdateNotificationWindow();
#ifdef __APPLE__
if (show_request_permission_window_) {
RequestPermissionWindow();
}
#endif
ImGui::End();
// Rendering
(void)io;
ImGui::Render();
SDL_SetRenderScale(main_renderer_, io.DisplayFramebufferScale.x,
io.DisplayFramebufferScale.y);
SDL_RenderClear(main_renderer_);
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), main_renderer_);
SDL_RenderPresent(main_renderer_);
@@ -872,28 +962,38 @@ int Render::DrawStreamWindow() {
StreamWindow();
ImGuiIO& io = ImGui::GetIO();
float stream_title_window_height =
fullscreen_button_pressed_ ? 0 : title_bar_height_;
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
// Set minimum window size to 0 to allow exact height control
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, stream_title_window_height),
ImGuiCond_Always);
ImGui::Begin("StreamTitleWindow", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoDocking);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
if (!fullscreen_button_pressed_) {
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(stream_window_width_,
fullscreen_button_pressed_ ? 0 : title_bar_height_),
ImGuiCond_Always);
ImGui::Begin("StreamWindowTitleBar", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoDocking);
ImGui::PopStyleColor();
TitleBar(false);
ImGui::End();
}
ImGui::End();
// Rendering
(void)io;
ImGui::Render();
SDL_SetRenderScale(stream_renderer_, io.DisplayFramebufferScale.x,
io.DisplayFramebufferScale.y);
SDL_RenderClear(stream_renderer_);
// std::shared_lock lock(client_properties_mutex_);
for (auto& it : client_properties_) {
auto props = it.second;
if (props->tab_selected_) {
@@ -913,8 +1013,25 @@ int Render::DrawStreamWindow() {
}
int Render::Run() {
latest_version_ = CheckUpdate();
update_available_ = IsNewerVersion(CROSSDESK_VERSION, latest_version_);
latest_version_info_ = CheckUpdate();
if (!latest_version_info_.empty() &&
latest_version_info_.contains("version") &&
latest_version_info_["version"].is_string()) {
latest_version_ = latest_version_info_["version"];
if (latest_version_info_.contains("releaseNotes") &&
latest_version_info_["releaseNotes"].is_string()) {
release_notes_ = latest_version_info_["releaseNotes"];
} else {
release_notes_ = "";
}
update_available_ = IsNewerVersion(CROSSDESK_VERSION, latest_version_);
if (update_available_) {
show_update_notification_window_ = true;
}
} else {
latest_version_ = "";
update_available_ = false;
}
path_manager_ = std::make_unique<PathManager>("CrossDesk");
if (path_manager_) {
@@ -929,10 +1046,15 @@ int Render::Run() {
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';
int signal_port_init = config_center_->GetSignalServerPort();
if (signal_port_init > 0) {
strncpy(signal_server_port_self_,
std::to_string(signal_port_init).c_str(),
sizeof(signal_server_port_self_) - 1);
signal_server_port_self_[sizeof(signal_server_port_self_) - 1] = '\0';
} else {
signal_server_port_self_[0] = '\0';
}
strncpy(cert_file_path_, cert_path_.c_str(), sizeof(cert_file_path_) - 1);
cert_file_path_[sizeof(cert_file_path_) - 1] = '\0';
} else {
@@ -941,6 +1063,8 @@ int Render::Run() {
}
InitializeLogger();
LOG_INFO("CrossDesk version: {}", CROSSDESK_VERSION);
InitializeSettings();
InitializeSDL();
InitializeModules();
@@ -1005,7 +1129,6 @@ void Render::InitializeModules() {
void Render::InitializeMainWindow() {
CreateMainWindow();
SetupMainWindow();
if (SDL_WINDOW_HIDDEN & SDL_GetWindowFlags(main_window_)) {
SDL_ShowWindow(main_window_);
}
@@ -1116,7 +1239,6 @@ void Render::HandleRecentConnections() {
void Render::HandleStreamWindow() {
if (need_to_create_stream_window_) {
CreateStreamWindow();
SetupStreamWindow();
need_to_create_stream_window_ = false;
}
@@ -1157,6 +1279,9 @@ void Render::Cleanup() {
CleanupFactories();
CleanupPeers();
WaitForThumbnailSaveTasks();
AudioDeviceDestroy();
DestroyMainWindowContext();
DestroyMainWindow();
@@ -1184,10 +1309,29 @@ void Render::CleanupPeer(std::shared_ptr<SubStreamWindowProperties> props) {
SDL_FlushEvent(STREAM_REFRESH_EVENT);
if (props->dst_buffer_) {
thumbnail_->SaveToThumbnail(
(char*)props->dst_buffer_, props->video_width_, props->video_height_,
props->remote_id_, props->remote_host_name_,
props->remember_password_ ? props->remote_password_ : "");
size_t buffer_size = props->dst_buffer_capacity_;
std::vector<unsigned char> buffer_copy(buffer_size);
memcpy(buffer_copy.data(), props->dst_buffer_, buffer_size);
int video_width = props->video_width_;
int video_height = props->video_height_;
std::string remote_id = props->remote_id_;
std::string remote_host_name = props->remote_host_name_;
std::string password =
props->remember_password_ ? props->remote_password_ : "";
std::thread save_thread([buffer_copy, video_width, video_height, remote_id,
remote_host_name, password,
thumbnail = thumbnail_]() {
thumbnail->SaveToThumbnail((char*)buffer_copy.data(), video_width,
video_height, remote_id, remote_host_name,
password);
});
{
std::lock_guard<std::mutex> lock(thumbnail_save_threads_mutex_);
thumbnail_save_threads_.emplace_back(std::move(save_thread));
}
}
if (props->peer_) {
@@ -1211,12 +1355,37 @@ void Render::CleanupPeers() {
DestroyPeer(&peer_);
}
for (auto& it : client_properties_) {
auto props = it.second;
CleanupPeer(props);
{
// std::shared_lock lock(client_properties_mutex_);
for (auto& it : client_properties_) {
auto props = it.second;
CleanupPeer(props);
}
}
client_properties_.clear();
{
// std::unique_lock lock(client_properties_mutex_);
client_properties_.clear();
}
}
void Render::WaitForThumbnailSaveTasks() {
std::vector<std::thread> threads_to_join;
{
std::lock_guard<std::mutex> lock(thumbnail_save_threads_mutex_);
threads_to_join.swap(thumbnail_save_threads_);
}
if (threads_to_join.empty()) {
return;
}
for (auto& thread : threads_to_join) {
if (thread.joinable()) {
thread.join();
}
}
}
void Render::CleanSubStreamWindowProperties(
@@ -1233,6 +1402,7 @@ void Render::CleanSubStreamWindowProperties(
}
void Render::UpdateRenderRect() {
// std::shared_lock lock(client_properties_mutex_);
for (auto& [_, props] : client_properties_) {
if (!props->reset_control_bar_pos_) {
props->mouse_diff_control_bar_pos_x_ = 0;
@@ -1307,34 +1477,41 @@ void Render::ProcessSdlEvent(const SDL_Event& event) {
DestroyStreamWindow();
DestroyStreamWindowContext();
for (auto& [host_name, props] : client_properties_) {
thumbnail_->SaveToThumbnail(
(char*)props->dst_buffer_, props->video_width_,
props->video_height_, host_name, props->remote_host_name_,
props->remember_password_ ? props->remote_password_ : "");
{
// std::shared_lock lock(client_properties_mutex_);
for (auto& [host_name, props] : client_properties_) {
thumbnail_->SaveToThumbnail(
(char*)props->dst_buffer_, props->video_width_,
props->video_height_, host_name, props->remote_host_name_,
props->remember_password_ ? props->remote_password_ : "");
if (props->peer_) {
std::string client_id = (host_name == client_id_)
? "C-" + std::string(client_id_)
: client_id_;
LOG_INFO("[{}] Leave connection [{}]", client_id, host_name);
LeaveConnection(props->peer_, host_name.c_str());
LOG_INFO("Destroy peer [{}]", client_id);
DestroyPeer(&props->peer_);
if (props->peer_) {
std::string client_id = (host_name == client_id_)
? "C-" + std::string(client_id_)
: client_id_;
LOG_INFO("[{}] Leave connection [{}]", client_id, host_name);
LeaveConnection(props->peer_, host_name.c_str());
LOG_INFO("Destroy peer [{}]", client_id);
DestroyPeer(&props->peer_);
}
props->streaming_ = false;
props->remember_password_ = false;
props->connection_established_ = false;
props->audio_capture_button_pressed_ = false;
memset(&props->net_traffic_stats_, 0,
sizeof(props->net_traffic_stats_));
SDL_SetWindowFullscreen(main_window_, false);
SDL_FlushEvents(STREAM_REFRESH_EVENT, STREAM_REFRESH_EVENT);
memset(audio_buffer_, 0, 720);
}
props->streaming_ = false;
props->remember_password_ = false;
props->connection_established_ = false;
props->audio_capture_button_pressed_ = false;
memset(&props->net_traffic_stats_, 0,
sizeof(props->net_traffic_stats_));
SDL_SetWindowFullscreen(main_window_, false);
SDL_FlushEvents(STREAM_REFRESH_EVENT, STREAM_REFRESH_EVENT);
memset(audio_buffer_, 0, 720);
}
client_properties_.clear();
{
// std::unique_lock lock(client_properties_mutex_);
client_properties_.clear();
}
rejoin_ = false;
is_client_mode_ = false;

View File

@@ -13,9 +13,12 @@
#include <chrono>
#include <fstream>
#include <mutex>
#include <nlohmann/json.hpp>
#include <optional>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include "IconsFontAwesome6.h"
#include "config_center.h"
@@ -29,6 +32,7 @@
#include "screen_capturer_factory.h"
#include "speaker_capturer_factory.h"
#include "thumbnail.h"
#if _WIN32
#include "win_tray.h"
#endif
@@ -68,10 +72,10 @@ class Render {
float sub_stream_window_height_ = 720;
float control_window_min_width_ = 20;
float control_window_max_width_ = 230;
float control_window_min_height_ = 40;
float control_window_max_height_ = 170;
float control_window_min_height_ = 38;
float control_window_max_height_ = 180;
float control_window_width_ = 230;
float control_window_height_ = 40;
float control_window_height_ = 38;
float control_bar_pos_x_ = 0;
float control_bar_pos_y_ = 30;
float mouse_diff_control_bar_pos_x_ = 0;
@@ -151,6 +155,7 @@ class Render {
int CreateStreamRenderWindow();
int TitleBar(bool main_window);
int MainWindow();
int UpdateNotificationWindow();
int StreamWindow();
int LocalWindow();
int RemoteWindow();
@@ -165,6 +170,8 @@ class Render {
bool ConnectionStatusWindow(
std::shared_ptr<SubStreamWindowProperties>& props);
int ShowRecentConnections();
void Hyperlink(const std::string& label, const std::string& url,
const float window_width);
private:
int ConnectTo(const std::string& remote_id, const char* password,
@@ -173,10 +180,8 @@ class Render {
int DestroyMainWindow();
int CreateStreamWindow();
int DestroyStreamWindow();
int SetupFontAndStyle();
int SetupMainWindow();
int SetupFontAndStyle(bool main_window);
int DestroyMainWindowContext();
int SetupStreamWindow();
int DestroyStreamWindowContext();
int DrawMainWindow();
int DrawStreamWindow();
@@ -184,6 +189,14 @@ class Render {
int NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props);
void DrawConnectionStatusText(
std::shared_ptr<SubStreamWindowProperties>& props);
#ifdef __APPLE__
int RequestPermissionWindow();
bool CheckScreenRecordingPermission();
bool CheckAccessibilityPermission();
void OpenScreenRecordingPreferences();
void OpenAccessibilityPreferences();
bool DrawToggleSwitch(const char* id, bool active, bool enabled);
#endif
public:
static void OnReceiveVideoBufferCb(const XVideoFrame* video_frame,
@@ -281,13 +294,15 @@ class Render {
/* ------ all windows property start ------ */
float title_bar_width_ = 640;
float title_bar_height_ = 30;
float title_bar_button_width_ = 30;
float title_bar_button_height_ = 30;
/* ------ all windows property end ------ */
/* ------ main window property start ------ */
// thumbnail
unsigned char aes128_key_[16];
unsigned char aes128_iv_[16];
std::unique_ptr<Thumbnail> thumbnail_;
std::shared_ptr<Thumbnail> thumbnail_;
// recent connections
std::vector<std::pair<std::string, Thumbnail::RecentConnection>>
@@ -300,6 +315,8 @@ class Render {
SDL_Window* main_window_ = nullptr;
SDL_Renderer* main_renderer_ = nullptr;
ImGuiContext* main_ctx_ = nullptr;
ImFont* main_windows_system_chinese_font_ = nullptr;
ImFont* stream_windows_system_chinese_font_ = nullptr;
bool exit_ = false;
const int sdl_refresh_ms_ = 16; // ~60 FPS
#if _WIN32
@@ -307,8 +324,10 @@ class Render {
#endif
// main window properties
std::string latest_version_ = "";
nlohmann::json latest_version_info_ = nlohmann::json{};
bool update_available_ = false;
std::string latest_version_ = "";
std::string release_notes_ = "";
bool start_mouse_controller_ = false;
bool mouse_controller_is_started_ = false;
bool start_screen_capturer_ = false;
@@ -325,6 +344,7 @@ class Render {
int main_window_height_real_ = 540;
float main_window_dpi_scaling_w_ = 1.0f;
float main_window_dpi_scaling_h_ = 1.0f;
float dpi_scale_ = 1.0f;
float main_window_width_default_ = 640;
float main_window_height_default_ = 480;
float main_window_width_ = 640;
@@ -349,6 +369,8 @@ class Render {
float notification_window_height_ = 80;
float about_window_width_ = 300;
float about_window_height_ = 170;
float update_notification_window_width_ = 400;
float update_notification_window_height_ = 320;
int screen_width_ = 1280;
int screen_height_ = 720;
int selected_display_ = 0;
@@ -362,6 +384,7 @@ class Render {
int audio_len_ = 0;
bool audio_buffer_fresh_ = false;
bool need_to_rejoin_ = false;
std::chrono::steady_clock::time_point last_rejoin_check_time_;
bool just_created_ = false;
std::string controlled_remote_id_ = "";
std::string focused_remote_id_ = "";
@@ -404,6 +427,7 @@ class Render {
bool show_about_window_ = false;
bool show_connection_status_window_ = false;
bool show_reset_password_window_ = false;
bool show_update_notification_window_ = false;
bool fullscreen_button_pressed_ = false;
bool focus_on_input_widget_ = true;
bool is_client_mode_ = false;
@@ -439,6 +463,9 @@ class Render {
bool show_new_version_icon_in_menu_ = true;
uint64_t new_version_icon_last_trigger_time_ = 0;
uint64_t new_version_icon_render_start_time_ = 0;
#ifdef __APPLE__
bool show_request_permission_window_ = true;
#endif
char client_id_[10] = "";
char client_id_display_[12] = "";
char client_id_with_password_[17] = "";
@@ -448,7 +475,7 @@ class Render {
int video_frame_rate_button_value_ = 1;
int video_encode_format_button_value_ = 0;
bool enable_hardware_video_codec_ = false;
bool enable_turn_ = false;
bool enable_turn_ = true;
bool enable_srtp_ = false;
char signal_server_ip_[256] = "api.crossdesk.cn";
char signal_server_port_[6] = "9099";
@@ -460,7 +487,7 @@ class Render {
int video_frame_rate_button_value_last_ = 0;
int video_encode_format_button_value_last_ = 0;
bool enable_hardware_video_codec_last_ = false;
bool enable_turn_last_ = false;
bool enable_turn_last_ = true;
bool enable_srtp_last_ = false;
bool enable_self_hosted_last_ = false;
bool enable_autostart_ = false;
@@ -482,9 +509,15 @@ class Render {
/* ------ sub stream window property start ------ */
std::unordered_map<std::string, std::shared_ptr<SubStreamWindowProperties>>
client_properties_;
std::shared_mutex client_properties_mutex_;
void CloseTab(decltype(client_properties_)::iterator& it);
/* ------ stream window property end ------ */
/* ------ async thumbnail save tasks ------ */
std::vector<std::thread> thumbnail_save_threads_;
std::mutex thumbnail_save_threads_mutex_;
void WaitForThumbnailSaveTasks();
/* ------ server mode ------ */
std::unordered_map<std::string, ConnectionStatus> connection_status_;
};

View File

@@ -21,13 +21,16 @@ int Render::SendKeyCommand(int key_code, bool is_down) {
remote_action.k.key_value = key_code;
if (!controlled_remote_id_.empty()) {
// std::shared_lock lock(client_properties_mutex_);
if (client_properties_.find(controlled_remote_id_) !=
client_properties_.end()) {
auto props = client_properties_[controlled_remote_id_];
if (props->connection_status_ == ConnectionStatus::Connected) {
std::string msg = remote_action.to_json();
SendDataFrame(props->peer_, msg.c_str(), msg.size(),
props->data_label_.c_str());
if (props->peer_) {
SendDataFrame(props->peer_, msg.c_str(), msg.size(),
props->data_label_.c_str());
}
}
}
}
@@ -42,6 +45,7 @@ int Render::ProcessMouseEvent(const SDL_Event& event) {
float ratio_x, ratio_y = 0;
RemoteAction remote_action;
// std::shared_lock lock(client_properties_mutex_);
for (auto& it : client_properties_) {
auto props = it.second;
if (!props->control_mouse_) {
@@ -94,8 +98,10 @@ int Render::ProcessMouseEvent(const SDL_Event& event) {
}
std::string msg = remote_action.to_json();
SendDataFrame(props->peer_, msg.c_str(), msg.size(),
props->data_label_.c_str());
if (props->peer_) {
SendDataFrame(props->peer_, msg.c_str(), msg.size(),
props->data_label_.c_str());
}
} else if (SDL_EVENT_MOUSE_WHEEL == event.type &&
last_mouse_event.button.x >= props->stream_render_rect_.x &&
last_mouse_event.button.x <= props->stream_render_rect_.x +
@@ -139,8 +145,10 @@ int Render::ProcessMouseEvent(const SDL_Event& event) {
render_height;
std::string msg = remote_action.to_json();
SendDataFrame(props->peer_, msg.c_str(), msg.size(),
props->data_label_.c_str());
if (props->peer_) {
SendDataFrame(props->peer_, msg.c_str(), msg.size(),
props->data_label_.c_str());
}
}
}
@@ -154,11 +162,14 @@ void Render::SdlCaptureAudioIn(void* userdata, Uint8* stream, int len) {
}
if (1) {
for (auto it : render->client_properties_) {
// std::shared_lock lock(render->client_properties_mutex_);
for (const auto& it : render->client_properties_) {
auto props = it.second;
if (props->connection_status_ == ConnectionStatus::Connected) {
SendAudioFrame(props->peer_, (const char*)stream, len,
render->audio_label_.c_str());
if (props->peer_) {
SendAudioFrame(props->peer_, (const char*)stream, len,
render->audio_label_.c_str());
}
}
}
@@ -207,6 +218,7 @@ void Render::OnReceiveVideoBufferCb(const XVideoFrame* video_frame,
}
std::string remote_id(user_id, user_id_size);
// std::shared_lock lock(render->client_properties_mutex_);
if (render->client_properties_.find(remote_id) ==
render->client_properties_.end()) {
return;
@@ -302,6 +314,7 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
}
std::string remote_id(user_id, user_id_size);
// std::shared_lock lock(render->client_properties_mutex_);
if (render->client_properties_.find(remote_id) !=
render->client_properties_.end()) {
// local
@@ -373,6 +386,7 @@ void Render::OnSignalStatusCb(SignalStatus status, const char* user_id,
}
std::string remote_id(client_id.begin() + 2, client_id.end());
// std::shared_lock lock(render->client_properties_mutex_);
if (render->client_properties_.find(remote_id) ==
render->client_properties_.end()) {
return;
@@ -402,6 +416,7 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
if (!render) return;
std::string remote_id(user_id, user_id_size);
// std::shared_lock lock(render->client_properties_mutex_);
auto it = render->client_properties_.find(remote_id);
auto props = (it != render->client_properties_.end()) ? it->second : nullptr;
@@ -428,7 +443,7 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
case ConnectionStatus::Closed: {
props->connection_established_ = false;
props->mouse_control_button_pressed_ = false;
if (props->dst_buffer_) {
if (props->dst_buffer_ && props->stream_texture_) {
memset(props->dst_buffer_, 0, props->dst_buffer_capacity_);
SDL_UpdateTexture(props->stream_texture_, NULL, props->dst_buffer_,
props->texture_width_);
@@ -562,6 +577,7 @@ void Render::NetStatusReport(const char* client_id, size_t client_id_size,
}
std::string remote_id(user_id, user_id_size);
// std::shared_lock lock(render->client_properties_mutex_);
if (render->client_properties_.find(remote_id) ==
render->client_properties_.end()) {
return;

View File

@@ -32,26 +32,30 @@ int LossRateDisplay(float loss_rate) {
}
int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
float button_width = title_bar_height_ * 0.8f;
float button_height = title_bar_height_ * 0.8f;
float line_padding = title_bar_height_ * 0.12f;
float line_thickness = title_bar_height_ * 0.07f;
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
if (props->control_bar_expand_) {
ImGui::SetCursorPosX(props->is_control_bar_in_left_
? (props->control_window_width_ + 5.0f)
: 38.0f);
// mouse control button
ImDrawList* draw_list = ImGui::GetWindowDrawList();
? props->control_window_width_ * 1.03f
: props->control_window_width_ * 0.2f);
if (props->is_control_bar_in_left_) {
draw_list->AddLine(ImVec2(ImGui::GetCursorScreenPos().x - 5.0f,
ImGui::GetCursorScreenPos().y - 7.0f),
ImVec2(ImGui::GetCursorScreenPos().x - 5.0f,
ImGui::GetCursorScreenPos().y - 7.0f +
props->control_window_height_),
IM_COL32(178, 178, 178, 255), 1.0f);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (!props->is_control_bar_in_left_) {
draw_list->AddLine(
ImVec2(ImGui::GetCursorScreenPos().x - button_height * 0.56f,
ImGui::GetCursorScreenPos().y + button_height * 0.2f),
ImVec2(ImGui::GetCursorScreenPos().x - button_height * 0.56f,
ImGui::GetCursorScreenPos().y + button_height * 0.8f),
IM_COL32(178, 178, 178, 255), 2.0f);
}
std::string display = ICON_FA_DISPLAY;
if (ImGui::Button(display.c_str(), ImVec2(25, 25))) {
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(display.c_str(), ImVec2(button_width, button_height))) {
ImGui::OpenPopup("display");
}
@@ -75,28 +79,29 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
}
props->display_selectable_hovered_ = ImGui::IsWindowHovered();
}
ImGui::SetWindowFontScale(1.0f);
ImGui::EndPopup();
}
ImGui::SetWindowFontScale(0.6f);
ImGui::SetWindowFontScale(0.5f);
ImVec2 text_size = ImGui::CalcTextSize(
std::to_string(props->selected_display_ + 1).c_str());
ImVec2 text_pos =
ImVec2(btn_min.x + (btn_size_actual.x - text_size.x) * 0.5f,
btn_min.y + (btn_size_actual.y - text_size.y) * 0.5f - 2.0f);
btn_min.y + (btn_size_actual.y - text_size.y) * 0.35f);
ImGui::GetWindowDrawList()->AddText(
text_pos, IM_COL32(0, 0, 0, 255),
std::to_string(props->selected_display_ + 1).c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::SameLine();
float disable_mouse_x = ImGui::GetCursorScreenPos().x + 4.0f;
float disable_mouse_y = ImGui::GetCursorScreenPos().y + 4.0f;
float mouse_x = ImGui::GetCursorScreenPos().x;
float mouse_y = ImGui::GetCursorScreenPos().y;
float disable_mouse_x = mouse_x + line_padding;
float disable_mouse_y = mouse_y + line_padding;
std::string mouse = props->mouse_control_button_pressed_
? ICON_FA_COMPUTER_MOUSE
: ICON_FA_COMPUTER_MOUSE;
if (ImGui::Button(mouse.c_str(), ImVec2(25, 25))) {
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(mouse.c_str(), ImVec2(button_width, button_height))) {
if (props->connection_established_) {
start_keyboard_capturer_ = !start_keyboard_capturer_;
props->control_mouse_ = !props->control_mouse_;
@@ -108,30 +113,35 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
: localization::control_mouse[localization_language_index_];
}
}
if (!props->mouse_control_button_pressed_) {
draw_list->AddLine(ImVec2(disable_mouse_x, disable_mouse_y),
ImVec2(mouse_x + button_width - line_padding,
mouse_y + button_height - line_padding),
IM_COL32(0, 0, 0, 255), line_thickness);
draw_list->AddLine(
ImVec2(disable_mouse_x, disable_mouse_y),
ImVec2(disable_mouse_x + 16.0f, disable_mouse_y + 14.2f),
IM_COL32(0, 0, 0, 255), 2.0f);
draw_list->AddLine(
ImVec2(disable_mouse_x - 1.2f, disable_mouse_y + 1.2f),
ImVec2(disable_mouse_x + 15.3f, disable_mouse_y + 15.4f),
ImVec2(disable_mouse_x - line_thickness * 0.7f,
disable_mouse_y + line_thickness * 0.7f),
ImVec2(
mouse_x + button_width - line_padding - line_thickness * 0.7f,
mouse_y + button_height - line_padding + line_thickness * 0.7f),
ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255)
: IM_COL32(179, 213, 253, 255),
2.0f);
line_thickness);
}
ImGui::SameLine();
// audio capture button
float disable_audio_x = ImGui::GetCursorScreenPos().x + 4;
float disable_audio_y = ImGui::GetCursorScreenPos().y + 4.0f;
// std::string audio = audio_capture_button_pressed_ ? ICON_FA_VOLUME_HIGH
// :
// ICON_FA_VOLUME_XMARK;
float audio_x = ImGui::GetCursorScreenPos().x;
float audio_y = ImGui::GetCursorScreenPos().y;
float disable_audio_x = audio_x + line_padding;
float disable_audio_y = audio_y + line_padding;
std::string audio = props->audio_capture_button_pressed_
? ICON_FA_VOLUME_HIGH
: ICON_FA_VOLUME_HIGH;
if (ImGui::Button(audio.c_str(), ImVec2(25, 25))) {
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(audio.c_str(), ImVec2(button_width, button_height))) {
if (props->connection_established_) {
props->audio_capture_button_pressed_ =
!props->audio_capture_button_pressed_;
@@ -148,17 +158,21 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
props->data_label_.c_str());
}
}
if (!props->audio_capture_button_pressed_) {
draw_list->AddLine(ImVec2(disable_audio_x, disable_audio_y),
ImVec2(audio_x + button_width - line_padding,
audio_y + button_height - line_padding),
IM_COL32(0, 0, 0, 255), line_thickness);
draw_list->AddLine(
ImVec2(disable_audio_x, disable_audio_y),
ImVec2(disable_audio_x + 16.0f, disable_audio_y + 14.2f),
IM_COL32(0, 0, 0, 255), 2.0f);
draw_list->AddLine(
ImVec2(disable_audio_x - 1.2f, disable_audio_y + 1.2f),
ImVec2(disable_audio_x + 15.3f, disable_audio_y + 15.4f),
ImVec2(disable_audio_x - line_thickness * 0.7f,
disable_audio_y + line_thickness * 0.7f),
ImVec2(
audio_x + button_width - line_padding - line_thickness * 0.7f,
audio_y + button_height - line_padding + line_thickness * 0.7f),
ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255)
: IM_COL32(179, 213, 253, 255),
2.0f);
line_thickness);
}
ImGui::SameLine();
@@ -170,7 +184,9 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
button_color_style_pushed = true;
}
std::string net_traffic_stats = ICON_FA_SIGNAL;
if (ImGui::Button(net_traffic_stats.c_str(), ImVec2(25, 25))) {
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(net_traffic_stats.c_str(),
ImVec2(button_width, button_height))) {
props->net_traffic_stats_button_pressed_ =
!props->net_traffic_stats_button_pressed_;
props->control_window_height_is_changing_ = true;
@@ -182,6 +198,7 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
: localization::show_net_traffic_stats
[localization_language_index_];
}
if (button_color_style_pushed) {
ImGui::PopStyleColor();
button_color_style_pushed = false;
@@ -191,7 +208,9 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
// fullscreen button
std::string fullscreen =
fullscreen_button_pressed_ ? ICON_FA_COMPRESS : ICON_FA_EXPAND;
if (ImGui::Button(fullscreen.c_str(), ImVec2(25, 25))) {
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(fullscreen.c_str(),
ImVec2(button_width, button_height))) {
fullscreen_button_pressed_ = !fullscreen_button_pressed_;
props->fullscreen_button_label_ =
fullscreen_button_pressed_
@@ -209,25 +228,35 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
ImGui::SameLine();
// close button
std::string close_button = ICON_FA_XMARK;
if (ImGui::Button(close_button.c_str(), ImVec2(25, 25))) {
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(close_button.c_str(),
ImVec2(button_width, button_height))) {
CleanupPeer(props);
}
ImGui::SameLine();
if (!props->is_control_bar_in_left_) {
draw_list->AddLine(ImVec2(ImGui::GetCursorScreenPos().x - 3.0f,
ImGui::GetCursorScreenPos().y - 7.0f),
ImVec2(ImGui::GetCursorScreenPos().x - 3.0f,
ImGui::GetCursorScreenPos().y - 7.0f +
props->control_window_height_),
IM_COL32(178, 178, 178, 255), 1.0f);
if (props->is_control_bar_in_left_) {
draw_list->AddLine(
ImVec2(ImGui::GetCursorScreenPos().x + button_height * 0.2f,
ImGui::GetCursorScreenPos().y + button_height * 0.2f),
ImVec2(ImGui::GetCursorScreenPos().x + button_height * 0.2f,
ImGui::GetCursorScreenPos().y + button_height * 0.8f),
IM_COL32(178, 178, 178, 255), 2.0f);
}
ImGui::SameLine();
}
ImGui::SetCursorPosX(props->is_control_bar_in_left_
? (props->control_window_width_ * 2 - 20.0f)
: 5.0f);
float expand_button_pos_x =
props->control_bar_expand_ ? (props->is_control_bar_in_left_
? props->control_window_width_ * 1.91f
: props->control_window_width_ * 0.03f)
: (props->is_control_bar_in_left_
? props->control_window_width_ * 1.02f
: props->control_window_width_ * 0.23f);
ImGui::SetCursorPosX(expand_button_pos_x);
std::string control_bar =
props->control_bar_expand_
@@ -235,13 +264,14 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
: ICON_FA_ANGLE_RIGHT)
: (props->is_control_bar_in_left_ ? ICON_FA_ANGLE_RIGHT
: ICON_FA_ANGLE_LEFT);
if (ImGui::Button(control_bar.c_str(), ImVec2(15, 25))) {
if (ImGui::Button(control_bar.c_str(),
ImVec2(button_height * 0.6f, button_height))) {
props->control_bar_expand_ = !props->control_bar_expand_;
props->control_bar_button_pressed_time_ = ImGui::GetTime();
props->control_window_width_is_changing_ = true;
if (!props->control_bar_expand_) {
props->control_window_height_ = 40;
props->control_window_height_ = props->control_window_min_height_;
props->net_traffic_stats_button_pressed_ = false;
}
}
@@ -257,13 +287,13 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
ImGui::SetCursorPos(ImVec2(props->is_control_bar_in_left_
? (props->control_window_width_ + 5.0f)
: 5.0f,
40.0f));
? props->control_window_width_ * 1.02f
: props->control_window_width_ * 0.02f,
props->control_window_min_height_));
ImGui::SetWindowFontScale(0.5f);
if (ImGui::BeginTable("NetTrafficStats", 4, ImGuiTableFlags_BordersH,
ImVec2(props->control_window_max_width_ - 10.0f,
props->control_window_max_height_ - 60.0f))) {
ImVec2(props->control_window_max_width_ * 0.9f,
props->control_window_max_height_ - 0.9f))) {
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
@@ -322,9 +352,12 @@ int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
ImGui::Text("FPS");
ImGui::TableNextColumn();
ImGui::Text("%d", props->fps_);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::EndTable();
}
ImGui::SetWindowFontScale(1.0f);
return 0;
}

View File

@@ -1,32 +1,38 @@
#include "layout_relative.h"
#include "localization.h"
#include "render.h"
namespace crossdesk {
int Render::StatusBar() {
ImGuiIO& io = ImGui::GetIO();
float status_bar_width = io.DisplaySize.x;
float status_bar_height = io.DisplaySize.y * STATUS_BAR_HEIGHT;
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
static bool a, b, c, d, e;
ImGui::SetNextWindowPos(
ImVec2(0, main_window_height_default_ - status_bar_height_ - 1),
ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(0, io.DisplaySize.y * (1 - STATUS_BAR_HEIGHT)),
ImGuiCond_Always);
ImGui::BeginChild(
"StatusBar", ImVec2(main_window_width_, status_bar_height_ + 1),
"StatusBar", ImVec2(status_bar_width, status_bar_height),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
ImVec2 dot_pos =
ImVec2(13, main_window_height_default_ - status_bar_height_ + 11.0f);
ImVec2 dot_pos = ImVec2(status_bar_width * 0.025f,
io.DisplaySize.y * (1 - STATUS_BAR_HEIGHT * 0.5f));
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddCircleFilled(dot_pos, 5.0f,
draw_list->AddCircleFilled(dot_pos, status_bar_height * 0.2f,
ImColor(signal_connected_ ? 0.0f : 1.0f,
signal_connected_ ? 1.0f : 0.0f, 0.0f),
100);
draw_list->AddCircle(dot_pos, 6.0f, ImColor(1.0f, 1.0f, 1.0f), 100);
draw_list->AddCircle(dot_pos, status_bar_height * 0.25f,
ImColor(1.0f, 1.0f, 1.0f), 100);
ImGui::SetWindowFontScale(0.6f);
draw_list->AddText(
ImVec2(25, main_window_height_default_ - status_bar_height_ + 3.0f),
ImVec2(status_bar_width * 0.045f,
io.DisplaySize.y * (1 - STATUS_BAR_HEIGHT * 0.9f)),
ImColor(0.0f, 0.0f, 0.0f),
signal_connected_
? localization::signal_connected[localization_language_index_].c_str()

View File

@@ -1,110 +1,97 @@
#include "layout_relative.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
#define BUTTON_PADDING 36.0f
#define NEW_VERSION_ICON_RENDER_TIME_INTERVAL 2000
namespace crossdesk {
int Render::TitleBar(bool main_window) {
ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
ImGuiIO& io = ImGui::GetIO();
float title_bar_width = title_bar_width_;
float title_bar_height = title_bar_height_;
float title_bar_height_padding = title_bar_height_;
float title_bar_button_width = title_bar_button_width_;
float title_bar_button_height = title_bar_button_height_;
if (main_window) {
title_bar_width = io.DisplaySize.x;
title_bar_height = io.DisplaySize.y * TITLE_BAR_HEIGHT;
title_bar_height_padding = io.DisplaySize.y * (TITLE_BAR_HEIGHT + 0.01f);
title_bar_button_width = io.DisplaySize.x * TITLE_BAR_BUTTON_WIDTH;
title_bar_button_height = io.DisplaySize.y * TITLE_BAR_BUTTON_HEIGHT;
title_bar_height_ = title_bar_height;
title_bar_button_width_ = title_bar_button_width;
title_bar_button_height_ = title_bar_button_height;
} else {
title_bar_width = io.DisplaySize.x;
title_bar_height = title_bar_button_height_;
title_bar_button_width = title_bar_button_width_;
title_bar_button_height = title_bar_button_height_;
}
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetWindowFontScale(0.8f);
ImGui::BeginChild(
main_window ? "MainTitleBar" : "StreamTitleBar",
ImVec2(main_window ? main_window_width_ : stream_window_width_,
title_bar_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::SetWindowFontScale(1.0f);
ImGui::BeginChild(main_window ? "MainTitleBar" : "StreamTitleBar",
ImVec2(title_bar_width, title_bar_height_padding),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
// get draw list
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (ImGui::BeginMenuBar()) {
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
(BUTTON_PADDING * 3 - 3));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive,
ImGui::SetCursorPos(
ImVec2(title_bar_width - title_bar_button_width * 3, 0.0f));
if (main_window) {
float bar_pos_x = title_bar_width - title_bar_button_width * 3 +
title_bar_button_width * 0.33f;
float bar_pos_y = title_bar_button_height * 0.5f;
// draw menu icon
float menu_bar_line_size = title_bar_button_width * 0.33f;
draw_list->AddLine(
ImVec2(bar_pos_x, bar_pos_y - title_bar_button_height * 0.15f),
ImVec2(bar_pos_x + menu_bar_line_size,
bar_pos_y - title_bar_button_height * 0.15f),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(
ImVec2(bar_pos_x, bar_pos_y + title_bar_button_height * 0.15f),
ImVec2(bar_pos_x + menu_bar_line_size,
bar_pos_y + title_bar_button_height * 0.15f),
IM_COL32(0, 0, 0, 255));
std::string title_bar_menu_button = "##title_bar_menu"; // ICON_FA_BARS;
std::string title_bar_menu = "##title_bar_menu";
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (main_window) {
float bar_pos_x = ImGui::GetCursorPosX() + 6;
float bar_pos_y = ImGui::GetCursorPosY() + 15;
std::string menu_button = " "; // ICON_FA_BARS;
if (ImGui::BeginMenu(menu_button.c_str())) {
ImGui::SetWindowFontScale(0.5f);
if (ImGui::MenuItem(
localization::settings[localization_language_index_].c_str())) {
show_settings_window_ = true;
}
if (ImGui::Button(
title_bar_menu_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height))) {
ImGui::OpenPopup(title_bar_menu.c_str());
}
ImGui::PopStyleColor(3);
show_new_version_icon_in_menu_ = false;
std::string about_str =
localization::about[localization_language_index_];
if (update_available_) {
auto now_time =
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
// every 2 seconds
if (now_time - new_version_icon_last_trigger_time_ >=
NEW_VERSION_ICON_RENDER_TIME_INTERVAL) {
show_new_version_icon_ = true;
new_version_icon_render_start_time_ = now_time;
new_version_icon_last_trigger_time_ = now_time;
}
// render for 1 second
if (show_new_version_icon_) {
about_str = about_str + " " + ICON_FA_TRIANGLE_EXCLAMATION;
if (now_time - new_version_icon_render_start_time_ >=
NEW_VERSION_ICON_RENDER_TIME_INTERVAL / 2) {
show_new_version_icon_ = false;
}
} else {
about_str = about_str + " ";
}
}
if (ImGui::MenuItem(about_str.c_str())) {
show_about_window_ = true;
}
if (update_available_ && ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::SetWindowFontScale(0.5f);
std::string new_version_available_str =
localization::new_version_available
[localization_language_index_] +
": " + latest_version_;
ImGui::Text("%s", new_version_available_str.c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::EndTooltip();
}
ImGui::SetWindowFontScale(1.0f);
ImGui::EndMenu();
} else {
show_new_version_icon_in_menu_ = true;
if (ImGui::BeginPopup(title_bar_menu.c_str())) {
ImGui::SetWindowFontScale(0.6f);
if (ImGui::MenuItem(
localization::settings[localization_language_index_].c_str())) {
show_settings_window_ = true;
}
float menu_bar_line_size = 15.0f;
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y - 6),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y - 6),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y + 6),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y + 6),
IM_COL32(0, 0, 0, 255));
show_new_version_icon_in_menu_ = false;
if (update_available_ && show_new_version_icon_in_menu_) {
std::string about_str = localization::about[localization_language_index_];
if (update_available_) {
auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
@@ -119,133 +106,198 @@ int Render::TitleBar(bool main_window) {
// render for 1 second
if (show_new_version_icon_) {
ImGui::SetWindowFontScale(0.6f);
ImGui::SetCursorPos(ImVec2(bar_pos_x + 10, bar_pos_y - 17));
ImGui::Text(ICON_FA_TRIANGLE_EXCLAMATION);
ImGui::SetWindowFontScale(1.0f);
about_str = about_str + " " + ICON_FA_TRIANGLE_EXCLAMATION;
if (now_time - new_version_icon_render_start_time_ >=
NEW_VERSION_ICON_RENDER_TIME_INTERVAL / 2) {
show_new_version_icon_ = false;
}
} else {
about_str = about_str + " ";
}
}
{
SettingWindow();
SelfHostedServerWindow();
AboutWindow();
if (ImGui::MenuItem(about_str.c_str())) {
show_about_window_ = true;
}
if (update_available_ && ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::SetWindowFontScale(0.5f);
std::string new_version_available_str =
localization::new_version_available[localization_language_index_] +
": " + latest_version_;
ImGui::Text("%s", new_version_available_str.c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::EndTooltip();
}
ImGui::EndPopup();
} else {
show_new_version_icon_in_menu_ = true;
}
if (update_available_ && show_new_version_icon_in_menu_) {
auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
// every 2 seconds
if (now_time - new_version_icon_last_trigger_time_ >=
NEW_VERSION_ICON_RENDER_TIME_INTERVAL) {
show_new_version_icon_ = true;
new_version_icon_render_start_time_ = now_time;
new_version_icon_last_trigger_time_ = now_time;
}
// render for 1 second
if (show_new_version_icon_) {
ImGui::SetWindowFontScale(0.6f);
ImGui::SetCursorPos(
ImVec2(bar_pos_x + title_bar_button_width * 0.15f,
bar_pos_y - title_bar_button_width * 0.325f));
ImGui::Text(ICON_FA_TRIANGLE_EXCLAMATION);
ImGui::SetWindowFontScale(1.0f);
if (now_time - new_version_icon_render_start_time_ >=
NEW_VERSION_ICON_RENDER_TIME_INTERVAL / 2) {
show_new_version_icon_ = false;
}
}
}
ImGui::PopStyleColor(2);
{
SettingWindow();
SelfHostedServerWindow();
AboutWindow();
}
} else {
ImGui::SetWindowFontScale(1.2f);
}
ImGui::SetCursorPos(ImVec2(
title_bar_width - title_bar_button_width * (main_window ? 2 : 3), 0.0f));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
float minimize_pos_x = title_bar_width -
title_bar_button_width * (main_window ? 2 : 3) +
title_bar_button_width * 0.33f;
float minimize_pos_y = title_bar_button_height * 0.5f;
std::string window_minimize_button = "##minimize"; // ICON_FA_MINUS;
if (ImGui::Button(window_minimize_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height))) {
SDL_MinimizeWindow(main_window ? main_window_ : stream_window_);
}
draw_list->AddLine(
ImVec2(minimize_pos_x, minimize_pos_y),
ImVec2(minimize_pos_x + title_bar_button_width * 0.33f, minimize_pos_y),
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(3);
if (!main_window) {
ImGui::SetCursorPos(
ImVec2(title_bar_width - title_bar_button_width * 2, 0.0f));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::SetCursorPosX(main_window
? (main_window_width_ - BUTTON_PADDING * 2)
: (stream_window_width_ - BUTTON_PADDING * 3));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
float minimize_pos_x = ImGui::GetCursorPosX() + 12;
float minimize_pos_y = ImGui::GetCursorPosY() + 15;
std::string window_minimize_button = "##minimize"; // ICON_FA_MINUS;
if (ImGui::Button(window_minimize_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_MinimizeWindow(main_window ? main_window_ : stream_window_);
}
draw_list->AddLine(ImVec2(minimize_pos_x, minimize_pos_y),
ImVec2(minimize_pos_x + 12, minimize_pos_y),
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(2);
if (!main_window) {
ImGui::SetCursorPosX(stream_window_width_ - BUTTON_PADDING * 2);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (window_maximized_) {
float pos_x_top = ImGui::GetCursorPosX() + 11;
float pos_y_top = ImGui::GetCursorPosY() + 11;
float pos_x_bottom = ImGui::GetCursorPosX() + 13;
float pos_y_bottom = ImGui::GetCursorPosY() + 9;
std::string window_restore_button =
"##restore"; // ICON_FA_WINDOW_RESTORE;
if (ImGui::Button(window_restore_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_RestoreWindow(stream_window_);
window_maximized_ = false;
}
draw_list->AddRect(ImVec2(pos_x_top, pos_y_top),
ImVec2(pos_x_top + 12, pos_y_top + 12),
IM_COL32(0, 0, 0, 255));
draw_list->AddRect(ImVec2(pos_x_bottom, pos_y_bottom),
ImVec2(pos_x_bottom + 12, pos_y_bottom + 12),
IM_COL32(0, 0, 0, 255));
draw_list->AddRectFilled(ImVec2(pos_x_top + 1, pos_y_top + 1),
ImVec2(pos_x_top + 11, pos_y_top + 11),
IM_COL32(255, 255, 255, 255));
} else {
float maximize_pos_x = ImGui::GetCursorPosX() + 12;
float maximize_pos_y = ImGui::GetCursorPosY() + 10;
std::string window_maximize_button =
"##maximize"; // ICON_FA_SQUARE_FULL;
if (ImGui::Button(window_maximize_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_MaximizeWindow(stream_window_);
window_maximized_ = !window_maximized_;
}
draw_list->AddRect(ImVec2(maximize_pos_x, maximize_pos_y),
ImVec2(maximize_pos_x + 12, maximize_pos_y + 12),
IM_COL32(0, 0, 0, 255));
if (window_maximized_) {
float pos_x_top = title_bar_width - title_bar_button_width * 1.65f;
float pos_y_top = title_bar_button_height * 0.36f;
float pos_x_bottom = title_bar_width - title_bar_button_width * 1.6f;
float pos_y_bottom = title_bar_button_height * 0.28f;
std::string window_restore_button =
"##restore"; // ICON_FA_WINDOW_RESTORE;
if (ImGui::Button(
window_restore_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height))) {
SDL_RestoreWindow(stream_window_);
window_maximized_ = false;
}
ImGui::PopStyleColor(2);
}
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
BUTTON_PADDING);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0, 0, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0, 0, 0.5f));
float xmark_pos_x = ImGui::GetCursorPosX() + 18;
float xmark_pos_y = ImGui::GetCursorPosY() + 16;
float xmark_size = 12.0f;
std::string close_button = "##xmark"; // ICON_FA_XMARK;
if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) {
#if _WIN32
if (enable_minimize_to_tray_) {
tray_->MinimizeToTray();
draw_list->AddRect(ImVec2(pos_x_top, pos_y_top),
ImVec2(pos_x_top + title_bar_button_height * 0.33f,
pos_y_top + title_bar_button_height * 0.33f),
IM_COL32(0, 0, 0, 255));
draw_list->AddRect(ImVec2(pos_x_bottom, pos_y_bottom),
ImVec2(pos_x_bottom + title_bar_button_height * 0.33f,
pos_y_bottom + title_bar_button_height * 0.33f),
IM_COL32(0, 0, 0, 255));
if (ImGui::IsItemHovered()) {
draw_list->AddRectFilled(
ImVec2(pos_x_top + title_bar_button_height * 0.02f,
pos_y_top + title_bar_button_height * 0.01f),
ImVec2(pos_x_top + title_bar_button_height * 0.32f,
pos_y_top + title_bar_button_height * 0.31f),
IM_COL32(229, 229, 229, 255));
} else {
#endif
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
#if _WIN32
draw_list->AddRectFilled(
ImVec2(pos_x_top + title_bar_button_height * 0.02f,
pos_y_top + title_bar_button_height * 0.01f),
ImVec2(pos_x_top + title_bar_button_height * 0.32f,
pos_y_top + title_bar_button_height * 0.31f),
IM_COL32(255, 255, 255, 255));
}
#endif
} else {
float maximize_pos_x = title_bar_width - title_bar_button_width * 1.5f -
title_bar_button_height * 0.165f;
float maximize_pos_y = title_bar_button_height * 0.33f;
std::string window_maximize_button =
"##maximize"; // ICON_FA_SQUARE_FULL;
if (ImGui::Button(
window_maximize_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height))) {
SDL_MaximizeWindow(stream_window_);
window_maximized_ = !window_maximized_;
}
draw_list->AddRect(
ImVec2(maximize_pos_x, maximize_pos_y),
ImVec2(maximize_pos_x + title_bar_button_height * 0.33f,
maximize_pos_y + title_bar_button_height * 0.33f),
IM_COL32(0, 0, 0, 255));
}
draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
xmark_pos_y - xmark_size / 2 + 0.75f),
ImVec2(xmark_pos_x + xmark_size / 2 - 1.5f,
xmark_pos_y + xmark_size / 2 - 0.5f),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(ImVec2(xmark_pos_x + xmark_size / 2 - 1.75f,
xmark_pos_y - xmark_size / 2 + 0.75f),
ImVec2(xmark_pos_x - xmark_size / 2,
xmark_pos_y + xmark_size / 2 - 1.0f),
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(2);
ImGui::PopStyleColor();
ImGui::PopStyleColor(3);
}
ImGui::EndMenuBar();
float xmark_button_pos_x = title_bar_width - title_bar_button_width;
ImGui::SetCursorPos(ImVec2(xmark_button_pos_x, 0.0f));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0, 0, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0, 0, 0.5f));
float xmark_pos_x = xmark_button_pos_x + title_bar_button_width * 0.5f;
float xmark_pos_y = title_bar_button_height * 0.5f;
float xmark_size = title_bar_button_width * 0.33f;
std::string close_button = "##xmark"; // ICON_FA_XMARK;
if (ImGui::Button(close_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height))) {
#if _WIN32
if (enable_minimize_to_tray_) {
tray_->MinimizeToTray();
} else {
#endif
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
#if _WIN32
}
#endif
}
draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
xmark_pos_y - xmark_size / 2 + 0.75f),
ImVec2(xmark_pos_x + xmark_size / 2 - 1.5f,
xmark_pos_y + xmark_size / 2 - 0.5f),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(
ImVec2(xmark_pos_x + xmark_size / 2 - 1.75f,
xmark_pos_y - xmark_size / 2 + 0.75f),
ImVec2(xmark_pos_x - xmark_size / 2, xmark_pos_y + xmark_size / 2 - 1.0f),
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(3);
ImGui::EndChild();
ImGui::PopStyleColor();
return 0;
}
} // namespace crossdesk

View File

@@ -8,8 +8,8 @@
namespace crossdesk {
void Hyperlink(const std::string& label, const std::string& url,
const float window_width) {
void Render::Hyperlink(const std::string& label, const std::string& url,
const float window_width) {
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 0, 255, 255));
ImGui::SetCursorPosX(window_width * 0.1f);
ImGui::Text("%s", label.c_str());
@@ -37,17 +37,18 @@ void Hyperlink(const std::string& label, const std::string& url,
int Render::AboutWindow() {
if (show_about_window_) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
float about_window_width = title_bar_button_width_ * 7.5f;
float about_window_height = latest_version_.empty()
? about_window_height_
: about_window_height_ + 20.0f;
? title_bar_button_width_ * 4.0f
: title_bar_button_width_ * 4.6f;
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(
(viewport->WorkSize.x - viewport->WorkPos.x - about_window_width_) / 2,
(viewport->WorkSize.x - viewport->WorkPos.x - about_window_width) / 2,
(viewport->WorkSize.y - viewport->WorkPos.y - about_window_height) /
2));
ImGui::SetNextWindowSize(ImVec2(about_window_width_, about_window_height));
ImGui::SetNextWindowSize(ImVec2(about_window_width, about_window_height));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
@@ -70,7 +71,7 @@ int Render::AboutWindow() {
std::string text = localization::version[localization_language_index_] +
": CrossDesk " + version;
ImGui::SetCursorPosX(about_window_width_ * 0.1f);
ImGui::SetCursorPosX(about_window_width * 0.1f);
ImGui::Text("%s", text.c_str());
if (update_available_) {
@@ -79,19 +80,19 @@ int Render::AboutWindow() {
": " + latest_version_;
std::string access_website =
localization::access_website[localization_language_index_];
Hyperlink(latest_version, "https://crossdesk.cn", about_window_width_);
Hyperlink(latest_version, "https://crossdesk.cn", about_window_width);
}
ImGui::Text("");
std::string copyright_text = "© 2025 by JUNKUN DI. All rights reserved.";
std::string license_text = "Licensed under GNU LGPL v3.";
ImGui::SetCursorPosX(about_window_width_ * 0.1f);
ImGui::SetCursorPosX(about_window_width * 0.1f);
ImGui::Text("%s", copyright_text.c_str());
ImGui::SetCursorPosX(about_window_width_ * 0.1f);
ImGui::SetCursorPosX(about_window_width * 0.1f);
ImGui::Text("%s", license_text.c_str());
ImGui::SetCursorPosX(about_window_width_ * 0.42f);
ImGui::SetCursorPosX(about_window_width * 0.445f);
ImGui::SetCursorPosY(about_window_height * 0.75f);
// OK
if (ImGui::Button(localization::ok[localization_language_index_].c_str())) {

View File

@@ -7,42 +7,39 @@ namespace crossdesk {
bool Render::ConnectionStatusWindow(
std::shared_ptr<SubStreamWindowProperties>& props) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGuiIO& io = ImGui::GetIO();
bool ret_flag = false;
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
connection_status_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
connection_status_window_height_) /
2));
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
connection_status_window_height_));
ImGui::SetNextWindowPos(
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
ImGui::SetNextWindowSize(
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::Begin("ConnectionStatusWindow", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
auto connection_status_window_width = ImGui::GetWindowSize().x;
auto connection_status_window_height = ImGui::GetWindowSize().y;
ImGui::SetWindowFontScale(0.5f);
std::string text;
if (ConnectionStatus::Connecting == props->connection_status_) {
text = localization::p2p_connecting[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
} else if (ConnectionStatus::Connected == props->connection_status_) {
text = localization::p2p_connected[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
// ok
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
@@ -51,8 +48,8 @@ bool Render::ConnectionStatusWindow(
}
} else if (ConnectionStatus::Disconnected == props->connection_status_) {
text = localization::p2p_disconnected[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
// ok
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
@@ -61,8 +58,8 @@ bool Render::ConnectionStatusWindow(
}
} else if (ConnectionStatus::Failed == props->connection_status_) {
text = localization::p2p_failed[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
// ok
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
@@ -71,8 +68,8 @@ bool Render::ConnectionStatusWindow(
}
} else if (ConnectionStatus::Closed == props->connection_status_) {
text = localization::p2p_closed[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
// ok
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
@@ -87,11 +84,9 @@ bool Render::ConnectionStatusWindow(
text = localization::reinput_password[localization_language_index_];
}
auto window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.4f);
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
ImGui::SetCursorPosX(connection_status_window_width * 0.336f);
ImGui::SetCursorPosY(connection_status_window_height * 0.4f);
ImGui::SetNextItemWidth(connection_status_window_width * 0.33f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
@@ -109,15 +104,16 @@ bool Render::ConnectionStatusWindow(
ImVec2 text_size = ImGui::CalcTextSize(
localization::remember_password[localization_language_index_]
.c_str());
ImGui::SetCursorPosX((window_width - text_size.x) * 0.5f - 13.0f);
ImGui::SetCursorPosX((connection_status_window_width - text_size.x) *
0.45f);
ImGui::Checkbox(
localization::remember_password[localization_language_index_].c_str(),
&(props->remember_password_));
ImGui::SetWindowFontScale(0.5f);
ImGui::PopStyleVar();
ImGui::SetCursorPosX(window_width * 0.315f);
ImGui::SetCursorPosY(window_height * 0.75f);
ImGui::SetCursorPosX(connection_status_window_width * 0.325f);
ImGui::SetCursorPosY(connection_status_window_height * 0.75f);
// ok
if (ImGui::Button(
localization::ok[localization_language_index_].c_str()) ||
@@ -140,14 +136,14 @@ bool Render::ConnectionStatusWindow(
}
} else if (password_validating_) {
text = localization::validate_password[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
}
} else if (ConnectionStatus::NoSuchTransmissionId ==
props->connection_status_) {
text = localization::no_such_id[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
// ok
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
@@ -158,11 +154,9 @@ bool Render::ConnectionStatusWindow(
}
}
auto window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.2f);
ImGui::SetCursorPosX((connection_status_window_width - text_width) * 0.5f);
ImGui::SetCursorPosY(connection_status_window_height * 0.2f);
ImGui::Text("%s", text.c_str());
ImGui::SetWindowFontScale(1.0f);

View File

@@ -53,11 +53,11 @@ int Render::ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props) {
ImVec2(props->control_window_width_, props->control_window_height_),
ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_ + 1), ImGuiCond_Once);
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Once);
float pos_x = 0;
float pos_y = 0;
float y_boundary = fullscreen_button_pressed_ ? 0 : (title_bar_height_ + 1);
float y_boundary = fullscreen_button_pressed_ ? 0 : title_bar_height_;
if (props->reset_control_bar_pos_) {
float new_cursor_pos_x = 0;
@@ -94,7 +94,7 @@ int Render::ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props) {
} else if (!props->reset_control_bar_pos_ &&
ImGui::IsMouseReleased(ImGuiMouseButton_Left) ||
props->control_window_width_is_changing_) {
if (props->control_window_pos_.x <= stream_window_width_ / 2) {
if (props->control_window_pos_.x <= stream_window_width_ * 0.5f) {
if (props->control_window_pos_.y + props->control_window_height_ >
stream_window_height_) {
pos_y = stream_window_height_ - props->control_window_height_;
@@ -118,18 +118,16 @@ int Render::ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props) {
}
}
props->is_control_bar_in_left_ = true;
} else if (props->control_window_pos_.x > stream_window_width_ / 2) {
} else if (props->control_window_pos_.x > stream_window_width_ * 0.5f) {
pos_x = 0;
pos_y =
(props->control_window_pos_.y >= y_boundary &&
props->control_window_pos_.y <=
stream_window_height_ - props->control_window_height_)
? props->control_window_pos_.y
: (props->control_window_pos_.y < (fullscreen_button_pressed_
? 0
: (title_bar_height_ + 1))
? (fullscreen_button_pressed_ ? 0
: (title_bar_height_ + 1))
: (props->control_window_pos_.y <
(fullscreen_button_pressed_ ? 0 : title_bar_height_)
? (fullscreen_button_pressed_ ? 0 : title_bar_height_)
: (stream_window_height_ - props->control_window_height_));
if (props->control_bar_expand_) {
@@ -202,15 +200,13 @@ int Render::ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props) {
: props->control_window_pos_.x,
props->control_window_pos_.y),
ImGuiCond_Always);
ImGui::SetWindowFontScale(0.5f);
std::string control_child_window_title =
props->remote_id_ + "ControlChildWindow";
ImGui::BeginChild(
control_child_window_title.c_str(),
ImVec2(props->control_window_width_ * 2, props->control_window_height_),
ImGuiChildFlags_Border, ImGuiWindowFlags_NoDecoration);
ImGui::SetWindowFontScale(1.0f);
ImGui::BeginChild(control_child_window_title.c_str(),
ImVec2(props->control_window_width_ * 2.0f,
props->control_window_height_),
ImGuiChildFlags_Border, ImGuiWindowFlags_NoDecoration);
ImGui::PopStyleColor();
ControlBar(props);

View File

@@ -6,31 +6,38 @@
namespace crossdesk {
int Render::SettingWindow() {
ImGuiIO& io = ImGui::GetIO();
if (show_settings_window_) {
if (settings_window_pos_reset_) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
#if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \
!defined(__arm__) && USE_CUDA) || \
defined(__APPLE__))
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SETTINGS_WINDOW_WIDTH_CN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SETTINGS_WINDOW_HEIGHT_CN) /
2));
ImVec2(io.DisplaySize.x * 0.343f, io.DisplaySize.y * 0.07f));
ImGui::SetNextWindowSize(
ImVec2(SETTINGS_WINDOW_WIDTH_CN, SETTINGS_WINDOW_HEIGHT_CN));
ImVec2(io.DisplaySize.x * 0.315f, io.DisplaySize.y * 0.85f));
#else
ImGui::SetNextWindowPos(
ImVec2(io.DisplaySize.x * 0.343f, io.DisplaySize.y * 0.1f));
ImGui::SetNextWindowSize(
ImVec2(io.DisplaySize.x * 0.315f, io.DisplaySize.y * 0.8f));
#endif
} else {
#if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \
!defined(__arm__) && USE_CUDA) || \
defined(__APPLE__))
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SETTINGS_WINDOW_WIDTH_EN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SETTINGS_WINDOW_HEIGHT_EN) /
2));
ImVec2(io.DisplaySize.x * 0.297f, io.DisplaySize.y * 0.07f));
ImGui::SetNextWindowSize(
ImVec2(SETTINGS_WINDOW_WIDTH_EN, SETTINGS_WINDOW_HEIGHT_EN));
ImVec2(io.DisplaySize.x * 0.407f, io.DisplaySize.y * 0.85f));
#else
ImGui::SetNextWindowPos(
ImVec2(io.DisplaySize.x * 0.297f, io.DisplaySize.y * 0.1f));
ImGui::SetNextWindowSize(
ImVec2(io.DisplaySize.x * 0.407f, io.DisplaySize.y * 0.8f));
#endif
}
settings_window_pos_reset_ = false;
@@ -38,10 +45,9 @@ int Render::SettingWindow() {
// Settings
{
static int settings_items_padding = 30;
static int settings_items_padding = title_bar_button_width_ * 0.75f;
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);
@@ -50,7 +56,6 @@ int Render::SettingWindow() {
nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoSavedSettings);
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
{
@@ -59,19 +64,29 @@ int Render::SettingWindow() {
localization::language_en[localization_language_index_].c_str()};
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text(
"%s", localization::language[localization_language_index_].c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 3.0f);
} else {
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.5f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##language", &language_button_value_, language_items,
IM_ARRAYSIZE(language_items));
ImGui::SetNextItemWidth(title_bar_button_width_ * 1.8f);
if (ImGui::BeginCombo("##language",
language_items[language_button_value_])) {
ImGui::SetWindowFontScale(0.5f);
for (int i = 0; i < IM_ARRAYSIZE(language_items); i++) {
bool selected = (i == language_button_value_);
if (ImGui::Selectable(language_items[i], selected))
language_button_value_ = i;
}
ImGui::EndCombo();
}
}
ImGui::Separator();
@@ -90,21 +105,31 @@ int Render::SettingWindow() {
.c_str()};
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text(
"%s",
localization::video_quality[localization_language_index_].c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 3.0f);
} else {
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.5f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##video_quality", &video_quality_button_value_,
video_quality_items, IM_ARRAYSIZE(video_quality_items));
ImGui::SetNextItemWidth(title_bar_button_width_ * 1.8f);
if (ImGui::BeginCombo(
"##video_quality",
video_quality_items[video_quality_button_value_])) {
ImGui::SetWindowFontScale(0.5f);
for (int i = 0; i < IM_ARRAYSIZE(video_quality_items); i++) {
bool selected = (i == video_quality_button_value_);
if (ImGui::Selectable(video_quality_items[i], selected))
video_quality_button_value_ = i;
}
ImGui::EndCombo();
}
}
ImGui::Separator();
@@ -113,22 +138,31 @@ int Render::SettingWindow() {
const char* video_frame_rate_items[] = {"30 fps", "60 fps"};
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s",
localization::video_frame_rate[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 3.0f);
} else {
ImGui::SetCursorPosX(VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.5f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##video_frame_rate", &video_frame_rate_button_value_,
video_frame_rate_items,
IM_ARRAYSIZE(video_frame_rate_items));
ImGui::SetNextItemWidth(title_bar_button_width_ * 1.8f);
if (ImGui::BeginCombo(
"##video_frame_rate",
video_frame_rate_items[video_frame_rate_button_value_])) {
ImGui::SetWindowFontScale(0.5f);
for (int i = 0; i < IM_ARRAYSIZE(video_frame_rate_items); i++) {
bool selected = (i == video_frame_rate_button_value_);
if (ImGui::Selectable(video_frame_rate_items[i], selected))
video_frame_rate_button_value_ = i;
}
ImGui::EndCombo();
}
}
ImGui::Separator();
@@ -139,41 +173,53 @@ int Render::SettingWindow() {
localization::av1[localization_language_index_].c_str()};
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text(
"%s",
localization::video_encode_format[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 3.0f);
} else {
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.5f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo(
"##video_encode_format", &video_encode_format_button_value_,
video_encode_format_items, IM_ARRAYSIZE(video_encode_format_items));
ImGui::SetNextItemWidth(title_bar_button_width_ * 1.8f);
if (ImGui::BeginCombo(
"##video_encode_format",
video_encode_format_items[video_encode_format_button_value_])) {
ImGui::SetWindowFontScale(0.5f);
for (int i = 0; i < IM_ARRAYSIZE(video_encode_format_items); i++) {
bool selected = (i == video_encode_format_button_value_);
if (ImGui::Selectable(video_encode_format_items[i], selected))
video_encode_format_button_value_ = i;
}
ImGui::EndCombo();
}
}
#if USE_CUDA && !defined(__aarch64__) && !defined(__arm__)
#if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \
!defined(__arm__) && USE_CUDA) || \
defined(__APPLE__))
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", localization::enable_hardware_video_codec
[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
} else {
ImGui::SetCursorPosX(ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 5.755f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_hardware_video_codec",
&enable_hardware_video_codec_);
}
@@ -183,17 +229,18 @@ int Render::SettingWindow() {
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text(
"%s",
localization::enable_turn[localization_language_index_].c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
} else {
ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 5.755f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_turn", &enable_turn_);
}
@@ -201,17 +248,18 @@ int Render::SettingWindow() {
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text(
"%s",
localization::enable_srtp[localization_language_index_].c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_SRTP_CHECKBOX_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
} else {
ImGui::SetCursorPosX(ENABLE_SRTP_CHECKBOX_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 5.755f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_srtp", &enable_srtp_);
}
@@ -220,19 +268,19 @@ int Render::SettingWindow() {
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 1);
ImGui::AlignTextToFramePadding();
if (ImGui::Button(localization::self_hosted_server_config
[localization_language_index_]
.c_str())) {
show_self_hosted_server_config_window_ = true;
}
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
} else {
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 5.755f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_self_hosted", &enable_self_hosted_);
}
@@ -240,18 +288,18 @@ int Render::SettingWindow() {
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s",
localization::enable_autostart[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_AUTOSTART_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
} else {
ImGui::SetCursorPosX(ENABLE_AUTOSTART_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 5.755f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_autostart_", &enable_autostart_);
}
@@ -259,18 +307,18 @@ int Render::SettingWindow() {
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text(
"%s",
localization::enable_daemon[localization_language_index_].c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_DAEMON_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
} else {
ImGui::SetCursorPosX(ENABLE_DAEMON_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 5.755f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_daemon_", &enable_daemon_);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
@@ -287,18 +335,18 @@ int Render::SettingWindow() {
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s",
localization::minimize_to_tray[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_MINIZE_TO_TRAY_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
} else {
ImGui::SetCursorPosX(ENABLE_MINIZE_TO_TRAY_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 5.755f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_minimize_to_tray_",
&enable_minimize_to_tray_);
}
@@ -308,13 +356,15 @@ int Render::SettingWindow() {
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 1.59f);
} else {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 2.22f);
}
settings_items_offset += settings_items_padding + 10;
settings_items_offset +=
settings_items_padding + title_bar_button_width_ * 0.3f;
ImGui::SetCursorPosY(settings_items_offset);
ImGui::PopStyleVar();
// OK
@@ -468,12 +518,10 @@ int Render::SettingWindow() {
settings_window_pos_reset_ = true;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::End();
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
}
}

View File

@@ -1,3 +1,4 @@
#include "layout_relative.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
@@ -5,25 +6,30 @@
namespace crossdesk {
int Render::MainWindow() {
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Always);
ImGuiIO& io = ImGui::GetIO();
float local_remote_window_width = io.DisplaySize.x;
float local_remote_window_height =
io.DisplaySize.y * (0.56f - TITLE_BAR_HEIGHT);
ImGui::SetNextWindowPos(ImVec2(0.0f, io.DisplaySize.y * (TITLE_BAR_HEIGHT)),
ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::BeginChild("DeskWindow",
ImVec2(main_window_width_default_, local_window_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::BeginChild(
"DeskWindow",
ImVec2(local_remote_window_width, local_remote_window_height),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
LocalWindow();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddLine(
ImVec2(main_window_width_default_ / 2, title_bar_height_ + 15.0f),
ImVec2(main_window_width_default_ / 2, title_bar_height_ + 225.0f),
IM_COL32(0, 0, 0, 122), 1.0f);
draw_list->AddLine(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.1f),
ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.53f),
IM_COL32(0, 0, 0, 122), 1.0f);
RemoteWindow();
ImGui::EndChild();
@@ -32,6 +38,7 @@ int Render::MainWindow() {
StatusBar();
if (show_connection_status_window_) {
// std::unique_lock lock(client_properties_mutex_);
for (auto it = client_properties_.begin();
it != client_properties_.end();) {
auto& props = it->second;

View File

@@ -0,0 +1,205 @@
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
#include <ApplicationServices/ApplicationServices.h>
#include <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>
#include <unistd.h>
#include <cstdlib>
namespace crossdesk {
bool Render::DrawToggleSwitch(const char* id, bool active, bool enabled) {
const float TRACK_HEIGHT = ImGui::GetFrameHeight();
const float TRACK_WIDTH = TRACK_HEIGHT * 1.8f;
const float TRACK_RADIUS = TRACK_HEIGHT * 0.5f;
const float KNOB_PADDING = 2.0f;
const float KNOB_HEIGHT = TRACK_HEIGHT - 4.0f;
const float KNOB_WIDTH = KNOB_HEIGHT * 1.2f;
const float KNOB_RADIUS = KNOB_HEIGHT * 0.5f;
const float DISABLED_ALPHA = 0.6f;
const float KNOB_ALPHA_DISABLED = 0.9f;
const ImVec4 COLOR_ACTIVE = ImVec4(0.0f, 0.0f, 1.0f, 1.0f);
const ImVec4 COLOR_ACTIVE_HOVER = ImVec4(0.26f, 0.59f, 0.98f, 1.0f);
const ImVec4 COLOR_INACTIVE = ImVec4(0.60f, 0.60f, 0.60f, 1.0f);
const ImVec4 COLOR_INACTIVE_HOVER = ImVec4(0.70f, 0.70f, 0.70f, 1.0f);
const ImVec4 COLOR_KNOB = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 track_pos = ImGui::GetCursorScreenPos();
ImGui::InvisibleButton(id, ImVec2(TRACK_WIDTH, TRACK_HEIGHT));
bool hovered = ImGui::IsItemHovered();
bool clicked = ImGui::IsItemClicked() && enabled;
ImVec4 track_color = active ? (hovered && enabled ? COLOR_ACTIVE_HOVER : COLOR_ACTIVE)
: (hovered && enabled ? COLOR_INACTIVE_HOVER : COLOR_INACTIVE);
if (!enabled) {
track_color.w *= DISABLED_ALPHA;
}
ImVec2 track_min = ImVec2(track_pos.x, track_pos.y + 0.5f);
ImVec2 track_max = ImVec2(track_pos.x + TRACK_WIDTH, track_pos.y + TRACK_HEIGHT - 0.5f);
draw_list->AddRectFilled(track_min, track_max, ImGui::GetColorU32(track_color), TRACK_RADIUS);
float knob_position = active ? 1.0f : 0.0f;
float knob_min_x = track_pos.x + KNOB_PADDING;
float knob_max_x = track_pos.x + TRACK_WIDTH - KNOB_WIDTH - KNOB_PADDING;
float knob_x = knob_min_x + knob_position * (knob_max_x - knob_min_x);
float knob_y = track_pos.y + (TRACK_HEIGHT - KNOB_HEIGHT) * 0.5f;
ImVec4 knob_color = COLOR_KNOB;
if (!enabled) {
knob_color.w = KNOB_ALPHA_DISABLED;
}
ImVec2 knob_min = ImVec2(knob_x, knob_y);
ImVec2 knob_max = ImVec2(knob_x + KNOB_WIDTH, knob_y + KNOB_HEIGHT);
draw_list->AddRectFilled(knob_min, knob_max, ImGui::GetColorU32(knob_color), KNOB_RADIUS);
return clicked;
}
bool Render::CheckScreenRecordingPermission() {
// CGPreflightScreenCaptureAccess is available on macOS 10.15+
if (@available(macOS 10.15, *)) {
bool granted = CGPreflightScreenCaptureAccess();
return granted;
}
// for older macOS versions, assume permission is granted
return true;
}
bool Render::CheckAccessibilityPermission() {
NSDictionary* options = @{(__bridge id)kAXTrustedCheckOptionPrompt : @NO};
bool trusted = AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)options);
return trusted;
}
void Render::OpenAccessibilityPreferences() {
NSDictionary* options = @{(__bridge id)kAXTrustedCheckOptionPrompt : @YES};
AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)options);
system("open "
"\"x-apple.systempreferences:com.apple.preference.security?Privacy_"
"Accessibility\"");
}
void Render::OpenScreenRecordingPreferences() {
if (@available(macOS 10.15, *)) {
CGRequestScreenCaptureAccess();
}
system("open "
"\"x-apple.systempreferences:com.apple.preference.security?Privacy_"
"ScreenCapture\"");
}
int Render::RequestPermissionWindow() {
bool screen_recording_granted = CheckScreenRecordingPermission();
bool accessibility_granted = CheckAccessibilityPermission();
show_request_permission_window_ = !screen_recording_granted || !accessibility_granted;
if (!show_request_permission_window_) {
return 0;
}
const ImGuiViewport* viewport = ImGui::GetMainViewport();
float window_width = localization_language_index_ == 0 ? REQUEST_PERMISSION_WINDOW_WIDTH_CN
: REQUEST_PERMISSION_WINDOW_WIDTH_EN;
float window_height = localization_language_index_ == 0 ? REQUEST_PERMISSION_WINDOW_HEIGHT_CN
: REQUEST_PERMISSION_WINDOW_HEIGHT_EN;
float checkbox_padding = localization_language_index_ == 0
? REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_CN
: REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_EN;
ImVec2 center_pos = ImVec2((viewport->WorkSize.x - window_width) * 0.5f + viewport->WorkPos.x,
(viewport->WorkSize.y - window_height) * 0.5f + viewport->WorkPos.y);
ImGui::SetNextWindowPos(center_pos, ImGuiCond_Once);
ImGui::SetNextWindowSize(ImVec2(window_width, window_height), ImGuiCond_Always);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin(
localization::request_permissions[localization_language_index_].c_str(), nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings);
ImGui::SetWindowFontScale(0.3f);
// use system font
if (main_windows_system_chinese_font_ != nullptr) {
ImGui::PushFont(main_windows_system_chinese_font_);
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetTextLineHeight() + 5.0f);
ImGui::SetCursorPosX(10.0f);
ImGui::TextWrapped(
"%s", localization::permission_required_message[localization_language_index_].c_str());
ImGui::Spacing();
ImGui::Spacing();
ImGui::Spacing();
// accessibility permission
ImGui::SetCursorPosX(10.0f);
ImGui::AlignTextToFramePadding();
ImGui::Text("1. %s:",
localization::accessibility_permission[localization_language_index_].c_str());
ImGui::SameLine();
ImGui::AlignTextToFramePadding();
ImGui::SetCursorPosX(checkbox_padding);
if (accessibility_granted) {
DrawToggleSwitch("accessibility_toggle_on", true, false);
} else {
if (DrawToggleSwitch("accessibility_toggle", accessibility_granted, !accessibility_granted)) {
OpenAccessibilityPreferences();
}
}
ImGui::Spacing();
// screen recording permission
ImGui::SetCursorPosX(10.0f);
ImGui::AlignTextToFramePadding();
ImGui::Text("2. %s:",
localization::screen_recording_permission[localization_language_index_].c_str());
ImGui::SameLine();
ImGui::AlignTextToFramePadding();
ImGui::SetCursorPosX(checkbox_padding);
if (screen_recording_granted) {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.0f);
DrawToggleSwitch("screen_recording_toggle_on", true, false);
} else {
if (DrawToggleSwitch("screen_recording_toggle", screen_recording_granted,
!screen_recording_granted)) {
OpenScreenRecordingPreferences();
}
}
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.45f);
// pop system font
if (main_windows_system_chinese_font_ != nullptr) {
ImGui::PopFont();
}
ImGui::End();
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleVar(4);
ImGui::PopStyleColor();
return 0;
}
} // namespace crossdesk

View File

@@ -54,12 +54,13 @@ int Render::ShowSimpleFileBrowser() {
if (show_file_browser_) {
ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
float fixed_width = 130.0f;
float fixed_width = title_bar_button_width_ * 3.8f;
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)) {
ImGui::SetWindowFontScale(0.5f);
bool file_selected = false;
auto roots = GetRootEntries();
@@ -120,33 +121,19 @@ int Render::ShowSimpleFileBrowser() {
}
int Render::SelfHostedServerWindow() {
ImGuiIO& io = ImGui::GetIO();
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));
ImVec2(io.DisplaySize.x * 0.298f, io.DisplaySize.y * 0.25f));
ImGui::SetNextWindowSize(
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN,
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN));
ImVec2(io.DisplaySize.x * 0.407f, io.DisplaySize.y * 0.41f));
} 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));
ImVec2(io.DisplaySize.x * 0.27f, io.DisplaySize.y * 0.3f));
ImGui::SetNextWindowSize(
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN,
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN));
ImVec2(io.DisplaySize.x * 0.465f, io.DisplaySize.y * 0.41f));
}
self_hosted_server_config_window_pos_reset_ = false;
@@ -154,9 +141,6 @@ int Render::SelfHostedServerWindow() {
// 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);
@@ -172,18 +156,17 @@ int Render::SelfHostedServerWindow() {
ImGui::SetWindowFontScale(0.5f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", localization::self_hosted_server_address
[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 2.5f);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 3.43f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::SetNextItemWidth(title_bar_button_width_ * 3.8f);
ImGui::InputText("##signal_server_ip_self_", signal_server_ip_self_,
IM_ARRAYSIZE(signal_server_ip_self_),
@@ -193,20 +176,18 @@ int Render::SelfHostedServerWindow() {
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::AlignTextToFramePadding();
ImGui::Text(
"%s",
localization::self_hosted_server_port[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 2.5f);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 3.43f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::SetNextItemWidth(title_bar_button_width_ * 3.8f);
ImGui::InputText("##signal_server_port_self_", signal_server_port_self_,
IM_ARRAYSIZE(signal_server_port_self_));
@@ -215,19 +196,17 @@ int Render::SelfHostedServerWindow() {
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", localization::self_hosted_server_coturn_server_port
[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 2.5f);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 3.43f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::SetNextItemWidth(title_bar_button_width_ * 3.8f);
ImGui::InputText("##coturn_server_port_self_", coturn_server_port_self_,
IM_ARRAYSIZE(coturn_server_port_self_));
@@ -236,19 +215,17 @@ int Render::SelfHostedServerWindow() {
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", localization::self_hosted_server_certificate_path
[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 2.5f);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 3.43f);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::SetNextItemWidth(title_bar_button_width_ * 3.8f);
ShowSimpleFileBrowser();
}
@@ -257,14 +234,14 @@ int Render::SelfHostedServerWindow() {
ImGui::EndDisabled();
}
ImGui::Dummy(ImVec2(0.0f, title_bar_button_width_ * 0.25f));
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN);
ImGui::SetCursorPosX(title_bar_button_width_ * 2.32f);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN);
ImGui::SetCursorPosX(title_bar_button_width_ * 2.7f);
}
settings_items_offset += settings_items_padding + 10;
ImGui::SetCursorPosY(settings_items_offset);
ImGui::PopStyleVar();
// OK
@@ -298,16 +275,27 @@ int Render::SelfHostedServerWindow() {
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_,
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_, 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();
int signal_port = config_center_->GetSignalServerPort();
if (signal_port > 0) {
strncpy(signal_server_port_self_, std::to_string(signal_port).c_str(),
sizeof(signal_server_port_self_) - 1);
signal_server_port_self_[sizeof(signal_server_port_self_) - 1] = '\0';
} else {
signal_server_port_self_[0] = '\0';
}
int coturn_port = config_center_->GetCoturnServerPort();
if (coturn_port > 0) {
strncpy(coturn_server_port_self_, std::to_string(coturn_port).c_str(),
sizeof(coturn_server_port_self_) - 1);
coturn_server_port_self_[sizeof(coturn_server_port_self_) - 1] = '\0';
} else {
coturn_server_port_self_[0] = '\0';
}
tls_cert_path_self_ = config_center_->GetCertFilePath();
}
ImGui::SetWindowFontScale(1.0f);

View File

@@ -32,12 +32,15 @@ void Render::DrawConnectionStatusText(
}
void Render::CloseTab(decltype(client_properties_)::iterator& it) {
CleanupPeer(it->second);
it = client_properties_.erase(it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
// std::unique_lock lock(client_properties_mutex_);
if (it != client_properties_.end()) {
CleanupPeer(it->second);
it = client_properties_.erase(it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
}
}
}
@@ -50,21 +53,28 @@ int Render::StreamWindow() {
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
ImGui::Begin("VideoBg", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoDocking);
bool video_bg_opened = ImGui::Begin(
"VideoBg", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoDocking);
ImGui::PopStyleColor(2);
ImGui::PopStyleVar();
if (!video_bg_opened) {
return 0;
}
ImGuiWindowFlags stream_window_flag =
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoMove;
if (!fullscreen_button_pressed_) {
ImGui::SetNextWindowPos(ImVec2(20, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(0, 20), ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 8.0f));
ImGui::SetNextWindowPos(
ImVec2(title_bar_button_width_ * 0.8f, title_bar_button_width_ * 0.1f),
ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(0, title_bar_button_width_ * 0.8f),
ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0.0f));
ImGui::Begin("TabBar", nullptr,
@@ -79,22 +89,32 @@ int Render::StreamWindow() {
ImGuiTabBarFlags_AutoSelectNewTabs)) {
is_tab_bar_hovered_ = ImGui::IsWindowHovered();
// std::shared_lock lock(client_properties_mutex_);
for (auto it = client_properties_.begin();
it != client_properties_.end();) {
auto& props = it->second;
if (!props->tab_opened_) {
CloseTab(it);
std::string remote_id_to_close = props->remote_id_;
// lock.unlock();
{
// std::unique_lock unique_lock(client_properties_mutex_);
auto close_it = client_properties_.find(remote_id_to_close);
if (close_it != client_properties_.end()) {
CloseTab(close_it);
}
}
// lock.lock();
it = client_properties_.begin();
continue;
}
ImGui::SetWindowFontScale(0.6f);
std::string tab_label =
enable_srtp_
? std::string(ICON_FA_SHIELD_HALVED) + " " + props->remote_id_
: props->remote_id_;
if (ImGui::BeginTabItem(tab_label.c_str(), &props->tab_opened_)) {
props->tab_selected_ = true;
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.6f);
ImGui::SetNextWindowSize(
ImVec2(stream_window_width_, stream_window_height_),
@@ -122,12 +142,25 @@ int Render::StreamWindow() {
focused_remote_id_ = props->remote_id_;
if (!props->peer_) {
it = client_properties_.erase(it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
std::string remote_id_to_erase = props->remote_id_;
// lock.unlock();
{
// std::unique_lock unique_lock(client_properties_mutex_);
auto erase_it = client_properties_.find(remote_id_to_erase);
if (erase_it != client_properties_.end()) {
erase_it = client_properties_.erase(erase_it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
}
}
}
// lock.lock();
ImGui::End();
ImGui::EndTabItem();
it = client_properties_.begin();
continue;
} else {
DrawConnectionStatusText(props);
++it;
@@ -137,7 +170,20 @@ int Render::StreamWindow() {
ImGui::EndTabItem();
} else {
props->tab_selected_ = false;
ImGui::SetWindowFontScale(1.0f);
if (!props->tab_opened_) {
std::string remote_id_to_close = props->remote_id_;
// lock.unlock();
{
// std::unique_lock unique_lock(client_properties_mutex_);
auto close_it = client_properties_.find(remote_id_to_close);
if (close_it != client_properties_.end()) {
CloseTab(close_it);
}
}
// lock.lock();
it = client_properties_.begin();
continue;
}
++it;
}
}
@@ -147,11 +193,22 @@ int Render::StreamWindow() {
ImGui::End(); // End TabBar
} else {
// std::shared_lock lock(client_properties_mutex_);
for (auto it = client_properties_.begin();
it != client_properties_.end();) {
auto& props = it->second;
if (!props->tab_opened_) {
CloseTab(it);
std::string remote_id_to_close = props->remote_id_;
// lock.unlock();
{
// std::unique_lock unique_lock(client_properties_mutex_);
auto close_it = client_properties_.find(remote_id_to_close);
if (close_it != client_properties_.end()) {
CloseTab(close_it);
}
}
// lock.lock();
it = client_properties_.begin();
continue;
}
@@ -181,12 +238,23 @@ int Render::StreamWindow() {
if (!props->peer_) {
fullscreen_button_pressed_ = false;
SDL_SetWindowFullscreen(stream_window_, false);
it = client_properties_.erase(it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
std::string remote_id_to_erase = props->remote_id_;
// lock.unlock();
{
// std::unique_lock unique_lock(client_properties_mutex_);
auto erase_it = client_properties_.find(remote_id_to_erase);
if (erase_it != client_properties_.end()) {
client_properties_.erase(erase_it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
}
}
}
// lock.lock();
it = client_properties_.begin();
continue;
} else {
DrawConnectionStatusText(props);
++it;

View File

@@ -0,0 +1,214 @@
#include <algorithm>
#include <cstdlib>
#include <string>
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
namespace crossdesk {
std::string CleanMarkdown(const std::string& markdown) {
std::string result = markdown;
// remove # title mark
size_t pos = 0;
while (pos < result.length()) {
if (result[pos] == '\n' || pos == 0) {
size_t line_start = (result[pos] == '\n') ? pos + 1 : pos;
if (line_start < result.length() && result[line_start] == '#') {
size_t hash_end = line_start;
while (hash_end < result.length() &&
(result[hash_end] == '#' || result[hash_end] == ' ')) {
hash_end++;
}
result.erase(line_start, hash_end - line_start);
pos = line_start;
continue;
}
}
pos++;
}
// remove ** bold mark
pos = 0;
while ((pos = result.find("**", pos)) != std::string::npos) {
result.erase(pos, 2);
}
// remove all spaces
result.erase(std::remove(result.begin(), result.end(), ' '), result.end());
// replace . with 、
pos = 0;
while ((pos = result.find('.', pos)) != std::string::npos) {
result.replace(pos, 1, "");
pos += 1; // Move to next position after the replacement
}
return result;
}
int Render::UpdateNotificationWindow() {
if (show_update_notification_window_ && update_available_) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
float update_notification_window_width = title_bar_button_width_ * 10.0f;
float update_notification_window_height = title_bar_button_width_ * 8.0f;
// #ifdef __APPLE__
// float font_scale = 0.3f;
// #else
// float font_scale = 0.5f;
// #endif
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
update_notification_window_width) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
update_notification_window_height) /
2),
ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(update_notification_window_width,
update_notification_window_height));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::Begin(
localization::notification[localization_language_index_].c_str(),
nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoTitleBar);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
update_notification_window_height * 0.05f);
// title: new version available
ImGui::SetCursorPosX(update_notification_window_width * 0.1f);
ImGui::SetWindowFontScale(0.55f);
std::string title =
localization::new_version_available[localization_language_index_] +
": v" + latest_version_;
ImGui::Text("%s", title.c_str());
ImGui::SetWindowFontScale(0.1f);
ImGui::Spacing();
// website link
std::string download_text =
localization::access_website[localization_language_index_] +
"https://crossdesk.cn";
ImGui::SetWindowFontScale(0.5f);
Hyperlink(download_text, "https://crossdesk.cn",
update_notification_window_width);
ImGui::SetWindowFontScale(1.0f);
ImGui::Spacing();
float scrollable_height =
update_notification_window_height - UPDATE_NOTIFICATION_RESERVED_HEIGHT;
if (main_windows_system_chinese_font_ != nullptr) {
ImGui::PushFont(main_windows_system_chinese_font_);
}
// scrollable content area
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
ImGui::BeginChild(
"ScrollableContent",
ImVec2(update_notification_window_width * 0.9f, scrollable_height),
ImGuiChildFlags_Border, ImGuiWindowFlags_None);
ImGui::SetWindowFontScale(0.5f);
// set text wrap position to current available width (accounts for
// scrollbar)
float wrap_pos = ImGui::GetContentRegionAvail().x;
ImGui::PushTextWrapPos(wrap_pos);
// release name
if (latest_version_info_.contains("releaseName") &&
latest_version_info_["releaseName"].is_string() &&
!latest_version_info_["releaseName"].empty()) {
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
std::string release_name =
latest_version_info_["releaseName"].get<std::string>();
ImGui::TextWrapped("%s", release_name.c_str());
ImGui::Spacing();
}
// release notes
if (!release_notes_.empty()) {
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
std::string cleaned_notes = CleanMarkdown(release_notes_);
ImGui::TextWrapped("%s", cleaned_notes.c_str());
ImGui::Spacing();
}
// release date
if (latest_version_info_.contains("releaseDate") &&
latest_version_info_["releaseDate"].is_string() &&
!latest_version_info_["releaseDate"].empty()) {
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
std::string date_label =
localization::release_date[localization_language_index_];
std::string release_date = latest_version_info_["releaseDate"];
std::string date_text = date_label + release_date;
ImGui::Text("%s", date_text.c_str());
ImGui::Spacing();
}
// pop text wrap position
ImGui::PopTextWrapPos();
ImGui::EndChild();
// pop system font
if (main_windows_system_chinese_font_ != nullptr) {
ImGui::PopFont();
}
ImGui::Spacing();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(update_notification_window_width * 0.407f);
} else {
ImGui::SetCursorPosX(update_notification_window_width * 0.367f);
}
ImGui::SetWindowFontScale(0.5f);
// update button
if (ImGui::Button(
localization::update[localization_language_index_].c_str())) {
// open download page
std::string url = "https://crossdesk.cn";
#if defined(_WIN32)
std::string cmd = "start " + url;
#elif defined(__APPLE__)
std::string cmd = "open " + url;
#else
std::string cmd = "xdg-open " + url;
#endif
system(cmd.c_str());
show_update_notification_window_ = false;
}
ImGui::SameLine();
if (ImGui::Button(
localization::cancel[localization_language_index_].c_str())) {
show_update_notification_window_ = false;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::End();
ImGui::PopStyleVar(3);
ImGui::PopStyleColor();
}
return 0;
}
} // namespace crossdesk

View File

@@ -36,7 +36,7 @@ std::filesystem::path PathManager::GetLogPath() {
#elif __APPLE__
return GetHome() + "/Library/Logs/" + app_name_;
#else
return GetCachePath() / app_name_ / "logs";
return GetCachePath() / "logs";
#endif
}

View File

@@ -1,5 +1,10 @@
#include "screen_capturer_x11.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/Xrandr.h>
#include <chrono>
#include <thread>
@@ -100,6 +105,7 @@ int ScreenCapturerX11::Destroy() {
int ScreenCapturerX11::Start(bool show_cursor) {
if (running_) return 0;
show_cursor_ = show_cursor;
running_ = true;
paused_ = false;
thread_ = std::thread([this]() {
@@ -156,6 +162,20 @@ void ScreenCapturerX11::OnFrame() {
AllPlanes, ZPixmap);
if (!image) return;
// if enable show cursor, draw cursor
if (show_cursor_) {
Window root_return, child_return;
int root_x, root_y, win_x, win_y;
unsigned int mask;
if (XQueryPointer(display_, root_, &root_return, &child_return, &root_x,
&root_y, &win_x, &win_y, &mask)) {
if (root_x >= left_ && root_x < left_ + width_ && root_y >= top_ &&
root_y < top_ + height_) {
DrawCursor(image, root_x - left_, root_y - top_);
}
}
}
bool needs_copy = image->bytes_per_line != width_ * 4;
std::vector<uint8_t> argb_buf;
uint8_t* src_argb = nullptr;
@@ -186,4 +206,82 @@ void ScreenCapturerX11::OnFrame() {
XDestroyImage(image);
}
void ScreenCapturerX11::DrawCursor(XImage* image, int x, int y) {
if (!display_ || !image) {
return;
}
// check XFixes extension
int event_base, error_base;
if (!XFixesQueryExtension(display_, &event_base, &error_base)) {
return;
}
XFixesCursorImage* cursor_image = XFixesGetCursorImage(display_);
if (!cursor_image) {
return;
}
int cursor_width = cursor_image->width;
int cursor_height = cursor_image->height;
int draw_x = x - cursor_image->xhot;
int draw_y = y - cursor_image->yhot;
// draw cursor on image
for (int cy = 0; cy < cursor_height; ++cy) {
for (int cx = 0; cx < cursor_width; ++cx) {
int img_x = draw_x + cx;
int img_y = draw_y + cy;
if (img_x < 0 || img_x >= image->width || img_y < 0 ||
img_y >= image->height) {
continue;
}
unsigned long cursor_pixel = cursor_image->pixels[cy * cursor_width + cx];
unsigned char a = (cursor_pixel >> 24) & 0xFF;
// if alpha is 0, skip
if (a == 0) {
continue;
}
unsigned long img_pixel = XGetPixel(image, img_x, img_y);
unsigned char img_r = (img_pixel >> 16) & 0xFF;
unsigned char img_g = (img_pixel >> 8) & 0xFF;
unsigned char img_b = img_pixel & 0xFF;
unsigned char cursor_r = (cursor_pixel >> 16) & 0xFF;
unsigned char cursor_g = (cursor_pixel >> 8) & 0xFF;
unsigned char cursor_b = cursor_pixel & 0xFF;
// alpha mix
unsigned char final_r, final_g, final_b;
if (a == 255) {
// if alpha is 255, use cursor color
final_r = cursor_r;
final_g = cursor_g;
final_b = cursor_b;
} else {
float alpha = a / 255.0f;
float inv_alpha = 1.0f - alpha;
final_r =
static_cast<unsigned char>(cursor_r * alpha + img_r * inv_alpha);
final_g =
static_cast<unsigned char>(cursor_g * alpha + img_g * inv_alpha);
final_b =
static_cast<unsigned char>(cursor_b * alpha + img_b * inv_alpha);
}
// set pixel
unsigned long new_pixel = (final_r << 16) | (final_g << 8) | final_b;
XPutPixel(image, img_x, img_y, new_pixel);
}
}
XFree(cursor_image);
}
} // namespace crossdesk

View File

@@ -7,10 +7,14 @@
#ifndef _SCREEN_CAPTURER_X11_H_
#define _SCREEN_CAPTURER_X11_H_
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xfixes.h>
// forward declarations for X11 types
struct _XDisplay;
typedef struct _XDisplay Display;
typedef unsigned long Window;
struct _XRRScreenResources;
typedef struct _XRRScreenResources XRRScreenResources;
struct _XImage;
typedef struct _XImage XImage;
#include <atomic>
#include <cstring>
@@ -43,6 +47,9 @@ class ScreenCapturerX11 : public ScreenCapturer {
void OnFrame();
private:
void DrawCursor(XImage* image, int x, int y);
private:
Display* display_ = nullptr;
Window root_ = 0;
@@ -60,12 +67,8 @@ class ScreenCapturerX11 : public ScreenCapturer {
cb_desktop_data callback_;
std::vector<DisplayInfo> display_info_list_;
// 缓冲区
std::vector<uint8_t> y_plane_;
std::vector<uint8_t> uv_plane_;
// 鼠标光标相关
void DrawCursor(XImage* image, int x, int y);
};
} // namespace crossdesk
#endif

View File

@@ -196,14 +196,26 @@ ScreenCapturerSckImpl::~ScreenCapturerSckImpl() {
int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) {
_on_data = cb;
fps_ = fps;
if (@available(macOS 10.15, *)) {
bool has_permission = CGPreflightScreenCaptureAccess();
if (!has_permission) {
LOG_ERROR("Screen recording permission not granted");
return -1;
}
}
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
__block SCShareableContent *content = nil;
__block NSError *capture_error = nil;
[SCShareableContent
getShareableContentWithCompletionHandler:^(SCShareableContent *result, NSError *error) {
if (error) {
NSLog(@"Failed to get shareable content: %@", error);
capture_error = error;
LOG_ERROR("Failed to get shareable content: {}",
std::string([error.localizedDescription UTF8String]));
} else {
content = result;
}
@@ -211,9 +223,10 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) {
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
if (!content || content.displays.count == 0) {
LOG_ERROR("Failed to get display info");
return 0;
if (capture_error || !content || content.displays.count == 0) {
LOG_ERROR("Failed to get display info, error: {}",
std::string([capture_error.localizedDescription UTF8String]));
return -1;
}
CGDirectDisplayID displays[10];
@@ -252,6 +265,16 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) {
}
int ScreenCapturerSckImpl::Start(bool show_cursor) {
if (permanent_error_) {
LOG_ERROR("Cannot start capturer: permanent error occurred");
return -1;
}
if (display_info_list_.empty()) {
LOG_ERROR("Cannot start capturer: display info not initialized");
return -1;
}
show_cursor_ = show_cursor;
StartOrReconfigureCapturer();
return 0;
@@ -307,17 +330,17 @@ void ScreenCapturerSckImpl::OnShareableContentCreated(SCShareableContent *conten
return;
}
if (!content.displays.count) {
if (!content.displays || content.displays.count == 0) {
LOG_ERROR("getShareableContent returned no displays");
permanent_error_ = true;
return;
}
SCDisplay *captured_display;
SCDisplay *captured_display = nil;
{
std::lock_guard<std::mutex> lock(lock_);
for (SCDisplay *display in content.displays) {
if (current_display_ == display.displayID) {
if (current_display_ != 0 && current_display_ == display.displayID) {
LOG_WARN("current display: {}, name: {}", current_display_,
display_id_name_map_[current_display_]);
captured_display = display;
@@ -326,13 +349,33 @@ void ScreenCapturerSckImpl::OnShareableContentCreated(SCShareableContent *conten
}
if (!captured_display) {
captured_display = content.displays.firstObject;
current_display_ = captured_display.displayID;
if (captured_display) {
current_display_ = captured_display.displayID;
}
}
}
if (!captured_display) {
LOG_ERROR("Failed to find valid display");
permanent_error_ = true;
return;
}
SCContentFilter *filter = [[SCContentFilter alloc] initWithDisplay:captured_display
excludingWindows:@[]];
if (!filter) {
LOG_ERROR("Failed to create SCContentFilter");
permanent_error_ = true;
return;
}
SCStreamConfiguration *config = [[SCStreamConfiguration alloc] init];
if (!config) {
LOG_ERROR("Failed to create SCStreamConfiguration");
permanent_error_ = true;
return;
}
config.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
config.showsCursor = show_cursor_;
config.width = filter.contentRect.size.width * filter.pointPixelScale;
@@ -423,11 +466,28 @@ void ScreenCapturerSckImpl::OnNewCVPixelBuffer(CVPixelBufferRef pixelBuffer,
}
void ScreenCapturerSckImpl::StartOrReconfigureCapturer() {
// The copy is needed to avoid capturing `this` in the Objective-C block. Accessing `helper_`
// inside the block is equivalent to `this->helper_` and would crash (UAF) if `this` is
// deleted before the block is executed.
if (permanent_error_) {
LOG_ERROR("Cannot reconfigure capturer: permanent error occurred");
return;
}
if (@available(macOS 10.15, *)) {
bool has_permission = CGPreflightScreenCaptureAccess();
if (!has_permission) {
LOG_ERROR("Screen recording permission not granted");
permanent_error_ = true;
return;
}
}
SckHelper *local_helper = helper_;
auto handler = ^(SCShareableContent *content, NSError *error) {
if (error) {
LOG_ERROR("getShareableContent failed: {}",
std::string([error.localizedDescription UTF8String]));
[local_helper onShareableContentCreated:nil];
return;
}
[local_helper onShareableContentCreated:content];
};
[SCShareableContent getShareableContentWithCompletionHandler:handler];

View File

@@ -77,12 +77,14 @@ ScreenCapturerWgc::~ScreenCapturerWgc() {
CleanUp();
if (nv12_frame_) {
delete nv12_frame_;
delete[] nv12_frame_;
nv12_frame_ = nullptr;
nv12_width_ = 0;
nv12_height_ = 0;
}
if (nv12_frame_scaled_) {
delete nv12_frame_scaled_;
delete[] nv12_frame_scaled_;
nv12_frame_scaled_ = nullptr;
}
}
@@ -215,13 +217,14 @@ int ScreenCapturerWgc::Resume(int monitor_index) {
}
int ScreenCapturerWgc::Stop() {
running_ = false;
for (int i = 0; i < sessions_.size(); i++) {
if (sessions_[i].running_) {
sessions_[i].session_->Stop();
sessions_[i].running_ = false;
}
}
running_ = false;
return 0;
}
@@ -256,18 +259,61 @@ int ScreenCapturerWgc::SwitchTo(int monitor_index) {
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame,
int id) {
if (!running_ || !on_data_) {
return;
}
std::lock_guard<std::mutex> lock(frame_mutex_);
if (on_data_) {
if (!nv12_frame_) {
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
if (id < 0 || id >= static_cast<int>(display_info_list_.size())) {
LOG_ERROR("WGC OnFrame invalid display index: {}", id);
return;
}
libyuv::ARGBToNV12((const uint8_t*)frame.data, frame.width * 4,
(uint8_t*)nv12_frame_, frame.width,
(uint8_t*)(nv12_frame_ + frame.width * frame.height),
frame.width, frame.width, frame.height);
if (!frame.data || frame.row_pitch == 0) {
LOG_ERROR("WGC OnFrame received invalid frame: data={}, row_pitch={}",
(void*)frame.data, frame.row_pitch);
return;
}
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
frame.height, display_info_list_[id].name.c_str());
// calculate the maximum width that can be contained in one row according to
// row_pitch (BGRA: 4 bytes per pixel), and take the minimum with logical
// width to avoid out-of-bounds access.
unsigned int max_width_by_pitch = frame.row_pitch / 4u;
int logical_width = static_cast<int>(
frame.width < max_width_by_pitch ? frame.width : max_width_by_pitch);
// libyuv::ARGBToNV12 requires even width/height
int even_width = logical_width & ~1;
int even_height = static_cast<int>(frame.height) & ~1;
if (even_width <= 0 || even_height <= 0) {
LOG_ERROR(
"WGC OnFrame invalid frame size after adjust: width={} "
"(frame.width={}, max_by_pitch={}), height={}",
logical_width, frame.width, max_width_by_pitch, frame.height);
return;
}
int nv12_size = even_width * even_height * 3 / 2;
if (!nv12_frame_ || nv12_width_ != even_width ||
nv12_height_ != even_height) {
delete[] nv12_frame_;
nv12_frame_ = new unsigned char[nv12_size];
nv12_width_ = even_width;
nv12_height_ = even_height;
}
libyuv::ARGBToNV12((const uint8_t*)frame.data,
static_cast<int>(frame.row_pitch), (uint8_t*)nv12_frame_,
even_width,
(uint8_t*)(nv12_frame_ + even_width * even_height),
even_width, even_width, even_height);
on_data_(nv12_frame_, nv12_size, even_width, even_height,
display_info_list_[id].name.c_str());
}
}

View File

@@ -3,6 +3,7 @@
#include <atomic>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
@@ -65,6 +66,10 @@ class ScreenCapturerWgc : public ScreenCapturer,
unsigned char* nv12_frame_ = nullptr;
unsigned char* nv12_frame_scaled_ = nullptr;
int nv12_width_ = 0;
int nv12_height_ = 0;
std::mutex frame_mutex_;
};
} // namespace crossdesk
#endif

View File

@@ -38,8 +38,50 @@ class SpeakerCapturerMacosx;
if (_owner->cb_ && dataPtr && length > 0 && asbd) {
std::vector<short> out_pcm16;
// ... 数据转换逻辑保持不变 ...
// 调用回调
if (asbd->mFormatFlags & kAudioFormatFlagIsFloat) {
int channels = asbd->mChannelsPerFrame;
int samples = (int)(length / sizeof(float));
float* floatData = (float*)dataPtr;
std::vector<short> pcm16(samples);
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) {
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 += pcm16[i * channels + c];
}
out_pcm16[i] = sum / channels;
}
} 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();
@@ -48,6 +90,7 @@ class SpeakerCapturerMacosx;
}
}
}
@end
namespace crossdesk {

View File

@@ -156,6 +156,12 @@ Thumbnail::~Thumbnail() {
}
}
int Thumbnail::SetThumbnailDpiScale(float dpi_scale) {
thumbnail_width_ = static_cast<int>(thumbnail_width_ * dpi_scale);
thumbnail_height_ = static_cast<int>(thumbnail_height_ * dpi_scale);
return 0;
}
int Thumbnail::SaveToThumbnail(const char* yuv420p, int width, int height,
const std::string& remote_id,
const std::string& host_name,

View File

@@ -33,6 +33,8 @@ class Thumbnail {
~Thumbnail();
public:
int SetThumbnailDpiScale(float dpi_scale);
int SaveToThumbnail(const char* yuv420p, int width, int height,
const std::string& remote_id,
const std::string& host_name,

View File

@@ -4,19 +4,15 @@
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _VERSION_CHECKER_H_
#define _VERSION_CHECKER_H_
#include "version_checker.h"
#include <httplib.h>
#include <iostream>
#include <nlohmann/json.hpp>
#include <sstream>
#include <string>
#include <vector>
using json = nlohmann::json;
namespace crossdesk {
static std::string latest_release_date_ = "";
@@ -129,7 +125,7 @@ bool IsNewerVersionWithDate(const std::string& current_version,
return false;
}
std::string CheckUpdate() {
nlohmann::json CheckUpdate() {
httplib::Client cli("https://version.crossdesk.cn");
cli.set_connection_timeout(5);
@@ -138,28 +134,25 @@ std::string CheckUpdate() {
if (auto res = cli.Get("/version.json")) {
if (res->status == 200) {
try {
auto j = json::parse(res->body);
std::string latest = j["version"];
auto j = nlohmann::json::parse(res->body);
if (j.contains("releaseDate") && j["releaseDate"].is_string()) {
latest_release_date_ = j["releaseDate"];
} else {
latest_release_date_ = "";
}
return latest;
return j;
} catch (std::exception&) {
latest_release_date_ = "";
return "";
return nlohmann::json{};
}
} else {
latest_release_date_ = "";
return "";
return nlohmann::json{};
}
} else {
latest_release_date_ = "";
return "";
return nlohmann::json{};
}
}
std::string GetLatestReleaseDate() { return latest_release_date_; }
} // namespace crossdesk
#endif
} // namespace crossdesk

View File

@@ -7,11 +7,12 @@
#ifndef _VERSION_CHECKER_H_
#define _VERSION_CHECKER_H_
#include <nlohmann/json.hpp>
#include <string>
namespace crossdesk {
std::string CheckUpdate();
nlohmann::json CheckUpdate();
bool IsNewerVersion(const std::string& current, const std::string& latest);

View File

@@ -29,7 +29,7 @@ if is_mode("debug") then
end
add_requires("spdlog 1.14.1", {system = false})
add_requires("imgui v1.91.5-docking", {configs = {sdl3 = true, sdl3_renderer = true}})
add_requires("imgui v1.92.1-docking", {configs = {sdl3 = true, sdl3_renderer = true}})
add_requires("openssl3 3.3.2", {system = false})
add_requires("nlohmann_json 3.11.3")
add_requires("cpp-httplib v0.26.0", {configs = {ssl = true}})
@@ -45,7 +45,7 @@ elseif is_os("linux") then
add_links("pulse-simple", "pulse")
add_requires("libyuv")
add_syslinks("pthread", "dl")
add_links("SDL3", "asound", "X11", "Xtst", "Xrandr")
add_links("SDL3", "asound", "X11", "Xtst", "Xrandr", "Xfixes")
add_cxflags("-Wno-unused-variable")
elseif is_os("macosx") then
add_links("SDL3")
@@ -182,6 +182,8 @@ target("gui")
if is_os("windows") then
add_files("src/gui/tray/*.cpp")
add_includedirs("src/gui/tray", {public = true})
elseif is_os("macosx") then
add_files("src/gui/windows/*.mm")
end
target("crossdesk")