mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-12-19 05:36:32 +08:00
Compare commits
12 Commits
v1.1.0-bet
...
web-client
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dd3c3e073 | ||
|
|
4ba4f17a6b | ||
|
|
f5d0291b5a | ||
|
|
1a64c1afef | ||
|
|
18f4973d0a | ||
|
|
37ede5861c | ||
|
|
497454ac51 | ||
|
|
4ebb7a6a4d | ||
|
|
7d1910df71 | ||
|
|
52e70a26f3 | ||
|
|
adb6cee326 | ||
|
|
941b5e5cdc |
86
.github/workflows/build.yml
vendored
86
.github/workflows/build.yml
vendored
@@ -35,12 +35,14 @@ jobs:
|
|||||||
id: set_deb_version
|
id: set_deb_version
|
||||||
run: |
|
run: |
|
||||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||||
|
BUILD_DATE=$(date +%Y%m%d)
|
||||||
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
|
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
|
||||||
LEGAL_VERSION="0.0.0-${VERSION_NUM}-${SHORT_SHA}"
|
LEGAL_VERSION="v0.0.0-${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}"
|
||||||
else
|
else
|
||||||
LEGAL_VERSION="${VERSION_NUM}-${SHORT_SHA}"
|
LEGAL_VERSION="v${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}"
|
||||||
fi
|
fi
|
||||||
echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV
|
echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV
|
||||||
|
echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -99,12 +101,14 @@ jobs:
|
|||||||
id: set_deb_version
|
id: set_deb_version
|
||||||
run: |
|
run: |
|
||||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||||
|
BUILD_DATE=$(date +%Y%m%d)
|
||||||
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
|
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
|
||||||
LEGAL_VERSION="0.0.0-${VERSION_NUM}-${SHORT_SHA}"
|
LEGAL_VERSION="v0.0.0-${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}"
|
||||||
else
|
else
|
||||||
LEGAL_VERSION="${VERSION_NUM}-${SHORT_SHA}"
|
LEGAL_VERSION="v${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}"
|
||||||
fi
|
fi
|
||||||
echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV
|
echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV
|
||||||
|
echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -160,9 +164,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION="${GITHUB_REF##*/}"
|
VERSION="${GITHUB_REF##*/}"
|
||||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||||
VERSION_NUM="${VERSION#v}-${SHORT_SHA}"
|
BUILD_DATE=$(date +%Y%m%d)
|
||||||
|
VERSION_NUM="v${VERSION#v}-${BUILD_DATE}-${SHORT_SHA}"
|
||||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV
|
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV
|
||||||
echo "VERSION_NUM=${VERSION_NUM}"
|
echo "VERSION_NUM=${VERSION_NUM}"
|
||||||
|
echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Cache xmake dependencies
|
- name: Cache xmake dependencies
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
@@ -223,7 +229,9 @@ jobs:
|
|||||||
$version = $version -replace '^v', ''
|
$version = $version -replace '^v', ''
|
||||||
$version = $version -replace '/', '-'
|
$version = $version -replace '/', '-'
|
||||||
$SHORT_SHA = $env:GITHUB_SHA.Substring(0,7)
|
$SHORT_SHA = $env:GITHUB_SHA.Substring(0,7)
|
||||||
echo "VERSION_NUM=$version-$SHORT_SHA" >> $env:GITHUB_ENV
|
$BUILD_DATE = Get-Date -Format "yyyyMMdd"
|
||||||
|
echo "VERSION_NUM=v$version-$BUILD_DATE-$SHORT_SHA" >> $env:GITHUB_ENV
|
||||||
|
echo "BUILD_DATE=$BUILD_DATE" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
- name: Cache xmake dependencies
|
- name: Cache xmake dependencies
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
@@ -332,17 +340,25 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION="${GITHUB_REF##*/}"
|
VERSION="${GITHUB_REF##*/}"
|
||||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||||
VERSION_NUM="${VERSION#v}-${SHORT_SHA}"
|
BUILD_DATE=$(date +%Y%m%d)
|
||||||
|
BUILD_DATE_ISO=$(date +%Y-%m-%d)
|
||||||
|
VERSION_NUM="${VERSION#v}-${BUILD_DATE}-${SHORT_SHA}"
|
||||||
|
VERSION_WITH_V="v${VERSION_NUM}"
|
||||||
|
VERSION_ONLY="${VERSION#v}"
|
||||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT
|
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT
|
||||||
|
echo "VERSION_WITH_V=${VERSION_WITH_V}" >> $GITHUB_OUTPUT
|
||||||
|
echo "VERSION_ONLY=${VERSION_ONLY}" >> $GITHUB_OUTPUT
|
||||||
|
echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_OUTPUT
|
||||||
|
echo "BUILD_DATE_ISO=${BUILD_DATE_ISO}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Rename artifacts
|
- name: Rename artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir -p release
|
mkdir -p release
|
||||||
cp artifacts/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_NUM }}.pkg
|
cp artifacts/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg
|
||||||
cp artifacts/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_NUM }}.pkg
|
cp artifacts/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg
|
||||||
cp artifacts/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_NUM }}.deb
|
cp artifacts/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_WITH_V }}.deb
|
||||||
cp artifacts/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_NUM }}.deb
|
cp artifacts/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.deb
|
||||||
cp artifacts/crossdesk-win-x64-${{ steps.version.outputs.VERSION_NUM }}/* release/crossdesk-win-x64-${{ steps.version.outputs.VERSION_NUM }}.exe
|
cp artifacts/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe
|
||||||
|
|
||||||
- name: List release files
|
- name: List release files
|
||||||
run: ls -lh release/
|
run: ls -lh release/
|
||||||
@@ -350,8 +366,8 @@ jobs:
|
|||||||
- name: Upload to Versioned GitHub Release
|
- name: Upload to Versioned GitHub Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
tag_name: v${{ steps.version.outputs.VERSION_NUM }}
|
tag_name: ${{ steps.version.outputs.VERSION_WITH_V }}
|
||||||
name: Release v${{ steps.version.outputs.VERSION_NUM }}
|
name: Release ${{ steps.version.outputs.VERSION_WITH_V }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
files: release/*
|
files: release/*
|
||||||
@@ -385,3 +401,45 @@ jobs:
|
|||||||
remote_host: ${{ secrets.SERVER_HOST }}
|
remote_host: ${{ secrets.SERVER_HOST }}
|
||||||
remote_user: ${{ secrets.SERVER_USER }}
|
remote_user: ${{ secrets.SERVER_USER }}
|
||||||
remote_key: ${{ secrets.SERVER_KEY }}
|
remote_key: ${{ secrets.SERVER_KEY }}
|
||||||
|
|
||||||
|
- name: Generate version.json
|
||||||
|
run: |
|
||||||
|
cat > version.json << EOF
|
||||||
|
{
|
||||||
|
"version": "${{ steps.version.outputs.VERSION_ONLY }}",
|
||||||
|
"releaseDate": "${{ steps.version.outputs.BUILD_DATE_ISO }}",
|
||||||
|
"downloads": {
|
||||||
|
"windows-x64": {
|
||||||
|
"url": "https://downloads.crossdesk.cn/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe",
|
||||||
|
"filename": "crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe"
|
||||||
|
},
|
||||||
|
"macos-x64": {
|
||||||
|
"url": "https://downloads.crossdesk.cn/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg",
|
||||||
|
"filename": "crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg"
|
||||||
|
},
|
||||||
|
"macos-arm64": {
|
||||||
|
"url": "https://downloads.crossdesk.cn/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg",
|
||||||
|
"filename": "crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg"
|
||||||
|
},
|
||||||
|
"linux-amd64": {
|
||||||
|
"url": "https://downloads.crossdesk.cn/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_WITH_V }}.deb",
|
||||||
|
"filename": "crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_WITH_V }}.deb"
|
||||||
|
},
|
||||||
|
"linux-arm64": {
|
||||||
|
"url": "https://downloads.crossdesk.cn/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.deb",
|
||||||
|
"filename": "crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.deb"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 }}
|
||||||
|
|||||||
51
.github/workflows/update-pages.yml
vendored
51
.github/workflows/update-pages.yml
vendored
@@ -1,51 +0,0 @@
|
|||||||
name: Update GitHub Pages Downloads
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update-pages:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout CrossDesk repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set version number
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
|
||||||
VERSION_NUM="${GITHUB_REF##*/}"
|
|
||||||
VERSION_NUM="${VERSION_NUM#v}-${SHORT_SHA}"
|
|
||||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV
|
|
||||||
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Checkout Pages repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
repository: kunkundi/kunkundi.github.io
|
|
||||||
token: ${{ secrets.GH_PAGES_PAT }}
|
|
||||||
path: pages
|
|
||||||
|
|
||||||
- name: Update download links
|
|
||||||
run: |
|
|
||||||
cd pages
|
|
||||||
echo "Updating download links to ${VERSION_NUM}"
|
|
||||||
sed -E -i "s/crossdesk-win-x64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.exe/crossdesk-win-x64-${VERSION_NUM}.exe/g" index.html
|
|
||||||
sed -E -i "s/crossdesk-macos-x64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.pkg/crossdesk-macos-x64-${VERSION_NUM}.pkg/g" index.html
|
|
||||||
sed -E -i "s/crossdesk-macos-arm64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.pkg/crossdesk-macos-arm64-${VERSION_NUM}.pkg/g" index.html
|
|
||||||
sed -E -i "s/crossdesk-linux-amd64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.deb/crossdesk-linux-amd64-${VERSION_NUM}.deb/g" index.html
|
|
||||||
sed -E -i "s/crossdesk-linux-arm64-[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9._-]+)?\.deb/crossdesk-linux-arm64-${VERSION_NUM}.deb/g" index.html
|
|
||||||
git diff index.html || true
|
|
||||||
|
|
||||||
- name: Commit & Push changes
|
|
||||||
run: |
|
|
||||||
cd pages
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git add index.html
|
|
||||||
git commit -m "Update download links to v${VERSION_NUM}" || echo "No changes to commit"
|
|
||||||
git push origin main
|
|
||||||
env:
|
|
||||||
VERSION_NUM: ${{ env.VERSION_NUM }}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||||
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
[](https://github.com/kunkundi/crossdesk/commits/web-client)
|
||||||
[](https://github.com/kunkundi/crossdesk/actions)
|
[](https://github.com/kunkundi/crossdesk/actions)
|
||||||
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
||||||
[]()
|
[]()
|
||||||
@@ -42,7 +42,9 @@ CrossDesk 是 [MiniRTC](https://github.com/kunkundi/minirtc.git) 实时音视频
|
|||||||
|
|
||||||
### Web 客户端
|
### Web 客户端
|
||||||
浏览器访问 [CrossDesk Web Client](https://web.crossdesk.cn/)。
|
浏览器访问 [CrossDesk Web Client](https://web.crossdesk.cn/)。
|
||||||
输入 **远程设备 ID** 与 **密码**,点击连接即可接入远程设备。
|
输入 **远程设备 ID** 与 **密码**,点击连接即可接入远程设备。如图,**iOS Safari 远程控制 Win11**:
|
||||||
|
|
||||||
|
<img width="645" height="300" alt="_cgi-bin_mmwebwx-bin_webwxgetmsgimg__ MsgID=932911462648581698 skey=@crypt_1f5153b1_b550ca7462b5009ce03c991cca2a92a7 mmweb_appid=wx_webfilehelper" src="https://github.com/user-attachments/assets/a5109e6f-752c-4654-9f4e-7e161bddf43e" />
|
||||||
|
|
||||||
## 如何编译
|
## 如何编译
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||||
[](https://github.com/kunkundi/crossdesk/commits/self-hosted-server)
|
[](https://github.com/kunkundi/crossdesk/commits/web-client)
|
||||||
[](https://github.com/kunkundi/crossdesk/actions)
|
[](https://github.com/kunkundi/crossdesk/actions)
|
||||||
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
[](https://hub.docker.com/r/crossdesk/crossdesk-server/tags)
|
||||||
[]()
|
[]()
|
||||||
@@ -45,7 +45,9 @@ Before connecting, you can customize configuration options in the settings, such
|
|||||||
### Web Client
|
### Web Client
|
||||||
|
|
||||||
Visit [CrossDesk Web Client](https://web.crossdesk.cn/).
|
Visit [CrossDesk Web Client](https://web.crossdesk.cn/).
|
||||||
Enter the **Remote Device ID** and **Password**, then click Connect to access the remote device.
|
Enter the **Remote Device ID** and **Password**, then click Connect to access the remote device. As shown, **iOS Safari remotely controlling Windows 11**:
|
||||||
|
|
||||||
|
<img width="645" height="300" alt="_cgi-bin_mmwebwx-bin_webwxgetmsgimg__ MsgID=932911462648581698 skey=@crypt_1f5153b1_b550ca7462b5009ce03c991cca2a92a7 mmweb_appid=wx_webfilehelper" src="https://github.com/user-attachments/assets/a5109e6f-752c-4654-9f4e-7e161bddf43e" />
|
||||||
|
|
||||||
## How to build
|
## How to build
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ ARCHITECTURE="amd64"
|
|||||||
MAINTAINER="Junkun Di <junkun.di@hotmail.com>"
|
MAINTAINER="Junkun Di <junkun.di@hotmail.com>"
|
||||||
DESCRIPTION="A simple cross-platform remote desktop client."
|
DESCRIPTION="A simple cross-platform remote desktop client."
|
||||||
|
|
||||||
DEB_DIR="${PKG_NAME}-${APP_VERSION}"
|
# Remove 'v' prefix from version for Debian package (Debian version must start with digit)
|
||||||
|
DEB_VERSION="${APP_VERSION#v}"
|
||||||
|
|
||||||
|
DEB_DIR="${PKG_NAME}-${DEB_VERSION}"
|
||||||
DEBIAN_DIR="$DEB_DIR/DEBIAN"
|
DEBIAN_DIR="$DEB_DIR/DEBIAN"
|
||||||
BIN_DIR="$DEB_DIR/usr/bin"
|
BIN_DIR="$DEB_DIR/usr/bin"
|
||||||
CERT_SRC_DIR="$DEB_DIR/opt/$PKG_NAME/certs"
|
CERT_SRC_DIR="$DEB_DIR/opt/$PKG_NAME/certs"
|
||||||
@@ -35,7 +38,7 @@ done
|
|||||||
|
|
||||||
cat > "$DEBIAN_DIR/control" << EOF
|
cat > "$DEBIAN_DIR/control" << EOF
|
||||||
Package: $PKG_NAME
|
Package: $PKG_NAME
|
||||||
Version: $APP_VERSION
|
Version: $DEB_VERSION
|
||||||
Architecture: $ARCHITECTURE
|
Architecture: $ARCHITECTURE
|
||||||
Maintainer: $MAINTAINER
|
Maintainer: $MAINTAINER
|
||||||
Description: $DESCRIPTION
|
Description: $DESCRIPTION
|
||||||
@@ -50,7 +53,7 @@ EOF
|
|||||||
|
|
||||||
cat > "$DESKTOP_DIR/$PKG_NAME.desktop" << EOF
|
cat > "$DESKTOP_DIR/$PKG_NAME.desktop" << EOF
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Version=$APP_VERSION
|
Version=$DEB_VERSION
|
||||||
Name=$APP_NAME
|
Name=$APP_NAME
|
||||||
Comment=$DESCRIPTION
|
Comment=$DESCRIPTION
|
||||||
Exec=/usr/bin/$PKG_NAME
|
Exec=/usr/bin/$PKG_NAME
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ ARCHITECTURE="arm64"
|
|||||||
MAINTAINER="Junkun Di <junkun.di@hotmail.com>"
|
MAINTAINER="Junkun Di <junkun.di@hotmail.com>"
|
||||||
DESCRIPTION="A simple cross-platform remote desktop client."
|
DESCRIPTION="A simple cross-platform remote desktop client."
|
||||||
|
|
||||||
DEB_DIR="${PKG_NAME}-${APP_VERSION}"
|
# Remove 'v' prefix from version for Debian package (Debian version must start with digit)
|
||||||
|
DEB_VERSION="${APP_VERSION#v}"
|
||||||
|
|
||||||
|
DEB_DIR="${PKG_NAME}-${DEB_VERSION}"
|
||||||
DEBIAN_DIR="$DEB_DIR/DEBIAN"
|
DEBIAN_DIR="$DEB_DIR/DEBIAN"
|
||||||
BIN_DIR="$DEB_DIR/usr/bin"
|
BIN_DIR="$DEB_DIR/usr/bin"
|
||||||
CERT_SRC_DIR="$DEB_DIR/opt/$PKG_NAME/certs"
|
CERT_SRC_DIR="$DEB_DIR/opt/$PKG_NAME/certs"
|
||||||
@@ -35,7 +38,7 @@ done
|
|||||||
|
|
||||||
cat > "$DEBIAN_DIR/control" << EOF
|
cat > "$DEBIAN_DIR/control" << EOF
|
||||||
Package: $PKG_NAME
|
Package: $PKG_NAME
|
||||||
Version: $APP_VERSION
|
Version: $DEB_VERSION
|
||||||
Architecture: $ARCHITECTURE
|
Architecture: $ARCHITECTURE
|
||||||
Maintainer: $MAINTAINER
|
Maintainer: $MAINTAINER
|
||||||
Description: $DESCRIPTION
|
Description: $DESCRIPTION
|
||||||
@@ -49,7 +52,7 @@ EOF
|
|||||||
|
|
||||||
cat > "$DESKTOP_DIR/$PKG_NAME.desktop" << EOF
|
cat > "$DESKTOP_DIR/$PKG_NAME.desktop" << EOF
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Version=$APP_VERSION
|
Version=$DEB_VERSION
|
||||||
Name=$APP_NAME
|
Name=$APP_NAME
|
||||||
Comment=$DESCRIPTION
|
Comment=$DESCRIPTION
|
||||||
Exec=/usr/bin/$PKG_NAME
|
Exec=/usr/bin/$PKG_NAME
|
||||||
|
|||||||
302
src/autostart/autostart.cpp
Normal file
302
src/autostart/autostart.cpp
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
#include "autostart.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <limits.h>
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace crossdesk {
|
||||||
|
|
||||||
|
static std::string get_home_dir() {
|
||||||
|
const char* home = std::getenv("HOME");
|
||||||
|
if (!home) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return std::string(home);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool file_exists(const std::string& path) {
|
||||||
|
return std::filesystem::exists(path) &&
|
||||||
|
std::filesystem::is_regular_file(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetExecutablePath() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
char path[32768];
|
||||||
|
DWORD length = GetModuleFileNameA(nullptr, path, sizeof(path));
|
||||||
|
if (length > 0 && length < sizeof(path)) {
|
||||||
|
return std::string(path);
|
||||||
|
}
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
char path[1024];
|
||||||
|
uint32_t size = sizeof(path);
|
||||||
|
if (_NSGetExecutablePath(path, &size) == 0) {
|
||||||
|
char resolved_path[PATH_MAX];
|
||||||
|
if (realpath(path, resolved_path) != nullptr) {
|
||||||
|
return std::string(resolved_path);
|
||||||
|
}
|
||||||
|
return std::string(path);
|
||||||
|
}
|
||||||
|
#elif defined(__linux__)
|
||||||
|
char path[PATH_MAX];
|
||||||
|
ssize_t count = readlink("/proc/self/exe", path, sizeof(path) - 1);
|
||||||
|
if (count != -1) {
|
||||||
|
path[count] = '\0';
|
||||||
|
return std::string(path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows
|
||||||
|
#ifdef _WIN32
|
||||||
|
static constexpr const char* WINDOWS_RUN_KEY =
|
||||||
|
"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
||||||
|
|
||||||
|
static bool windows_enable(const std::string& appName,
|
||||||
|
const std::string& exePath) {
|
||||||
|
if (exePath.empty() || !std::filesystem::exists(exePath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HKEY hKey = nullptr;
|
||||||
|
// Use KEY_WRITE to ensure we have write permission
|
||||||
|
LONG result =
|
||||||
|
RegOpenKeyExA(HKEY_CURRENT_USER, WINDOWS_RUN_KEY, 0, KEY_WRITE, &hKey);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string regValue = exePath;
|
||||||
|
if (!exePath.empty() && exePath.find(' ') != std::string::npos) {
|
||||||
|
if (exePath.front() != '"' || exePath.back() != '"') {
|
||||||
|
regValue = "\"" + exePath + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we close the key even if RegSetValueExA fails
|
||||||
|
result = RegSetValueExA(hKey, appName.c_str(), 0, REG_SZ,
|
||||||
|
reinterpret_cast<const BYTE*>(regValue.c_str()),
|
||||||
|
static_cast<DWORD>(regValue.size() + 1));
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
|
||||||
|
return result == ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool windows_disable(const std::string& appName) {
|
||||||
|
HKEY hKey = nullptr;
|
||||||
|
LONG result =
|
||||||
|
RegOpenKeyExA(HKEY_CURRENT_USER, WINDOWS_RUN_KEY, 0, KEY_WRITE, &hKey);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = RegDeleteValueA(hKey, appName.c_str());
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
|
||||||
|
// Return true even if the value doesn't exist (already disabled)
|
||||||
|
return result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool windows_exists(const std::string& appName) {
|
||||||
|
HKEY hKey = nullptr;
|
||||||
|
LONG result =
|
||||||
|
RegOpenKeyExA(HKEY_CURRENT_USER, WINDOWS_RUN_KEY, 0, KEY_READ, &hKey);
|
||||||
|
if (result != ERROR_SUCCESS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = RegQueryValueExA(hKey, appName.c_str(), nullptr, nullptr, nullptr,
|
||||||
|
nullptr);
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
|
||||||
|
return result == ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Linux
|
||||||
|
#if defined(__linux__)
|
||||||
|
static std::string linux_desktop_path(const std::string& appName) {
|
||||||
|
std::string home = get_home_dir();
|
||||||
|
if (home.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return home + "/.config/autostart/" + appName + ".desktop";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool linux_enable(const std::string& appName,
|
||||||
|
const std::string& exePath) {
|
||||||
|
std::string home = get_home_dir();
|
||||||
|
if (home.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path dir =
|
||||||
|
std::filesystem::path(home) / ".config" / "autostart";
|
||||||
|
|
||||||
|
// Create directory if it doesn't exist
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::create_directories(dir, ec);
|
||||||
|
if (ec) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path = linux_desktop_path(appName);
|
||||||
|
if (path.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream file(path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file << "[Desktop Entry]\n";
|
||||||
|
file << "Type=Application\n";
|
||||||
|
file << "Exec=" << exePath << "\n";
|
||||||
|
file << "Hidden=false\n";
|
||||||
|
file << "NoDisplay=false\n";
|
||||||
|
file << "X-GNOME-Autostart-enabled=true\n";
|
||||||
|
file << "Terminal=false\n";
|
||||||
|
file << "StartupNotify=false\n";
|
||||||
|
file << "Name=" << appName << "\n";
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return file.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool linux_disable(const std::string& appName) {
|
||||||
|
std::string path = linux_desktop_path(appName);
|
||||||
|
if (path.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
return std::filesystem::remove(path, ec) && !ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool linux_exists(const std::string& appName) {
|
||||||
|
std::string path = linux_desktop_path(appName);
|
||||||
|
if (path.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file_exists(path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// macOS
|
||||||
|
#ifdef __APPLE__
|
||||||
|
static std::string mac_plist_path(const std::string& appName) {
|
||||||
|
std::string home = get_home_dir();
|
||||||
|
if (home.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return home + "/Library/LaunchAgents/" + appName + ".plist";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mac_enable(const std::string& appName, const std::string& exePath) {
|
||||||
|
std::string path = mac_plist_path(appName);
|
||||||
|
if (path.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure LaunchAgents directory exists
|
||||||
|
std::filesystem::path dir =
|
||||||
|
std::filesystem::path(get_home_dir()) / "Library" / "LaunchAgents";
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::create_directories(dir, ec);
|
||||||
|
if (ec) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream file(path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file << R"(<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Label</key>
|
||||||
|
<string>)"
|
||||||
|
<< appName << R"(</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>)"
|
||||||
|
<< exePath << R"(</string>
|
||||||
|
</array>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>)";
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return file.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mac_disable(const std::string& appName) {
|
||||||
|
std::string path = mac_plist_path(appName);
|
||||||
|
if (path.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
return std::filesystem::remove(path, ec) && !ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mac_exists(const std::string& appName) {
|
||||||
|
std::string path = mac_plist_path(appName);
|
||||||
|
if (path.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return file_exists(path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool EnableAutostart(const std::string& appName) {
|
||||||
|
std::string exePath = GetExecutablePath();
|
||||||
|
if (exePath.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
return windows_enable(appName, exePath);
|
||||||
|
#elif __APPLE__
|
||||||
|
return mac_enable(appName, exePath);
|
||||||
|
#else
|
||||||
|
return linux_enable(appName, exePath);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DisableAutostart(const std::string& appName) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return windows_disable(appName);
|
||||||
|
#elif __APPLE__
|
||||||
|
return mac_disable(appName);
|
||||||
|
#else
|
||||||
|
return linux_disable(appName);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAutostartEnabled(const std::string& appName) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return windows_exists(appName);
|
||||||
|
#elif __APPLE__
|
||||||
|
return mac_exists(appName);
|
||||||
|
#else
|
||||||
|
return linux_exists(appName);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crossdesk
|
||||||
21
src/autostart/autostart.h
Normal file
21
src/autostart/autostart.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2025-11-18
|
||||||
|
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _AUTOSTART_H_
|
||||||
|
#define _AUTOSTART_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace crossdesk {
|
||||||
|
|
||||||
|
bool EnableAutostart(const std::string& appName);
|
||||||
|
|
||||||
|
bool DisableAutostart(const std::string& appName);
|
||||||
|
|
||||||
|
bool IsAutostartEnabled(const std::string& appName);
|
||||||
|
|
||||||
|
} // namespace crossdesk
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "config_center.h"
|
#include "config_center.h"
|
||||||
|
|
||||||
|
#include "autostart.h"
|
||||||
|
#include "rd_log.h"
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
ConfigCenter::ConfigCenter(const std::string& config_path,
|
ConfigCenter::ConfigCenter(const std::string& config_path,
|
||||||
@@ -48,7 +51,8 @@ int ConfigCenter::Load() {
|
|||||||
ini_.GetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
ini_.GetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
||||||
enable_self_hosted_ =
|
enable_self_hosted_ =
|
||||||
ini_.GetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
ini_.GetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||||
|
enable_autostart_ =
|
||||||
|
ini_.GetBoolValue(section_, "enable_autostart", enable_autostart_);
|
||||||
enable_minimize_to_tray_ = ini_.GetBoolValue(
|
enable_minimize_to_tray_ = ini_.GetBoolValue(
|
||||||
section_, "enable_minimize_to_tray", enable_minimize_to_tray_);
|
section_, "enable_minimize_to_tray", enable_minimize_to_tray_);
|
||||||
|
|
||||||
@@ -71,6 +75,7 @@ int ConfigCenter::Save() {
|
|||||||
static_cast<long>(signal_server_port_));
|
static_cast<long>(signal_server_port_));
|
||||||
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
||||||
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||||
|
ini_.SetBoolValue(section_, "enable_autostart", enable_autostart_);
|
||||||
ini_.SetBoolValue(section_, "enable_minimize_to_tray",
|
ini_.SetBoolValue(section_, "enable_minimize_to_tray",
|
||||||
enable_minimize_to_tray_);
|
enable_minimize_to_tray_);
|
||||||
|
|
||||||
@@ -221,6 +226,29 @@ int ConfigCenter::SetMinimizeToTray(bool enable_minimize_to_tray) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ConfigCenter::SetAutostart(bool enable_autostart) {
|
||||||
|
enable_autostart_ = enable_autostart;
|
||||||
|
bool success = false;
|
||||||
|
if (enable_autostart) {
|
||||||
|
success = EnableAutostart("CrossDesk");
|
||||||
|
} else {
|
||||||
|
success = DisableAutostart("CrossDesk");
|
||||||
|
}
|
||||||
|
|
||||||
|
ini_.SetBoolValue(section_, "enable_autostart", enable_autostart_);
|
||||||
|
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||||
|
if (rc < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
LOG_ERROR("SetAutostart failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
|
|
||||||
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; }
|
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; }
|
||||||
@@ -274,4 +302,6 @@ std::string ConfigCenter::GetDefaultCertFilePath() const {
|
|||||||
bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }
|
bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }
|
||||||
|
|
||||||
bool ConfigCenter::IsMinimizeToTray() const { return enable_minimize_to_tray_; }
|
bool ConfigCenter::IsMinimizeToTray() const { return enable_minimize_to_tray_; }
|
||||||
|
|
||||||
|
bool ConfigCenter::IsEnableAutostart() const { return enable_autostart_; }
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
@@ -40,6 +40,7 @@ class ConfigCenter {
|
|||||||
int SetCertFilePath(const std::string& cert_file_path);
|
int SetCertFilePath(const std::string& cert_file_path);
|
||||||
int SetSelfHosted(bool enable_self_hosted);
|
int SetSelfHosted(bool enable_self_hosted);
|
||||||
int SetMinimizeToTray(bool enable_minimize_to_tray);
|
int SetMinimizeToTray(bool enable_minimize_to_tray);
|
||||||
|
int SetAutostart(bool enable_autostart);
|
||||||
|
|
||||||
// read config
|
// read config
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ class ConfigCenter {
|
|||||||
std::string GetDefaultCertFilePath() const;
|
std::string GetDefaultCertFilePath() const;
|
||||||
bool IsSelfHosted() const;
|
bool IsSelfHosted() const;
|
||||||
bool IsMinimizeToTray() const;
|
bool IsMinimizeToTray() const;
|
||||||
|
bool IsEnableAutostart() const;
|
||||||
|
|
||||||
int Load();
|
int Load();
|
||||||
int Save();
|
int Save();
|
||||||
@@ -86,6 +88,7 @@ class ConfigCenter {
|
|||||||
std::string cert_file_path_default_ = "";
|
std::string cert_file_path_default_ = "";
|
||||||
bool enable_self_hosted_ = false;
|
bool enable_self_hosted_ = false;
|
||||||
bool enable_minimize_to_tray_ = false;
|
bool enable_minimize_to_tray_ = false;
|
||||||
|
bool enable_autostart_ = false;
|
||||||
};
|
};
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
#endif
|
#endif
|
||||||
@@ -120,8 +120,14 @@ int KeyboardCapturer::Hook(OnKeyAction on_key_action, void* user_ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int KeyboardCapturer::Unhook() {
|
int KeyboardCapturer::Unhook() {
|
||||||
CFRelease(run_loop_source_);
|
if (run_loop_source_) {
|
||||||
CFRelease(event_tap_);
|
CFRelease(run_loop_source_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_tap_) {
|
||||||
|
CFRelease(event_tap_);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -21,11 +21,11 @@
|
|||||||
#define SETTINGS_WINDOW_WIDTH_CN 202
|
#define SETTINGS_WINDOW_WIDTH_CN 202
|
||||||
#define SETTINGS_WINDOW_WIDTH_EN 248
|
#define SETTINGS_WINDOW_WIDTH_EN 248
|
||||||
#if _WIN32
|
#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_CN 345
|
||||||
#define SETTINGS_WINDOW_HEIGHT_EN 345
|
#define SETTINGS_WINDOW_HEIGHT_EN 345
|
||||||
#else
|
|
||||||
#define SETTINGS_WINDOW_HEIGHT_CN 315
|
|
||||||
#define SETTINGS_WINDOW_HEIGHT_EN 315
|
|
||||||
#endif
|
#endif
|
||||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228
|
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228
|
||||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275
|
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275
|
||||||
@@ -47,6 +47,8 @@
|
|||||||
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
|
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
|
||||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
|
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
|
||||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218
|
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218
|
||||||
|
#define ENABLE_AUTOSTART_PADDING_CN 171
|
||||||
|
#define ENABLE_AUTOSTART_PADDING_EN 218
|
||||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_CN 171
|
#define ENABLE_MINIZE_TO_TRAY_PADDING_CN 171
|
||||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_EN 218
|
#define ENABLE_MINIZE_TO_TRAY_PADDING_EN 218
|
||||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN 90
|
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN 90
|
||||||
|
|||||||
@@ -168,8 +168,10 @@ static std::vector<std::string> access_website = {
|
|||||||
static std::vector<std::string> confirm_delete_connection = {
|
static std::vector<std::string> confirm_delete_connection = {
|
||||||
reinterpret_cast<const char*>(u8"确认删除此连接"),
|
reinterpret_cast<const char*>(u8"确认删除此连接"),
|
||||||
"Confirm to delete this connection"};
|
"Confirm to delete this connection"};
|
||||||
#if _WIN32
|
|
||||||
|
|
||||||
|
static std::vector<std::string> enable_autostart = {
|
||||||
|
reinterpret_cast<const char*>(u8"开机自启:"), "Auto Start:"};
|
||||||
|
#if _WIN32
|
||||||
static std::vector<std::string> minimize_to_tray = {
|
static std::vector<std::string> minimize_to_tray = {
|
||||||
reinterpret_cast<const char*>(u8"退出时最小化到系统托盘:"),
|
reinterpret_cast<const char*>(u8"退出时最小化到系统托盘:"),
|
||||||
"Minimize to system tray when exit:"};
|
"Minimize to system tray when exit:"};
|
||||||
|
|||||||
@@ -260,6 +260,8 @@ int Render::LoadSettingsFromCacheFile() {
|
|||||||
enable_turn_ = config_center_->IsEnableTurn();
|
enable_turn_ = config_center_->IsEnableTurn();
|
||||||
enable_srtp_ = config_center_->IsEnableSrtp();
|
enable_srtp_ = config_center_->IsEnableSrtp();
|
||||||
enable_self_hosted_ = config_center_->IsSelfHosted();
|
enable_self_hosted_ = config_center_->IsSelfHosted();
|
||||||
|
enable_autostart_ = config_center_->IsEnableAutostart();
|
||||||
|
enable_minimize_to_tray_ = config_center_->IsMinimizeToTray();
|
||||||
|
|
||||||
language_button_value_last_ = language_button_value_;
|
language_button_value_last_ = language_button_value_;
|
||||||
video_quality_button_value_last_ = video_quality_button_value_;
|
video_quality_button_value_last_ = video_quality_button_value_;
|
||||||
@@ -268,6 +270,8 @@ int Render::LoadSettingsFromCacheFile() {
|
|||||||
enable_turn_last_ = enable_turn_;
|
enable_turn_last_ = enable_turn_;
|
||||||
enable_srtp_last_ = enable_srtp_;
|
enable_srtp_last_ = enable_srtp_;
|
||||||
enable_self_hosted_last_ = enable_self_hosted_;
|
enable_self_hosted_last_ = enable_self_hosted_;
|
||||||
|
enable_autostart_last_ = enable_autostart_;
|
||||||
|
enable_minimize_to_tray_last_ = enable_minimize_to_tray_;
|
||||||
|
|
||||||
LOG_INFO("Load settings from cache file");
|
LOG_INFO("Load settings from cache file");
|
||||||
|
|
||||||
@@ -325,8 +329,9 @@ int Render::ScreenCapturerInit() {
|
|||||||
|
|
||||||
int Render::StartScreenCapturer() {
|
int Render::StartScreenCapturer() {
|
||||||
if (screen_capturer_) {
|
if (screen_capturer_) {
|
||||||
LOG_INFO("Start screen capturer");
|
LOG_INFO("Start screen capturer, show cursor: {}", show_cursor_);
|
||||||
screen_capturer_->Start();
|
|
||||||
|
screen_capturer_->Start(show_cursor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -908,6 +913,7 @@ int Render::DrawStreamWindow() {
|
|||||||
|
|
||||||
int Render::Run() {
|
int Render::Run() {
|
||||||
latest_version_ = CheckUpdate();
|
latest_version_ = CheckUpdate();
|
||||||
|
update_available_ = IsNewerVersion(CROSSDESK_VERSION, latest_version_);
|
||||||
|
|
||||||
path_manager_ = std::make_unique<PathManager>("CrossDesk");
|
path_manager_ = std::make_unique<PathManager>("CrossDesk");
|
||||||
if (path_manager_) {
|
if (path_manager_) {
|
||||||
|
|||||||
@@ -308,6 +308,7 @@ class Render {
|
|||||||
|
|
||||||
// main window properties
|
// main window properties
|
||||||
std::string latest_version_ = "";
|
std::string latest_version_ = "";
|
||||||
|
bool update_available_ = false;
|
||||||
bool start_mouse_controller_ = false;
|
bool start_mouse_controller_ = false;
|
||||||
bool mouse_controller_is_started_ = false;
|
bool mouse_controller_is_started_ = false;
|
||||||
bool start_screen_capturer_ = false;
|
bool start_screen_capturer_ = false;
|
||||||
@@ -315,6 +316,7 @@ class Render {
|
|||||||
bool start_speaker_capturer_ = false;
|
bool start_speaker_capturer_ = false;
|
||||||
bool speaker_capturer_is_started_ = false;
|
bool speaker_capturer_is_started_ = false;
|
||||||
bool start_keyboard_capturer_ = true;
|
bool start_keyboard_capturer_ = true;
|
||||||
|
bool show_cursor_ = false;
|
||||||
bool keyboard_capturer_is_started_ = false;
|
bool keyboard_capturer_is_started_ = false;
|
||||||
bool foucs_on_main_window_ = false;
|
bool foucs_on_main_window_ = false;
|
||||||
bool foucs_on_stream_window_ = false;
|
bool foucs_on_stream_window_ = false;
|
||||||
@@ -461,6 +463,8 @@ class Render {
|
|||||||
bool enable_turn_last_ = false;
|
bool enable_turn_last_ = false;
|
||||||
bool enable_srtp_last_ = false;
|
bool enable_srtp_last_ = false;
|
||||||
bool enable_self_hosted_last_ = false;
|
bool enable_self_hosted_last_ = false;
|
||||||
|
bool enable_autostart_ = false;
|
||||||
|
bool enable_autostart_last_ = false;
|
||||||
bool enable_minimize_to_tray_ = false;
|
bool enable_minimize_to_tray_ = false;
|
||||||
bool enable_minimize_to_tray_last_ = false;
|
bool enable_minimize_to_tray_last_ = false;
|
||||||
char signal_server_ip_self_[256] = "";
|
char signal_server_ip_self_[256] = "";
|
||||||
|
|||||||
@@ -464,6 +464,13 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
|||||||
#else
|
#else
|
||||||
render->start_mouse_controller_ = true;
|
render->start_mouse_controller_ = true;
|
||||||
#endif
|
#endif
|
||||||
|
if (std::all_of(render->connection_status_.begin(),
|
||||||
|
render->connection_status_.end(), [](const auto& kv) {
|
||||||
|
return kv.first.find("web") != std::string::npos;
|
||||||
|
})) {
|
||||||
|
render->show_cursor_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ConnectionStatus::Closed: {
|
case ConnectionStatus::Closed: {
|
||||||
@@ -486,6 +493,14 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
|||||||
|
|
||||||
render->connection_status_.erase(remote_id);
|
render->connection_status_.erase(remote_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (std::all_of(render->connection_status_.begin(),
|
||||||
|
render->connection_status_.end(), [](const auto& kv) {
|
||||||
|
return kv.first.find("web") == std::string::npos;
|
||||||
|
})) {
|
||||||
|
render->show_cursor_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ int Render::TitleBar(bool main_window) {
|
|||||||
|
|
||||||
std::string about_str =
|
std::string about_str =
|
||||||
localization::about[localization_language_index_];
|
localization::about[localization_language_index_];
|
||||||
if (!latest_version_.empty()) {
|
if (update_available_) {
|
||||||
auto now_time =
|
auto now_time =
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
std::chrono::steady_clock::now().time_since_epoch())
|
std::chrono::steady_clock::now().time_since_epoch())
|
||||||
@@ -75,7 +75,7 @@ int Render::TitleBar(bool main_window) {
|
|||||||
show_about_window_ = true;
|
show_about_window_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!latest_version_.empty() && ImGui::IsItemHovered()) {
|
if (update_available_ && ImGui::IsItemHovered()) {
|
||||||
ImGui::BeginTooltip();
|
ImGui::BeginTooltip();
|
||||||
ImGui::SetWindowFontScale(0.5f);
|
ImGui::SetWindowFontScale(0.5f);
|
||||||
std::string new_version_available_str =
|
std::string new_version_available_str =
|
||||||
@@ -104,7 +104,7 @@ int Render::TitleBar(bool main_window) {
|
|||||||
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y + 6),
|
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y + 6),
|
||||||
IM_COL32(0, 0, 0, 255));
|
IM_COL32(0, 0, 0, 255));
|
||||||
|
|
||||||
if (!latest_version_.empty() && show_new_version_icon_in_menu_) {
|
if (update_available_ && show_new_version_icon_in_menu_) {
|
||||||
auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
|
auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
std::chrono::steady_clock::now().time_since_epoch())
|
std::chrono::steady_clock::now().time_since_epoch())
|
||||||
.count();
|
.count();
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
void Hyperlink(const std::string& label, const std::string& url) {
|
void Hyperlink(const std::string& label, const std::string& url,
|
||||||
|
const float window_width) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 0, 255, 255));
|
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 0, 255, 255));
|
||||||
|
ImGui::SetCursorPosX(window_width * 0.1f);
|
||||||
ImGui::Text("%s", label.c_str());
|
ImGui::Text("%s", label.c_str());
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
@@ -67,23 +69,26 @@ int Render::AboutWindow() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string text = localization::version[localization_language_index_] +
|
std::string text = localization::version[localization_language_index_] +
|
||||||
": CrossDesk v" + version;
|
": CrossDesk " + version;
|
||||||
|
ImGui::SetCursorPosX(about_window_width_ * 0.1f);
|
||||||
ImGui::Text("%s", text.c_str());
|
ImGui::Text("%s", text.c_str());
|
||||||
|
|
||||||
if (!latest_version_.empty()) {
|
if (update_available_) {
|
||||||
std::string latest_version =
|
std::string latest_version =
|
||||||
localization::new_version_available[localization_language_index_] +
|
localization::new_version_available[localization_language_index_] +
|
||||||
": " + latest_version_;
|
": " + latest_version_;
|
||||||
std::string access_website =
|
std::string access_website =
|
||||||
localization::access_website[localization_language_index_];
|
localization::access_website[localization_language_index_];
|
||||||
Hyperlink(latest_version, "https://crossdesk.cn");
|
Hyperlink(latest_version, "https://crossdesk.cn", about_window_width_);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Text("");
|
ImGui::Text("");
|
||||||
|
|
||||||
std::string copyright_text = "© 2025 by JUNKUN DI. All rights reserved.";
|
std::string copyright_text = "© 2025 by JUNKUN DI. All rights reserved.";
|
||||||
std::string license_text = "Licensed under GNU LGPL v3.";
|
std::string license_text = "Licensed under GNU LGPL v3.";
|
||||||
|
ImGui::SetCursorPosX(about_window_width_ * 0.1f);
|
||||||
ImGui::Text("%s", copyright_text.c_str());
|
ImGui::Text("%s", copyright_text.c_str());
|
||||||
|
ImGui::SetCursorPosX(about_window_width_ * 0.1f);
|
||||||
ImGui::Text("%s", license_text.c_str());
|
ImGui::Text("%s", license_text.c_str());
|
||||||
|
|
||||||
ImGui::SetCursorPosX(about_window_width_ * 0.42f);
|
ImGui::SetCursorPosX(about_window_width_ * 0.42f);
|
||||||
|
|||||||
@@ -233,6 +233,25 @@ int Render::SettingWindow() {
|
|||||||
ImGui::SetCursorPosY(settings_items_offset);
|
ImGui::SetCursorPosY(settings_items_offset);
|
||||||
ImGui::Checkbox("##enable_self_hosted", &enable_self_hosted_);
|
ImGui::Checkbox("##enable_self_hosted", &enable_self_hosted_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
{
|
||||||
|
settings_items_offset += settings_items_padding;
|
||||||
|
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||||
|
|
||||||
|
ImGui::Text("%s",
|
||||||
|
localization::enable_autostart[localization_language_index_]
|
||||||
|
.c_str());
|
||||||
|
|
||||||
|
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||||
|
ImGui::SetCursorPosX(ENABLE_AUTOSTART_PADDING_CN);
|
||||||
|
} else {
|
||||||
|
ImGui::SetCursorPosX(ENABLE_AUTOSTART_PADDING_EN);
|
||||||
|
}
|
||||||
|
ImGui::SetCursorPosY(settings_items_offset);
|
||||||
|
ImGui::Checkbox("##enable_autostart_", &enable_autostart_);
|
||||||
|
}
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
@@ -347,6 +366,22 @@ int Render::SettingWindow() {
|
|||||||
}
|
}
|
||||||
enable_self_hosted_last_ = enable_self_hosted_;
|
enable_self_hosted_last_ = enable_self_hosted_;
|
||||||
|
|
||||||
|
if (enable_autostart_) {
|
||||||
|
config_center_->SetAutostart(true);
|
||||||
|
} else {
|
||||||
|
config_center_->SetAutostart(false);
|
||||||
|
}
|
||||||
|
enable_autostart_last_ = enable_autostart_;
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
if (enable_minimize_to_tray_) {
|
||||||
|
config_center_->SetMinimizeToTray(true);
|
||||||
|
} else {
|
||||||
|
config_center_->SetMinimizeToTray(false);
|
||||||
|
}
|
||||||
|
enable_minimize_to_tray_last_ = enable_minimize_to_tray_;
|
||||||
|
#endif
|
||||||
|
|
||||||
settings_window_pos_reset_ = true;
|
settings_window_pos_reset_ = true;
|
||||||
|
|
||||||
// Recreate peer instance
|
// Recreate peer instance
|
||||||
|
|||||||
@@ -36,9 +36,21 @@ int ScreenCapturerX11::Init(const int fps, cb_desktop_data cb) {
|
|||||||
XRRCrtcInfo* crtc_info =
|
XRRCrtcInfo* crtc_info =
|
||||||
XRRGetCrtcInfo(display_, screen_res_, output_info->crtc);
|
XRRGetCrtcInfo(display_, screen_res_, output_info->crtc);
|
||||||
|
|
||||||
display_info_list_.push_back(
|
std::string name(output_info->name);
|
||||||
DisplayInfo((void*)display_, output_info->name, true, crtc_info->x,
|
|
||||||
crtc_info->y, crtc_info->width, crtc_info->height));
|
if (name.empty()) {
|
||||||
|
name = "Display" + std::to_string(i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean display name, remove non-alphanumeric characters
|
||||||
|
name.erase(
|
||||||
|
std::remove_if(name.begin(), name.end(),
|
||||||
|
[](unsigned char c) { return !std::isalnum(c); }),
|
||||||
|
name.end());
|
||||||
|
|
||||||
|
display_info_list_.push_back(DisplayInfo(
|
||||||
|
(void*)display_, name, true, crtc_info->x, crtc_info->y,
|
||||||
|
crtc_info->x + crtc_info->width, crtc_info->y + crtc_info->height));
|
||||||
|
|
||||||
XRRFreeCrtcInfo(crtc_info);
|
XRRFreeCrtcInfo(crtc_info);
|
||||||
}
|
}
|
||||||
@@ -86,7 +98,7 @@ int ScreenCapturerX11::Destroy() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ScreenCapturerX11::Start() {
|
int ScreenCapturerX11::Start(bool show_cursor) {
|
||||||
if (running_) return 0;
|
if (running_) return 0;
|
||||||
running_ = true;
|
running_ = true;
|
||||||
paused_ = false;
|
paused_ = false;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include <X11/extensions/Xrandr.h>
|
||||||
|
#include <X11/extensions/Xfixes.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -30,7 +31,7 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
|||||||
public:
|
public:
|
||||||
int Init(const int fps, cb_desktop_data cb) override;
|
int Init(const int fps, cb_desktop_data cb) override;
|
||||||
int Destroy() override;
|
int Destroy() override;
|
||||||
int Start() override;
|
int Start(bool show_cursor) override;
|
||||||
int Stop() override;
|
int Stop() override;
|
||||||
|
|
||||||
int Pause(int monitor_index) override;
|
int Pause(int monitor_index) override;
|
||||||
@@ -54,6 +55,7 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
|||||||
std::atomic<bool> running_{false};
|
std::atomic<bool> running_{false};
|
||||||
std::atomic<bool> paused_{false};
|
std::atomic<bool> paused_{false};
|
||||||
std::atomic<int> monitor_index_{0};
|
std::atomic<int> monitor_index_{0};
|
||||||
|
std::atomic<bool> show_cursor_{true};
|
||||||
int fps_ = 60;
|
int fps_ = 60;
|
||||||
cb_desktop_data callback_;
|
cb_desktop_data callback_;
|
||||||
std::vector<DisplayInfo> display_info_list_;
|
std::vector<DisplayInfo> display_info_list_;
|
||||||
@@ -61,6 +63,9 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
|||||||
// 缓冲区
|
// 缓冲区
|
||||||
std::vector<uint8_t> y_plane_;
|
std::vector<uint8_t> y_plane_;
|
||||||
std::vector<uint8_t> uv_plane_;
|
std::vector<uint8_t> uv_plane_;
|
||||||
|
|
||||||
|
// 鼠标光标相关
|
||||||
|
void DrawCursor(XImage* image, int x, int y);
|
||||||
};
|
};
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
#endif
|
#endif
|
||||||
@@ -28,8 +28,8 @@ int ScreenCapturerSck::Destroy() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ScreenCapturerSck::Start() {
|
int ScreenCapturerSck::Start(bool show_cursor) {
|
||||||
screen_capturer_sck_impl_->Start();
|
screen_capturer_sck_impl_->Start(show_cursor);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class ScreenCapturerSck : public ScreenCapturer {
|
|||||||
public:
|
public:
|
||||||
int Init(const int fps, cb_desktop_data cb) override;
|
int Init(const int fps, cb_desktop_data cb) override;
|
||||||
int Destroy() override;
|
int Destroy() override;
|
||||||
int Start() override;
|
int Start(bool show_cursor) override;
|
||||||
int Stop() override;
|
int Stop() override;
|
||||||
|
|
||||||
int Pause(int monitor_index) override;
|
int Pause(int monitor_index) override;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer {
|
|||||||
public:
|
public:
|
||||||
int Init(const int fps, cb_desktop_data cb) override;
|
int Init(const int fps, cb_desktop_data cb) override;
|
||||||
|
|
||||||
int Start() override;
|
int Start(bool show_cursor) override;
|
||||||
|
|
||||||
int SwitchTo(int monitor_index) override;
|
int SwitchTo(int monitor_index) override;
|
||||||
|
|
||||||
@@ -80,6 +80,7 @@ class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer {
|
|||||||
int width_ = 0;
|
int width_ = 0;
|
||||||
int height_ = 0;
|
int height_ = 0;
|
||||||
int fps_ = 60;
|
int fps_ = 60;
|
||||||
|
bool show_cursor_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Called by SckHelper when shareable content is returned by ScreenCaptureKit. `content` will be
|
// Called by SckHelper when shareable content is returned by ScreenCaptureKit. `content` will be
|
||||||
@@ -225,13 +226,17 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) {
|
|||||||
CGRect bounds = CGDisplayBounds(display_id);
|
CGRect bounds = CGDisplayBounds(display_id);
|
||||||
bool is_primary = CGDisplayIsMain(display_id);
|
bool is_primary = CGDisplayIsMain(display_id);
|
||||||
|
|
||||||
std::string name;
|
std::string name = GetDisplayName(display_id);
|
||||||
name = GetDisplayName(display_id);
|
|
||||||
|
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
name = "Display " + std::to_string(unnamed_count++);
|
name = "Display" + std::to_string(unnamed_count++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clean display name, remove non-alphanumeric characters
|
||||||
|
name.erase(
|
||||||
|
std::remove_if(name.begin(), name.end(), [](unsigned char c) { return !std::isalnum(c); }),
|
||||||
|
name.end());
|
||||||
|
|
||||||
DisplayInfo info((void *)(uintptr_t)display_id, name, is_primary,
|
DisplayInfo info((void *)(uintptr_t)display_id, name, is_primary,
|
||||||
static_cast<int>(bounds.origin.x), static_cast<int>(bounds.origin.y),
|
static_cast<int>(bounds.origin.x), static_cast<int>(bounds.origin.y),
|
||||||
static_cast<int>(bounds.origin.x + bounds.size.width),
|
static_cast<int>(bounds.origin.x + bounds.size.width),
|
||||||
@@ -246,7 +251,8 @@ int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ScreenCapturerSckImpl::Start() {
|
int ScreenCapturerSckImpl::Start(bool show_cursor) {
|
||||||
|
show_cursor_ = show_cursor;
|
||||||
StartOrReconfigureCapturer();
|
StartOrReconfigureCapturer();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -328,7 +334,7 @@ void ScreenCapturerSckImpl::OnShareableContentCreated(SCShareableContent *conten
|
|||||||
excludingWindows:@[]];
|
excludingWindows:@[]];
|
||||||
SCStreamConfiguration *config = [[SCStreamConfiguration alloc] init];
|
SCStreamConfiguration *config = [[SCStreamConfiguration alloc] init];
|
||||||
config.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
|
config.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
|
||||||
config.showsCursor = false;
|
config.showsCursor = show_cursor_;
|
||||||
config.width = filter.contentRect.size.width * filter.pointPixelScale;
|
config.width = filter.contentRect.size.width * filter.pointPixelScale;
|
||||||
config.height = filter.contentRect.size.height * filter.pointPixelScale;
|
config.height = filter.contentRect.size.height * filter.pointPixelScale;
|
||||||
config.captureResolution = SCCaptureResolutionAutomatic;
|
config.captureResolution = SCCaptureResolutionAutomatic;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class ScreenCapturer {
|
|||||||
public:
|
public:
|
||||||
virtual int Init(const int fps, cb_desktop_data cb) = 0;
|
virtual int Init(const int fps, cb_desktop_data cb) = 0;
|
||||||
virtual int Destroy() = 0;
|
virtual int Destroy() = 0;
|
||||||
virtual int Start() = 0;
|
virtual int Start(bool show_cursor) = 0;
|
||||||
virtual int Stop() = 0;
|
virtual int Stop() = 0;
|
||||||
virtual int Pause(int monitor_index) = 0;
|
virtual int Pause(int monitor_index) = 0;
|
||||||
virtual int Resume(int monitor_index) = 0;
|
virtual int Resume(int monitor_index) = 0;
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
|
|||||||
|
|
||||||
int ScreenCapturerWgc::Destroy() { return 0; }
|
int ScreenCapturerWgc::Destroy() { return 0; }
|
||||||
|
|
||||||
int ScreenCapturerWgc::Start() {
|
int ScreenCapturerWgc::Start(bool show_cursor) {
|
||||||
if (running_ == true) {
|
if (running_ == true) {
|
||||||
LOG_ERROR("Screen capturer already running");
|
LOG_ERROR("Screen capturer already running");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -172,7 +172,7 @@ int ScreenCapturerWgc::Start() {
|
|||||||
if (sessions_[i].running_) {
|
if (sessions_[i].running_) {
|
||||||
LOG_ERROR("Session {} is already running", i);
|
LOG_ERROR("Session {} is already running", i);
|
||||||
} else {
|
} else {
|
||||||
sessions_[i].session_->Start();
|
sessions_[i].session_->Start(show_cursor);
|
||||||
|
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
sessions_[i].session_->Pause();
|
sessions_[i].session_->Pause();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
|||||||
|
|
||||||
int Init(const int fps, cb_desktop_data cb) override;
|
int Init(const int fps, cb_desktop_data cb) override;
|
||||||
int Destroy() override;
|
int Destroy() override;
|
||||||
int Start() override;
|
int Start(bool show_cursor) override;
|
||||||
int Stop() override;
|
int Stop() override;
|
||||||
|
|
||||||
int Pause(int monitor_index) override;
|
int Pause(int monitor_index) override;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class WgcSession {
|
|||||||
|
|
||||||
virtual void RegisterObserver(wgc_session_observer* observer) = 0;
|
virtual void RegisterObserver(wgc_session_observer* observer) = 0;
|
||||||
|
|
||||||
virtual int Start() = 0;
|
virtual int Start(bool show_cursor) = 0;
|
||||||
virtual int Stop() = 0;
|
virtual int Stop() = 0;
|
||||||
|
|
||||||
virtual int Pause() = 0;
|
virtual int Pause() = 0;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) {
|
|||||||
observer_ = observer;
|
observer_ = observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WgcSessionImpl::Start() {
|
int WgcSessionImpl::Start(bool show_cursor) {
|
||||||
std::lock_guard locker(lock_);
|
std::lock_guard locker(lock_);
|
||||||
|
|
||||||
if (is_running_) return 0;
|
if (is_running_) return 0;
|
||||||
@@ -91,7 +91,7 @@ int WgcSessionImpl::Start() {
|
|||||||
|
|
||||||
capture_session_.StartCapture();
|
capture_session_.StartCapture();
|
||||||
|
|
||||||
capture_session_.IsCursorCaptureEnabled(false);
|
capture_session_.IsCursorCaptureEnabled(show_cursor);
|
||||||
|
|
||||||
error = 0;
|
error = 0;
|
||||||
} catch (winrt::hresult_error) {
|
} catch (winrt::hresult_error) {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class WgcSessionImpl : public WgcSession {
|
|||||||
|
|
||||||
void RegisterObserver(wgc_session_observer* observer) override;
|
void RegisterObserver(wgc_session_observer* observer) override;
|
||||||
|
|
||||||
int Start() override;
|
int Start(bool show_cursor) override;
|
||||||
int Stop() override;
|
int Stop() override;
|
||||||
|
|
||||||
int Pause() override;
|
int Pause() override;
|
||||||
|
|||||||
@@ -12,11 +12,15 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
|
static std::string latest_release_date_ = "";
|
||||||
|
|
||||||
std::string ExtractNumericPart(const std::string& ver) {
|
std::string ExtractNumericPart(const std::string& ver) {
|
||||||
size_t start = 0;
|
size_t start = 0;
|
||||||
while (start < ver.size() && !std::isdigit(ver[start])) start++;
|
while (start < ver.size() && !std::isdigit(ver[start])) start++;
|
||||||
@@ -39,6 +43,39 @@ std::vector<int> SplitVersion(const std::string& ver) {
|
|||||||
return nums;
|
return nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extract date from version string (format: v1.2.3-20251113-abc
|
||||||
|
// or 1.2.3-20251113-abc)
|
||||||
|
std::string ExtractDateFromVersion(const std::string& version) {
|
||||||
|
size_t dash1 = version.find('-');
|
||||||
|
if (dash1 != std::string::npos) {
|
||||||
|
size_t dash2 = version.find('-', dash1 + 1);
|
||||||
|
if (dash2 != std::string::npos) {
|
||||||
|
std::string date_part = version.substr(dash1 + 1, dash2 - dash1 - 1);
|
||||||
|
|
||||||
|
bool is_date = true;
|
||||||
|
for (char c : date_part) {
|
||||||
|
if (!std::isdigit(c)) {
|
||||||
|
is_date = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_date) {
|
||||||
|
// convert YYYYMMDD to YYYY-MM-DD
|
||||||
|
return date_part.substr(0, 4) + "-" + date_part.substr(4, 2) + "-" +
|
||||||
|
date_part.substr(6, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare two dates in YYYY-MM-DD format
|
||||||
|
bool IsNewerDate(const std::string& date1, const std::string& date2) {
|
||||||
|
if (date1.empty() || date2.empty()) return false;
|
||||||
|
// simple string comparison works for ISO date format (YYYY-MM-DD)
|
||||||
|
return date2 > date1;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsNewerVersion(const std::string& current, const std::string& latest) {
|
bool IsNewerVersion(const std::string& current, const std::string& latest) {
|
||||||
auto v1 = SplitVersion(ExtractNumericPart(current));
|
auto v1 = SplitVersion(ExtractNumericPart(current));
|
||||||
auto v2 = SplitVersion(ExtractNumericPart(latest));
|
auto v2 = SplitVersion(ExtractNumericPart(latest));
|
||||||
@@ -51,6 +88,44 @@ bool IsNewerVersion(const std::string& current, const std::string& latest) {
|
|||||||
if (v2[i] > v1[i]) return true;
|
if (v2[i] > v1[i]) return true;
|
||||||
if (v2[i] < v1[i]) return false;
|
if (v2[i] < v1[i]) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if versions are equal, compare by release date
|
||||||
|
if (!latest_release_date_.empty()) {
|
||||||
|
// try to extract date from current version string
|
||||||
|
std::string current_date = ExtractDateFromVersion(current);
|
||||||
|
if (!current_date.empty()) {
|
||||||
|
return IsNewerDate(current_date, latest_release_date_);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNewerVersionWithDate(const std::string& current_version,
|
||||||
|
const std::string& current_date,
|
||||||
|
const std::string& latest_version,
|
||||||
|
const std::string& latest_date) {
|
||||||
|
// compare versions
|
||||||
|
auto v1 = SplitVersion(ExtractNumericPart(current_version));
|
||||||
|
auto v2 = SplitVersion(ExtractNumericPart(latest_version));
|
||||||
|
|
||||||
|
size_t len = std::max(v1.size(), v2.size());
|
||||||
|
v1.resize(len, 0);
|
||||||
|
v2.resize(len, 0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
if (v2[i] > v1[i]) return true;
|
||||||
|
if (v2[i] < v1[i]) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if versions are equal, compare by release date
|
||||||
|
if (!current_date.empty() && !latest_date.empty()) {
|
||||||
|
return IsNewerDate(current_date, latest_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if dates are not available, versions are equal
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,17 +139,27 @@ std::string CheckUpdate() {
|
|||||||
if (res->status == 200) {
|
if (res->status == 200) {
|
||||||
try {
|
try {
|
||||||
auto j = json::parse(res->body);
|
auto j = json::parse(res->body);
|
||||||
std::string latest = j["latest_version"];
|
std::string latest = j["version"];
|
||||||
|
if (j.contains("releaseDate") && j["releaseDate"].is_string()) {
|
||||||
|
latest_release_date_ = j["releaseDate"];
|
||||||
|
} else {
|
||||||
|
latest_release_date_ = "";
|
||||||
|
}
|
||||||
return latest;
|
return latest;
|
||||||
} catch (std::exception&) {
|
} catch (std::exception&) {
|
||||||
|
latest_release_date_ = "";
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
latest_release_date_ = "";
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
latest_release_date_ = "";
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string GetLatestReleaseDate() { return latest_release_date_; }
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
#endif
|
#endif
|
||||||
Submodule submodules/minirtc updated: 2f8b0c1ff4...29ae03237f
@@ -133,9 +133,15 @@ target("thumbnail")
|
|||||||
add_files("src/thumbnail/*.cpp")
|
add_files("src/thumbnail/*.cpp")
|
||||||
add_includedirs("src/thumbnail", {public = true})
|
add_includedirs("src/thumbnail", {public = true})
|
||||||
|
|
||||||
target("config_center")
|
target("autostart")
|
||||||
set_kind("object")
|
set_kind("object")
|
||||||
add_deps("rd_log")
|
add_deps("rd_log")
|
||||||
|
add_files("src/autostart/*.cpp")
|
||||||
|
add_includedirs("src/autostart", {public = true})
|
||||||
|
|
||||||
|
target("config_center")
|
||||||
|
set_kind("object")
|
||||||
|
add_deps("rd_log", "autostart")
|
||||||
add_files("src/config_center/*.cpp")
|
add_files("src/config_center/*.cpp")
|
||||||
add_includedirs("src/config_center", {public = true})
|
add_includedirs("src/config_center", {public = true})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user