Compare commits

..

10 Commits

18 changed files with 3235 additions and 3058 deletions

View File

@@ -15,76 +15,28 @@ env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs: jobs:
# Linux amd64 build-linux:
build-linux-amd64: name: Build Linux (${{ matrix.arch }})
name: Build on Ubuntu 22.04 amd64 runs-on: ${{ matrix.runner }}
runs-on: ubuntu-22.04
container:
image: crossdesk/ubuntu20.04:latest
options: --user root
steps:
- name: Extract version number
id: version
run: |
VERSION="${GITHUB_REF##*/}"
VERSION_NUM="${VERSION#v}"
echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV
- name: Set legal Debian version
shell: bash
id: set_deb_version
run: |
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d)
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
LEGAL_VERSION="v0.0.0-${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}"
else
LEGAL_VERSION="v${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}"
fi
echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV
echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build CrossDesk
env:
CUDA_PATH: /usr/local/cuda
XMAKE_GLOBALDIR: /data
run: |
ls -la $XMAKE_GLOBALDIR
xmake f --CROSSDESK_VERSION=${LEGAL_VERSION} --USE_CUDA=true --root -y
xmake b -vy --root crossdesk
- name: Package
run: |
chmod +x ./scripts/linux/pkg_amd64.sh
./scripts/linux/pkg_amd64.sh ${LEGAL_VERSION}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: crossdesk-linux-amd64-${{ env.LEGAL_VERSION }}
path: ${{ github.workspace }}/crossdesk-linux-amd64-${{ env.LEGAL_VERSION }}.deb
# Linux arm64
build-linux-arm64:
name: Build on Ubuntu 22.04 arm64
runs-on: ubuntu-22.04-arm
strategy: strategy:
matrix: matrix:
include: include:
- arch: amd64
runner: ubuntu-22.04
image: crossdesk/ubuntu20.04:latest
package_script: ./scripts/linux/pkg_amd64.sh
- arch: arm64 - arch: arm64
runner: ubuntu-22.04-arm
image: crossdesk/ubuntu20.04-arm64v8:latest image: crossdesk/ubuntu20.04-arm64v8:latest
package_script: ./scripts/linux/pkg_arm64.sh package_script: ./scripts/linux/pkg_arm64.sh
container: container:
image: ${{ matrix.image }} image: ${{ matrix.image }}
options: --user root options: --user root
steps: steps:
- name: Extract version number - name: Extract version number
id: version
run: | run: |
VERSION="${GITHUB_REF##*/}" VERSION="${GITHUB_REF##*/}"
VERSION_NUM="${VERSION#v}" VERSION_NUM="${VERSION#v}"
@@ -92,15 +44,16 @@ jobs:
- name: Set legal Debian version - name: Set legal Debian version
shell: bash shell: bash
id: set_deb_version
run: | run: |
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7) SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d)
if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then
LEGAL_VERSION="v0.0.0-${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}" LEGAL_VERSION="v0.0.0-${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}"
else else
LEGAL_VERSION="v${VERSION_NUM}-${BUILD_DATE}-${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 echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
@@ -289,17 +242,31 @@ jobs:
cd "${{ github.workspace }}\scripts\windows" cd "${{ github.workspace }}\scripts\windows"
makensis /DVERSION=$env:VERSION_NUM nsis_script.nsi makensis /DVERSION=$env:VERSION_NUM nsis_script.nsi
- name: Package Portable
shell: pwsh
run: |
$portableDir = "${{ github.workspace }}\portable"
New-Item -ItemType Directory -Force -Path $portableDir
Copy-Item "${{ github.workspace }}\build\windows\x64\release\crossdesk.exe" "$portableDir\CrossDesk.exe"
Compress-Archive -Path "$portableDir\*" -DestinationPath "${{ github.workspace }}\crossdesk-win-x64-portable-${{ env.VERSION_NUM }}.zip"
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: crossdesk-win-x64-${{ env.VERSION_NUM }} name: crossdesk-win-x64-${{ env.VERSION_NUM }}
path: ${{ github.workspace }}/scripts/windows/crossdesk-win-x64-${{ env.VERSION_NUM }}.exe path: ${{ github.workspace }}/scripts/windows/crossdesk-win-x64-${{ env.VERSION_NUM }}.exe
- name: Upload portable artifact
uses: actions/upload-artifact@v4
with:
name: crossdesk-win-x64-portable-${{ env.VERSION_NUM }}
path: ${{ github.workspace }}/crossdesk-win-x64-portable-${{ env.VERSION_NUM }}.zip
release: release:
name: Publish Release name: Publish Release
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
needs: needs:
[build-linux-amd64, build-linux-arm64, build-macos, build-windows-x64] [build-linux, build-macos, build-windows-x64]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -335,6 +302,7 @@ jobs:
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-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_WITH_V }}/* release/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.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_WITH_V }}/* release/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe cp artifacts/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe
cp artifacts/crossdesk-win-x64-portable-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-win-x64-portable-${{ steps.version.outputs.VERSION_WITH_V }}.zip
- name: List release files - name: List release files
run: ls -lh release/ run: ls -lh release/
@@ -392,6 +360,10 @@ jobs:
"url": "https://downloads.crossdesk.cn/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe", "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" "filename": "crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe"
}, },
"windows-x64-portable": {
"url": "https://downloads.crossdesk.cn/crossdesk-win-x64-portable-${{ steps.version.outputs.VERSION_WITH_V }}.zip",
"filename": "crossdesk-win-x64-portable-${{ steps.version.outputs.VERSION_WITH_V }}.zip"
},
"macos-x64": { "macos-x64": {
"url": "https://downloads.crossdesk.cn/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg", "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" "filename": "crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg"

View File

@@ -11,7 +11,7 @@ jobs:
update-version-json: update-version-json:
name: Update version.json with release information name: Update version.json with release information
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -25,7 +25,7 @@ jobs:
VERSION_ONLY="${TAG_NAME#v}" VERSION_ONLY="${TAG_NAME#v}"
echo "TAG_NAME=${TAG_NAME}" >> $GITHUB_OUTPUT echo "TAG_NAME=${TAG_NAME}" >> $GITHUB_OUTPUT
echo "VERSION_ONLY=${VERSION_ONLY}" >> $GITHUB_OUTPUT echo "VERSION_ONLY=${VERSION_ONLY}" >> $GITHUB_OUTPUT
# Extract date from tag if available (format: v1.2.3-20251113-abc) # Extract date from tag if available (format: v1.2.3-20251113-abc)
if [[ "${TAG_NAME}" =~ -([0-9]{8})- ]]; then if [[ "${TAG_NAME}" =~ -([0-9]{8})- ]]; then
DATE_STR="${BASH_REMATCH[1]}" DATE_STR="${BASH_REMATCH[1]}"
@@ -45,7 +45,7 @@ jobs:
# Use jq to properly escape JSON # Use jq to properly escape JSON
RELEASE_BODY="${{ github.event.release.body }}" RELEASE_BODY="${{ github.event.release.body }}"
RELEASE_NAME="${{ github.event.release.name }}" RELEASE_NAME="${{ github.event.release.name }}"
# Handle empty values # Handle empty values
if [ -z "$RELEASE_BODY" ]; then if [ -z "$RELEASE_BODY" ]; then
RELEASE_BODY="" RELEASE_BODY=""
@@ -53,15 +53,15 @@ jobs:
if [ -z "$RELEASE_NAME" ]; then if [ -z "$RELEASE_NAME" ]; then
RELEASE_NAME="" RELEASE_NAME=""
fi fi
# Save to temporary files for proper handling # Save to temporary files for proper handling
echo -n "$RELEASE_BODY" > /tmp/release_body.txt echo -n "$RELEASE_BODY" > /tmp/release_body.txt
echo -n "$RELEASE_NAME" > /tmp/release_name.txt echo -n "$RELEASE_NAME" > /tmp/release_name.txt
# Use jq to escape JSON strings # Use jq to escape JSON strings
RELEASE_BODY_JSON=$(jq -Rs . < /tmp/release_body.txt) RELEASE_BODY_JSON=$(jq -Rs . < /tmp/release_body.txt)
RELEASE_NAME_JSON=$(jq -Rs . < /tmp/release_name.txt) RELEASE_NAME_JSON=$(jq -Rs . < /tmp/release_name.txt)
echo "RELEASE_BODY=${RELEASE_BODY_JSON}" >> $GITHUB_OUTPUT echo "RELEASE_BODY=${RELEASE_BODY_JSON}" >> $GITHUB_OUTPUT
echo "RELEASE_NAME=${RELEASE_NAME_JSON}" >> $GITHUB_OUTPUT echo "RELEASE_NAME=${RELEASE_NAME_JSON}" >> $GITHUB_OUTPUT
@@ -85,7 +85,7 @@ jobs:
else else
DOWNLOADS_JSON="" DOWNLOADS_JSON=""
fi fi
# If downloads is empty, use default structure # If downloads is empty, use default structure
if [ -z "$DOWNLOADS_JSON" ]; then if [ -z "$DOWNLOADS_JSON" ]; then
DOWNLOADS_JSON=$(cat << DOWNLOADS_EOF DOWNLOADS_JSON=$(cat << DOWNLOADS_EOF
@@ -94,6 +94,10 @@ jobs:
"url": "https://downloads.crossdesk.cn/crossdesk-win-x64-${{ steps.version.outputs.TAG_NAME }}.exe", "url": "https://downloads.crossdesk.cn/crossdesk-win-x64-${{ steps.version.outputs.TAG_NAME }}.exe",
"filename": "crossdesk-win-x64-${{ steps.version.outputs.TAG_NAME }}.exe" "filename": "crossdesk-win-x64-${{ steps.version.outputs.TAG_NAME }}.exe"
}, },
"windows-x64-portable": {
"url": "https://downloads.crossdesk.cn/crossdesk-win-x64-portable-${{ steps.version.outputs.TAG_NAME }}.zip",
"filename": "crossdesk-win-x64-portable-${{ steps.version.outputs.TAG_NAME }}.zip"
},
"macos-x64": { "macos-x64": {
"url": "https://downloads.crossdesk.cn/crossdesk-macos-x64-${{ steps.version.outputs.TAG_NAME }}.pkg", "url": "https://downloads.crossdesk.cn/crossdesk-macos-x64-${{ steps.version.outputs.TAG_NAME }}.pkg",
"filename": "crossdesk-macos-x64-${{ steps.version.outputs.TAG_NAME }}.pkg" "filename": "crossdesk-macos-x64-${{ steps.version.outputs.TAG_NAME }}.pkg"
@@ -114,7 +118,7 @@ jobs:
DOWNLOADS_EOF DOWNLOADS_EOF
) )
fi fi
# Generate version.json using cat and heredoc # Generate version.json using cat and heredoc
cat > version.json << EOF cat > version.json << EOF
{ {
@@ -126,7 +130,7 @@ jobs:
"downloads": ${DOWNLOADS_JSON} "downloads": ${DOWNLOADS_JSON}
} }
EOF EOF
cat version.json cat version.json
- name: Upload version.json to server - name: Upload version.json to server
@@ -137,4 +141,4 @@ jobs:
remote_path: /var/www/html/version/ remote_path: /var/www/html/version/
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 }}

View File

@@ -0,0 +1,2 @@
// Application icon (IDI_ICON1 = 1, which is the default app icon resource ID)
IDI_ICON1 ICON "..\\..\\icons\\windows\\crossdesk.ico"

View File

@@ -46,7 +46,7 @@ ShowInstDetails show
Section "MainSection" Section "MainSection"
; Check if CrossDesk is running ; Check if CrossDesk is running
StrCpy $1 "crossdesk.exe" StrCpy $1 "CrossDesk.exe"
nsProcess::_FindProcess "$1" nsProcess::_FindProcess "$1"
Pop $R0 Pop $R0
@@ -72,10 +72,7 @@ installApp:
SetOverwrite ifnewer SetOverwrite ifnewer
; Main application executable path ; Main application executable path
File /oname=crossdesk.exe "..\..\build\windows\x64\release\crossdesk.exe" File /oname=CrossDesk.exe "..\..\build\windows\x64\release\crossdesk.exe"
; Copy icon file to installation directory
File "${MUI_ICON}"
; Write uninstall information ; Write uninstall information
WriteUninstaller "$INSTDIR\uninstall.exe" WriteUninstaller "$INSTDIR\uninstall.exe"
@@ -85,28 +82,23 @@ installApp:
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "DisplayIcon" "$INSTDIR\crossdesk.ico" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "DisplayIcon" "$INSTDIR\CrossDesk.exe"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "NoModify" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "NoRepair" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_REG_KEY}" "NoRepair" 1
WriteRegStr HKCU "Software\${PRODUCT_NAME}" "InstallDir" "$INSTDIR" WriteRegStr HKCU "Software\${PRODUCT_NAME}" "InstallDir" "$INSTDIR"
SectionEnd SectionEnd
; After installation
Section -Post
ExecWait '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\mt.exe" -manifest "$INSTDIR\crossdesk.manifest" -outputresource:"$INSTDIR\crossdesk.exe";1'
SectionEnd
Section -AdditionalIcons Section -AdditionalIcons
; Desktop shortcut ; Desktop shortcut
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\crossdesk.exe" "" "$INSTDIR\crossdesk.ico" CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\CrossDesk.exe" "" "$INSTDIR\CrossDesk.exe"
; Start menu shortcut ; Start menu shortcut
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" "$INSTDIR\crossdesk.exe" "" "$INSTDIR\crossdesk.ico" CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" "$INSTDIR\CrossDesk.exe" "" "$INSTDIR\CrossDesk.exe"
SectionEnd SectionEnd
Section "Uninstall" Section "Uninstall"
; Check if CrossDesk is running ; Check if CrossDesk is running
StrCpy $1 "crossdesk.exe" StrCpy $1 "CrossDesk.exe"
nsProcess::_FindProcess "$1" nsProcess::_FindProcess "$1"
Pop $R0 Pop $R0
@@ -129,7 +121,7 @@ cancelUninstall:
uninstallApp: uninstallApp:
; Delete main executable and uninstaller ; Delete main executable and uninstaller
Delete "$INSTDIR\crossdesk.exe" Delete "$INSTDIR\CrossDesk.exe"
Delete "$INSTDIR\uninstall.exe" Delete "$INSTDIR\uninstall.exe"
; Recursively delete installation directory ; Recursively delete installation directory
@@ -152,5 +144,5 @@ SectionEnd
; ------ Functions ------ ; ------ Functions ------
Function LaunchApp Function LaunchApp
Exec "$INSTDIR\crossdesk.exe" Exec "$INSTDIR\CrossDesk.exe"
FunctionEnd FunctionEnd

View File

@@ -74,6 +74,15 @@ int ConfigCenter::Load() {
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_);
const char* file_transfer_save_path_value =
ini_.GetValue(section_, "file_transfer_save_path", nullptr);
if (file_transfer_save_path_value != nullptr &&
strlen(file_transfer_save_path_value) > 0) {
file_transfer_save_path_ = file_transfer_save_path_value;
} else {
file_transfer_save_path_ = "";
}
return 0; return 0;
} }
@@ -104,6 +113,9 @@ int ConfigCenter::Save() {
ini_.SetBoolValue(section_, "enable_minimize_to_tray", ini_.SetBoolValue(section_, "enable_minimize_to_tray",
enable_minimize_to_tray_); enable_minimize_to_tray_);
ini_.SetValue(section_, "file_transfer_save_path",
file_transfer_save_path_.c_str());
SI_Error rc = ini_.SaveFile(config_path_.c_str()); SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) { if (rc < 0) {
return -1; return -1;
@@ -358,4 +370,19 @@ bool ConfigCenter::IsMinimizeToTray() const { return enable_minimize_to_tray_; }
bool ConfigCenter::IsEnableAutostart() const { return enable_autostart_; } bool ConfigCenter::IsEnableAutostart() const { return enable_autostart_; }
bool ConfigCenter::IsEnableDaemon() const { return enable_daemon_; } bool ConfigCenter::IsEnableDaemon() const { return enable_daemon_; }
int ConfigCenter::SetFileTransferSavePath(const std::string& path) {
file_transfer_save_path_ = path;
ini_.SetValue(section_, "file_transfer_save_path",
file_transfer_save_path_.c_str());
SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) {
return -1;
}
return 0;
}
std::string ConfigCenter::GetFileTransferSavePath() const {
return file_transfer_save_path_;
}
} // namespace crossdesk } // namespace crossdesk

View File

@@ -39,6 +39,7 @@ class ConfigCenter {
int SetMinimizeToTray(bool enable_minimize_to_tray); int SetMinimizeToTray(bool enable_minimize_to_tray);
int SetAutostart(bool enable_autostart); int SetAutostart(bool enable_autostart);
int SetDaemon(bool enable_daemon); int SetDaemon(bool enable_daemon);
int SetFileTransferSavePath(const std::string& path);
// read config // read config
@@ -59,6 +60,7 @@ class ConfigCenter {
bool IsMinimizeToTray() const; bool IsMinimizeToTray() const;
bool IsEnableAutostart() const; bool IsEnableAutostart() const;
bool IsEnableDaemon() const; bool IsEnableDaemon() const;
std::string GetFileTransferSavePath() const;
int Load(); int Load();
int Save(); int Save();
@@ -85,6 +87,7 @@ class ConfigCenter {
bool enable_minimize_to_tray_ = false; bool enable_minimize_to_tray_ = false;
bool enable_autostart_ = false; bool enable_autostart_ = false;
bool enable_daemon_ = false; bool enable_daemon_ = false;
std::string file_transfer_save_path_ = "";
}; };
} // namespace crossdesk } // namespace crossdesk
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -198,11 +198,17 @@ static std::vector<std::string> file_transfer = {
reinterpret_cast<const char*>(u8"文件传输:"), "File Transfer:"}; reinterpret_cast<const char*>(u8"文件传输:"), "File Transfer:"};
static std::vector<std::string> connection_status = { static std::vector<std::string> connection_status = {
reinterpret_cast<const char*>(u8"连接状态:"), "Connection Status:"}; reinterpret_cast<const char*>(u8"连接状态:"), "Connection Status:"};
static std::vector<std::string> file_transfer_save_path = {
#if _WIN32 reinterpret_cast<const char*>(u8"文件接收保存路径:"),
"File Transfer Save Path:"};
static std::vector<std::string> browse = {
reinterpret_cast<const char*>(u8"浏览"), "Browse"};
static std::vector<std::string> default_desktop = {
reinterpret_cast<const char*>(u8"桌面"), "Desktop"};
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:"};
#if _WIN32
static std::vector<LPCWSTR> exit_program = {L"退出", L"Exit"}; static std::vector<LPCWSTR> exit_program = {L"退出", L"Exit"};
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__

View File

@@ -492,6 +492,16 @@ int Render::LoadSettingsFromCacheFile() {
enable_daemon_ = config_center_->IsEnableDaemon(); enable_daemon_ = config_center_->IsEnableDaemon();
enable_minimize_to_tray_ = config_center_->IsMinimizeToTray(); enable_minimize_to_tray_ = config_center_->IsMinimizeToTray();
// File transfer save path
{
std::string saved_path = config_center_->GetFileTransferSavePath();
strncpy(file_transfer_save_path_buf_, saved_path.c_str(),
sizeof(file_transfer_save_path_buf_) - 1);
file_transfer_save_path_buf_[sizeof(file_transfer_save_path_buf_) - 1] =
'\0';
file_transfer_save_path_last_ = saved_path;
}
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_;
video_encode_format_button_value_last_ = video_encode_format_button_value_; video_encode_format_button_value_last_ = video_encode_format_button_value_;
@@ -883,7 +893,7 @@ void Render::UpdateInteractions() {
mouse_controller_is_started_ = false; mouse_controller_is_started_ = false;
} }
if (start_keyboard_capturer_ && foucs_on_stream_window_) { if (start_keyboard_capturer_ && focus_on_stream_window_) {
if (!keyboard_capturer_is_started_) { if (!keyboard_capturer_is_started_) {
StartKeyboardCapturer(); StartKeyboardCapturer();
keyboard_capturer_is_started_ = true; keyboard_capturer_is_started_ = true;
@@ -1383,23 +1393,19 @@ int Render::DrawServerWindow() {
LOG_ERROR("Server context is null"); LOG_ERROR("Server context is null");
return -1; return -1;
} }
if (server_window_) {
int w = 0;
int h = 0;
SDL_GetWindowSize(server_window_, &w, &h);
if (w > 0 && h > 0) {
server_window_width_ = (float)w;
server_window_height_ = (float)h;
}
}
ImGui::SetCurrentContext(server_ctx_); ImGui::SetCurrentContext(server_ctx_);
ImGui_ImplSDLRenderer3_NewFrame(); ImGui_ImplSDLRenderer3_NewFrame();
ImGui_ImplSDL3_NewFrame(); ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame(); ImGui::NewFrame();
ImGuiIO& io = ImGui::GetIO();
server_window_width_ = io.DisplaySize.x;
server_window_height_ = io.DisplaySize.y;
ServerWindow(); ServerWindow();
ImGui::Render(); ImGui::Render();
SDL_SetRenderScale(server_renderer_, io.DisplayFramebufferScale.x,
io.DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(server_renderer_, 255, 255, 255, 255); SDL_SetRenderDrawColor(server_renderer_, 255, 255, 255, 255);
SDL_RenderClear(server_renderer_); SDL_RenderClear(server_renderer_);
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), server_renderer_); ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), server_renderer_);
@@ -2166,7 +2172,7 @@ void Render::ProcessSdlEvent(const SDL_Event& event) {
case SDL_EVENT_WINDOW_FOCUS_GAINED: case SDL_EVENT_WINDOW_FOCUS_GAINED:
if (stream_window_ && if (stream_window_ &&
SDL_GetWindowID(stream_window_) == event.window.windowID) { SDL_GetWindowID(stream_window_) == event.window.windowID) {
foucs_on_stream_window_ = true; focus_on_stream_window_ = true;
} else if (main_window_ && } else if (main_window_ &&
SDL_GetWindowID(main_window_) == event.window.windowID) { SDL_GetWindowID(main_window_) == event.window.windowID) {
foucs_on_main_window_ = true; foucs_on_main_window_ = true;
@@ -2176,7 +2182,7 @@ void Render::ProcessSdlEvent(const SDL_Event& event) {
case SDL_EVENT_WINDOW_FOCUS_LOST: case SDL_EVENT_WINDOW_FOCUS_LOST:
if (stream_window_ && if (stream_window_ &&
SDL_GetWindowID(stream_window_) == event.window.windowID) { SDL_GetWindowID(stream_window_) == event.window.windowID) {
foucs_on_stream_window_ = false; focus_on_stream_window_ = false;
} else if (main_window_ && } else if (main_window_ &&
SDL_GetWindowID(main_window_) == event.window.windowID) { SDL_GetWindowID(main_window_) == event.window.windowID) {
foucs_on_main_window_ = false; foucs_on_main_window_ = false;
@@ -2190,7 +2196,7 @@ void Render::ProcessSdlEvent(const SDL_Event& event) {
case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP: case SDL_EVENT_MOUSE_BUTTON_UP:
case SDL_EVENT_MOUSE_WHEEL: case SDL_EVENT_MOUSE_WHEEL:
if (foucs_on_stream_window_) { if (focus_on_stream_window_) {
ProcessMouseEvent(event); ProcessMouseEvent(event);
} }
break; break;

View File

@@ -434,7 +434,7 @@ class Render {
bool show_cursor_ = false; 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 focus_on_stream_window_ = false;
bool main_window_minimized_ = false; bool main_window_minimized_ = false;
uint32_t last_main_minimize_request_tick_ = 0; uint32_t last_main_minimize_request_tick_ = 0;
uint32_t last_stream_minimize_request_tick_ = 0; uint32_t last_stream_minimize_request_tick_ = 0;
@@ -646,6 +646,8 @@ class Render {
bool enable_daemon_last_ = false; bool enable_daemon_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 file_transfer_save_path_buf_[512] = "";
std::string file_transfer_save_path_last_ = "";
char signal_server_ip_self_[256] = ""; char signal_server_ip_self_[256] = "";
char signal_server_port_self_[6] = ""; char signal_server_port_self_[6] = "";
char coturn_server_port_self_[6] = ""; char coturn_server_port_self_[6] = "";

View File

@@ -321,6 +321,14 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
std::string remote_user_id = std::string(user_id, user_id_size); std::string remote_user_id = std::string(user_id, user_id_size);
static FileReceiver receiver; static FileReceiver receiver;
// Update output directory from config
std::string configured_path =
render->config_center_->GetFileTransferSavePath();
if (!configured_path.empty()) {
receiver.SetOutputDir(std::filesystem::u8path(configured_path));
} else if (receiver.OutputDir().empty()) {
receiver = FileReceiver(); // re-init with default desktop path
}
receiver.SetOnSendAck([render, receiver.SetOnSendAck([render,
remote_user_id](const FileTransferAck& ack) -> int { remote_user_id](const FileTransferAck& ack) -> int {
bool is_server_sending = remote_user_id.rfind("C-", 0) != 0; bool is_server_sending = remote_user_id.rfind("C-", 0) != 0;

View File

@@ -1,6 +1,10 @@
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#if defined(_WIN32)
#include <windows.h>
#endif
#include "layout.h" #include "layout.h"
#include "localization.h" #include "localization.h"
#include "rd_log.h" #include "rd_log.h"
@@ -24,13 +28,34 @@ void Render::Hyperlink(const std::string& label, const std::string& url,
ImGui::EndTooltip(); ImGui::EndTooltip();
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
#if defined(_WIN32) #if defined(_WIN32)
std::string cmd = "start " + url; int wide_len =
MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, nullptr, 0);
if (wide_len > 0) {
std::wstring wide_url(static_cast<size_t>(wide_len), L'\0');
MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, &wide_url[0],
wide_len);
if (!wide_url.empty() && wide_url.back() == L'\0') {
wide_url.pop_back();
}
std::wstring cmd = L"cmd.exe /c start \"\" \"" + wide_url + L"\"";
STARTUPINFOW startup_info = {sizeof(startup_info)};
PROCESS_INFORMATION process_info = {};
if (CreateProcessW(nullptr, &cmd[0], nullptr, nullptr, FALSE,
CREATE_NO_WINDOW, nullptr, nullptr, &startup_info,
&process_info)) {
CloseHandle(process_info.hThread);
CloseHandle(process_info.hProcess);
}
}
#elif defined(__APPLE__) #elif defined(__APPLE__)
std::string cmd = "open " + url; std::string cmd = "open " + url;
#else #else
std::string cmd = "xdg-open " + url; std::string cmd = "xdg-open " + url;
#endif #endif
#if !defined(_WIN32)
system(cmd.c_str()); // open browser system(cmd.c_str()); // open browser
#endif
} }
} }
} }

View File

@@ -97,7 +97,7 @@ int Render::FileTransferWindow(
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 0.9f)); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 0.9f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f));
ImGui::PushStyleColor(ImGuiCol_TitleBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_TitleBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));

View File

@@ -2,6 +2,7 @@
#include "localization.h" #include "localization.h"
#include "rd_log.h" #include "rd_log.h"
#include "render.h" #include "render.h"
#include "tinyfiledialogs.h"
namespace crossdesk { namespace crossdesk {
@@ -15,28 +16,28 @@ int Render::SettingWindow() {
!defined(__arm__) && USE_CUDA) || \ !defined(__arm__) && USE_CUDA) || \
defined(__APPLE__)) defined(__APPLE__))
ImGui::SetNextWindowPos( ImGui::SetNextWindowPos(
ImVec2(io.DisplaySize.x * 0.343f, io.DisplaySize.y * 0.07f)); ImVec2(io.DisplaySize.x * 0.343f, io.DisplaySize.y * 0.05f));
ImGui::SetNextWindowSize( ImGui::SetNextWindowSize(
ImVec2(io.DisplaySize.x * 0.315f, io.DisplaySize.y * 0.85f)); ImVec2(io.DisplaySize.x * 0.315f, io.DisplaySize.y * 0.9f));
#else #else
ImGui::SetNextWindowPos( ImGui::SetNextWindowPos(
ImVec2(io.DisplaySize.x * 0.343f, io.DisplaySize.y * 0.1f)); ImVec2(io.DisplaySize.x * 0.343f, io.DisplaySize.y * 0.08f));
ImGui::SetNextWindowSize( ImGui::SetNextWindowSize(
ImVec2(io.DisplaySize.x * 0.315f, io.DisplaySize.y * 0.8f)); ImVec2(io.DisplaySize.x * 0.315f, io.DisplaySize.y * 0.85f));
#endif #endif
} else { } else {
#if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \ #if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \
!defined(__arm__) && USE_CUDA) || \ !defined(__arm__) && USE_CUDA) || \
defined(__APPLE__)) defined(__APPLE__))
ImGui::SetNextWindowPos( ImGui::SetNextWindowPos(
ImVec2(io.DisplaySize.x * 0.297f, io.DisplaySize.y * 0.07f)); ImVec2(io.DisplaySize.x * 0.297f, io.DisplaySize.y * 0.05f));
ImGui::SetNextWindowSize( ImGui::SetNextWindowSize(
ImVec2(io.DisplaySize.x * 0.407f, io.DisplaySize.y * 0.85f)); ImVec2(io.DisplaySize.x * 0.407f, io.DisplaySize.y * 0.9f));
#else #else
ImGui::SetNextWindowPos( ImGui::SetNextWindowPos(
ImVec2(io.DisplaySize.x * 0.297f, io.DisplaySize.y * 0.1f)); ImVec2(io.DisplaySize.x * 0.297f, io.DisplaySize.y * 0.08f));
ImGui::SetNextWindowSize( ImGui::SetNextWindowSize(
ImVec2(io.DisplaySize.x * 0.407f, io.DisplaySize.y * 0.8f)); ImVec2(io.DisplaySize.x * 0.407f, io.DisplaySize.y * 0.85f));
#endif #endif
} }
@@ -330,10 +331,14 @@ int Render::SettingWindow() {
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
} }
#if _WIN32
ImGui::Separator(); ImGui::Separator();
{ {
#ifndef _WIN32
ImGui::BeginDisabled();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
#endif
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset); ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
@@ -349,8 +354,67 @@ int Render::SettingWindow() {
ImGui::Checkbox("##enable_minimize_to_tray_", ImGui::Checkbox("##enable_minimize_to_tray_",
&enable_minimize_to_tray_); &enable_minimize_to_tray_);
} #ifndef _WIN32
ImGui::PopStyleColor();
ImGui::EndDisabled();
#endif #endif
}
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset);
ImGui::AlignTextToFramePadding();
ImGui::Text(
"%s",
localization::file_transfer_save_path[localization_language_index_]
.c_str());
ImGui::SameLine();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(title_bar_button_width_ * 2.8f);
} else {
ImGui::SetCursorPosX(title_bar_button_width_ * 4.3f);
}
std::string display_path =
strlen(file_transfer_save_path_buf_) > 0
? file_transfer_save_path_buf_
: localization::default_desktop[localization_language_index_];
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
ImVec4(0.95f, 0.95f, 0.95f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
ImVec4(0.9f, 0.9f, 0.9f, 1.0f));
ImGui::PushFont(main_windows_system_chinese_font_);
if (ImGui::Button(display_path.c_str(),
ImVec2(title_bar_button_width_ * 2.0f, 0))) {
const char* folder =
tinyfd_selectFolderDialog(localization::file_transfer_save_path
[localization_language_index_]
.c_str(),
strlen(file_transfer_save_path_buf_) > 0
? file_transfer_save_path_buf_
: nullptr);
if (folder) {
strncpy(file_transfer_save_path_buf_, folder,
sizeof(file_transfer_save_path_buf_) - 1);
file_transfer_save_path_buf_[sizeof(file_transfer_save_path_buf_) -
1] = '\0';
}
}
if (ImGui::IsItemHovered() &&
strlen(file_transfer_save_path_buf_) > 0) {
ImGui::BeginTooltip();
ImGui::SetWindowFontScale(0.5f);
ImGui::Text("%s", file_transfer_save_path_buf_);
ImGui::SetWindowFontScale(1.0f);
ImGui::EndTooltip();
}
ImGui::PopFont();
ImGui::PopStyleColor(3);
}
if (stream_window_inited_) { if (stream_window_inited_) {
ImGui::EndDisabled(); ImGui::EndDisabled();
} }
@@ -469,6 +533,10 @@ int Render::SettingWindow() {
enable_minimize_to_tray_last_ = enable_minimize_to_tray_; enable_minimize_to_tray_last_ = enable_minimize_to_tray_;
#endif #endif
// File transfer save path
config_center_->SetFileTransferSavePath(file_transfer_save_path_buf_);
file_transfer_save_path_last_ = file_transfer_save_path_buf_;
settings_window_pos_reset_ = true; settings_window_pos_reset_ = true;
// Recreate peer instance // Recreate peer instance
@@ -516,6 +584,13 @@ int Render::SettingWindow() {
enable_turn_ = enable_turn_last_; enable_turn_ = enable_turn_last_;
} }
// Restore file transfer save path
strncpy(file_transfer_save_path_buf_,
file_transfer_save_path_last_.c_str(),
sizeof(file_transfer_save_path_buf_) - 1);
file_transfer_save_path_buf_[sizeof(file_transfer_save_path_buf_) - 1] =
'\0';
settings_window_pos_reset_ = true; settings_window_pos_reset_ = true;
} }
ImGui::SetWindowFontScale(0.5f); ImGui::SetWindowFontScale(0.5f);

View File

@@ -138,7 +138,7 @@ int Render::StreamWindow() {
UpdateRenderRect(); UpdateRenderRect();
ControlWindow(props); ControlWindow(props);
// Show file transfer window if needed // Show file transfer window if needed
FileTransferWindow(props); FileTransferWindow(props);
@@ -151,12 +151,12 @@ int Render::StreamWindow() {
// std::unique_lock unique_lock(client_properties_mutex_); // std::unique_lock unique_lock(client_properties_mutex_);
auto erase_it = client_properties_.find(remote_id_to_erase); auto erase_it = client_properties_.find(remote_id_to_erase);
if (erase_it != client_properties_.end()) { if (erase_it != client_properties_.end()) {
erase_it = client_properties_.erase(erase_it); // Ensure we flush pending STREAM_REFRESH_EVENT events and
if (client_properties_.empty()) { // clean up peer resources before erasing the entry, otherwise
SDL_Event event; // SDL events may still hold raw pointers to freed
event.type = SDL_EVENT_QUIT; // SubStreamWindowProperties (including video_frame_mutex_),
SDL_PushEvent(&event); // leading to std::system_error when locking.
} CloseTab(erase_it);
} }
} }
// lock.lock(); // lock.lock();
@@ -236,10 +236,10 @@ int Render::StreamWindow() {
UpdateRenderRect(); UpdateRenderRect();
ControlWindow(props); ControlWindow(props);
// Show file transfer window if needed // Show file transfer window if needed
FileTransferWindow(props); FileTransferWindow(props);
ImGui::End(); ImGui::End();
if (!props->peer_) { if (!props->peer_) {
@@ -251,12 +251,7 @@ int Render::StreamWindow() {
// std::unique_lock unique_lock(client_properties_mutex_); // std::unique_lock unique_lock(client_properties_mutex_);
auto erase_it = client_properties_.find(remote_id_to_erase); auto erase_it = client_properties_.find(remote_id_to_erase);
if (erase_it != client_properties_.end()) { if (erase_it != client_properties_.end()) {
client_properties_.erase(erase_it); CloseTab(erase_it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
}
} }
} }
// lock.lock(); // lock.lock();

View File

@@ -97,6 +97,14 @@ class FileReceiver {
const std::filesystem::path& OutputDir() const { return output_dir_; } const std::filesystem::path& OutputDir() const { return output_dir_; }
void SetOutputDir(const std::filesystem::path& dir) {
output_dir_ = dir;
if (!output_dir_.empty()) {
std::error_code ec;
std::filesystem::create_directories(output_dir_, ec);
}
}
private: private:
static std::filesystem::path GetDefaultDesktopPath(); static std::filesystem::path GetDefaultDesktopPath();

View File

@@ -203,4 +203,7 @@ target("crossdesk")
set_kind("binary") set_kind("binary")
add_deps("rd_log", "common", "gui") add_deps("rd_log", "common", "gui")
add_files("src/app/*.cpp") add_files("src/app/*.cpp")
add_includedirs("src/app", {public = true}) add_includedirs("src/app", {public = true})
if is_os("windows") then
add_files("scripts/windows/crossdesk.rc")
end