[ Content contribution] deepin-music Windows (MSYS2 UCRT64) 移植编译指南
Tofloor
poster avatar
deepin-superuser
deepin
3 hours ago
Author

deepin-music Windows (MSYS2 UCRT64) 移植编译指南

1. 环境准备

1.1 安装 MSYS2

msys2.org 下载安装 MSYS2。

1.2 打开 UCRT64 终端

启动 UCRT64 终端 (ucrt64.exe),不要使用 MSYS Shell (msys2.exe)。

终端 编译器 用途
ucrt64.exe MinGW-w64 GCC + UCRT 推荐,原生 Windows 程序
mingw64.exe MinGW-w64 GCC + MSVCRT 可用
msys2.exe Cygwin GCC 不推荐,产物依赖 msys-2.0.dll

1.3 安装编译工具链

pacman -S base-devel
pacman -S mingw-w64-ucrt-x86_64-{cmake,gcc,pkg-config}

1.4 安装依赖包

# Qt6 全家桶
pacman -S mingw-w64-ucrt-x86_64-{qt6-base,qt6-multimedia,qt6-declarative,qt6-tools,qt6-svg,qt6-5compat,qt6-shadertools}

# DTK 依赖
pacman -S mingw-w64-ucrt-x86_64-{icu,spdlog,uchardet,dbus}

# deepin-music 依赖
pacman -S mingw-w64-ucrt-x86_64-{taglib,ffmpeg,vlc,SDL2}

2. 编译顺序

依赖链如下,必须严格按顺序编译:

dtkcommon → dtklog → dtk6core → dtk6gui → dtk6widget → dtk6declarative → deepin-music

3. 编译步骤

所有项目统一安装到 UCRT64 的 /ucrt64 目录下。

3.1 编译 dtkcommon

无外部依赖,纯 cmake 配置安装。

cd /c/workspace/deepin/dtkcommon
rm -rf build
cmake -B build -G "MinGW Makefiles" \
  -DCMAKE_INSTALL_PREFIX=/ucrt64
cmake --build build -j6
cmake --install build

3.2 编译 dtklog

依赖 Qt6 Core、spdlog。需要禁用 systemd。

cd /c/workspace/deepin/dtklog
rm -rf build
cmake -B build -G "MinGW Makefiles" \
  -DCMAKE_INSTALL_PREFIX=/ucrt64 \
  -DDTK5=OFF \
  -DBUILD_WITH_SYSTEMD=OFF
cmake --build build -j6
cmake --install build

3.3 编译 dtk6core

依赖 dtkcommon、dtklog、Qt6 Core/Xml/DBus、ICU、uchardet。

cd /c/workspace/deepin/dtkcore
rm -rf build
cmake -B build -G "MinGW Makefiles" \
  -DCMAKE_INSTALL_PREFIX=/ucrt64 \
  -DDTK5=OFF \
  -DBUILD_WITH_SYSTEMD=OFF \
  -DBUILD_EXAMPLES=OFF \
  -DBUILD_DOCS=OFF
cmake --build build -j6
cmake --install build

3.4 编译 dtk6gui

依赖 dtk6core、Qt6 Gui/DBus/Network。禁用 librsvg、libxdg、freeimage、libraw。

cd /c/workspace/deepin/dtkgui
rm -rf build
cmake -B build -G "MinGW Makefiles" \
  -DCMAKE_INSTALL_PREFIX=/ucrt64 \
  -DDTK5=OFF \
  -DDTK_DISABLE_LIBXDG=ON \
  -DDTK_DISABLE_LIBRSVG=ON \
  -DDTK_DISABLE_EX_IMAGE_FORMAT=ON \
  -DBUILD_EXAMPLES=OFF \
  -DBUILD_DOCS=OFF
cmake --build build -j6
cmake --install build

3.5 编译 dtk6widget

依赖 dtk6gui、dtk6core、Qt6 Widgets/DBus/PrintSupport/Svg。

cd /c/workspace/deepin/dtkwidget
rm -rf build
cmake -B build -G "MinGW Makefiles" \
  -DCMAKE_INSTALL_PREFIX=/ucrt64 \
  -DDTK5=OFF \
  -DBUILD_EXAMPLES=OFF \
  -DBUILD_PLUGINS=OFF \
  -DBUILD_DOCS=OFF
cmake --build build -j6
cmake --install build

3.6 编译 dtk6declarative

依赖 dtk6gui、Qt6 Quick/QuickControls2/DBus。

cd /c/workspace/deepin/dtkdeclarative
rm -rf build
cmake -B build -G "MinGW Makefiles" \
  -DCMAKE_INSTALL_PREFIX=/ucrt64 \
  -DDTK5=OFF \
  -DBUILD_DOCS=OFF
cmake --build build -j6
cmake --install build

3.7 编译 deepin-music

依赖以上全部 + VLC、taglib、ffmpeg、SDL2。

cd /c/workspace/deepin/deepin-music
rm -rf build
cmake -B build -G "MinGW Makefiles" \
  -DCMAKE_INSTALL_PREFIX=/ucrt64
cmake --build build -j6

4. 一键编译脚本

将以下内容保存为 build-all.sh,在 UCRT64 终端中执行:

#!/bin/bash
set -e

export PREFIX=/ucrt64
export CMAKE_PREFIX_PATH=/ucrt64
SRC_DIR=/c/workspace/deepin

echo "=== [1/7] 编译 dtkcommon ==="
cd "$SRC_DIR/dtkcommon"
rm -rf build
cmake -B build -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$PREFIX
cmake --build build -j6
cmake --install build

echo "=== [2/7] 编译 dtklog ==="
cd "$SRC_DIR/dtklog"
rm -rf build
cmake -B build -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$PREFIX -DDTK5=OFF -DBUILD_WITH_SYSTEMD=OFF
cmake --build build -j6
cmake --install build

echo "=== [3/7] 编译 dtk6core ==="
cd "$SRC_DIR/dtkcore"
rm -rf build
cmake -B build -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$PREFIX -DDTK5=OFF -DBUILD_WITH_SYSTEMD=OFF -DBUILD_EXAMPLES=OFF -DBUILD_DOCS=OFF
cmake --build build -j6
cmake --install build

echo "=== [4/7] 编译 dtk6gui ==="
cd "$SRC_DIR/dtkgui"
rm -rf build
cmake -B build -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$PREFIX -DDTK5=OFF -DDTK_DISABLE_LIBXDG=ON -DDTK_DISABLE_LIBRSVG=ON -DDTK_DISABLE_EX_IMAGE_FORMAT=ON -DBUILD_EXAMPLES=OFF -DBUILD_DOCS=OFF
cmake --build build -j6
cmake --install build

echo "=== [5/7] 编译 dtk6widget ==="
cd "$SRC_DIR/dtkwidget"
rm -rf build
cmake -B build -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$PREFIX -DDTK5=OFF -DBUILD_EXAMPLES=OFF -DBUILD_PLUGINS=OFF -DBUILD_DOCS=OFF
cmake --build build -j6
cmake --install build

echo "=== [6/7] 编译 dtk6declarative ==="
cd "$SRC_DIR/dtkdeclarative"
rm -rf build
cmake -B build -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$PREFIX -DDTK5=OFF -DBUILD_DOCS=OFF
cmake --build build -j6
cmake --install build

echo "=== [7/7] 编译 deepin-music ==="
cd "$SRC_DIR/deepin-music"
rm -rf build
cmake -B build -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$PREFIX
cmake --build build -j6

echo "=== 全部编译完成 ==="

5. 发布部署

编译完成后,使用 PowerShell 脚本部署。

5.1 首次完整部署 — deploy.ps1

首次部署或依赖变化时执行。自动从 ucrt64 解析并复制所有 DLL 依赖。将以下内容保存为 deepin-music/deploy.ps1

# Deepin Music Windows Deploy Script
# Copies built binaries and auto-resolves all dependencies from ucrt64

$ErrorActionPreference = "Stop"

$BUILD_DIR = "C:\Workspace\deepin\deepin-music\build"
$DEPLOY_DIR = "C:\Workspace\deepin\deepin-music\deploy"
$QT_BIN = "C:\msys64\ucrt64\bin"
$DTK_LIB = "C:\msys64\ucrt64\lib"
$BASH = "C:\msys64\usr\bin\bash.exe"

Write-Host "=== Deploying Deepin Music ===" -ForegroundColor Green

# 0. Kill running deepin-music process if exists
Write-Host "Stopping deepin-music process..." -ForegroundColor Yellow
$procs = Get-Process -Name "deepin-music" -ErrorAction SilentlyContinue
if ($procs) {
    $procs | Stop-Process -Force
    Write-Host "  - Killed deepin-music process"
    Start-Sleep -Seconds 1
} else {
    Write-Host "  - No deepin-music process running"
}

# 0b. Clear deploy directory
Write-Host "Clearing deploy directory..." -ForegroundColor Yellow
if (Test-Path $DEPLOY_DIR) {
    Remove-Item "$DEPLOY_DIR\*" -Recurse -Force
    Write-Host "  - Deploy directory cleared"
} else {
    New-Item -ItemType Directory -Path $DEPLOY_DIR -Force | Out-Null
    Write-Host "  - Deploy directory created"
}

# 1. Copy new build output
Write-Host "Copying build output..." -ForegroundColor Yellow
Copy-Item "$BUILD_DIR\src\music-player\deepin-music.exe" "$DEPLOY_DIR\" -Force
Copy-Item "$BUILD_DIR\src\libdmusic\libdmusic.dll" "$DEPLOY_DIR\" -Force
Write-Host "  - deepin-music.exe"
Write-Host "  - libdmusic.dll"

# 2. Copy DTK libraries
Write-Host "Copying DTK libraries..." -ForegroundColor Yellow
$dtkDlls = @(
    @{ Src = "$DTK_LIB\dtk6declarative.dll"; Dst = "dtk6declarative.dll" },
    @{ Src = "$DTK_LIB\libdtk6gui.dll"; Dst = "libdtk6gui.dll" },
    @{ Src = "$DTK_LIB\libdtk6core.dll"; Dst = "libdtk6core.dll" },
    @{ Src = "$DTK_LIB\libdtk6widget.dll"; Dst = "libdtk6widget.dll" },
    @{ Src = "$DTK_LIB\libdtk6log.dll"; Dst = "libdtk6log.dll" }
)
foreach ($dll in $dtkDlls) {
    if (Test-Path $dll.Src) {
        Copy-Item $dll.Src "$DEPLOY_DIR\$($dll.Dst)" -Force
        Write-Host "  - $($dll.Dst)"
    } else {
        Write-Host "  WARNING: $($dll.Dst) not found at $($dll.Src)" -ForegroundColor Red
    }
}

# 3. Copy ALL QML modules
Write-Host "Copying QML modules..." -ForegroundColor Yellow
$qmlModulesSrc = "C:\msys64\ucrt64\share\qt6\qml"
$qmlModulesDst = "$DEPLOY_DIR\qml"
if (Test-Path $qmlModulesSrc) {
    if (Test-Path $qmlModulesDst) { Remove-Item $qmlModulesDst -Recurse -Force }
    Copy-Item $qmlModulesSrc $qmlModulesDst -Recurse -Force
    $count = (Get-ChildItem $qmlModulesDst -Directory).Count
    Write-Host "  - $count QML modules copied"
}
$dtkQmlSrc = "C:\msys64\ucrt64\lib\qt6\qml"
if (Test-Path $dtkQmlSrc) {
    Copy-Item "$dtkQmlSrc\*" "$qmlModulesDst\" -Recurse -Force
    Write-Host "  - DTK QML modules merged"
}

# 4. Copy VLC plugins
Write-Host "Copying VLC plugins..." -ForegroundColor Yellow
$vlcPluginsSrc = "C:\msys64\ucrt64\lib\vlc\plugins"
$vlcPluginsDst = "$DEPLOY_DIR\plugins\vlc\plugins"
if (Test-Path $vlcPluginsSrc) {
    if (Test-Path $vlcPluginsDst) { Remove-Item $vlcPluginsDst -Recurse -Force }
    Copy-Item $vlcPluginsSrc $vlcPluginsDst -Recurse -Force
    Write-Host "  - plugins/"
}

# 5. Copy Qt6 plugins
Write-Host "Copying Qt6 plugins..." -ForegroundColor Yellow
$qtPluginsSrc = "C:\msys64\ucrt64\share\qt6\plugins"
$qtPluginDirs = @("platforms", "iconengines", "imageformats", "styles", "tls", "generic", "sqldrivers")
foreach ($dir in $qtPluginDirs) {
    $src = Join-Path $qtPluginsSrc $dir
    $dst = Join-Path "$DEPLOY_DIR\plugins" $dir
    if (Test-Path $src) {
        if (-not (Test-Path $dst)) { New-Item -ItemType Directory -Path $dst -Force | Out-Null }
        Copy-Item "$src\*.dll" $dst -Force
        Write-Host "  - $dir/"
    }
}

# 5c. Copy dynamically loaded DLLs (QLibrary, not in ldd output)
Write-Host "Copying dynamically loaded DLLs..." -ForegroundColor Yellow
$dynamicDlls = @(
    "libvlc.dll",
    "SDL2.dll"
)
foreach ($dll in $dynamicDlls) {
    $src = Join-Path $QT_BIN $dll
    if (Test-Path $src) {
        Copy-Item $src "$DEPLOY_DIR\" -Force
        Write-Host "  - $dll"
    }
}

# 6. Auto-resolve ALL DLL dependencies (AFTER plugins are in place)
Write-Host "Auto-resolving DLL dependencies (ldd)..." -ForegroundColor Yellow
$totalCopied = 0

$scanScript = @'
export PATH=/ucrt64/bin:/c/workspace/deepin/deepin-music/deploy:/usr/bin
cd /c/workspace/deepin/deepin-music/deploy
find . -name "*.dll" -o -name "*.exe" | while read dll; do
    ldd "$dll" 2>/dev/null
done | grep "=>" | awk '{print $3}' | sort -u | while read f; do
    b=$(basename "$f")
    if echo "$f" | grep -q "/ucrt64/bin/"; then
        echo "$b"
    fi
done | sort -u
'@
$scanScriptPath = "$DEPLOY_DIR\_scan.sh"
[System.IO.File]::WriteAllText($scanScriptPath, $scanScript)

for ($iter = 0; $iter -lt 5; $iter++) {
    $ucrtDlls = & $BASH $scanScriptPath 2>$null | Where-Object { $_ -ne "" }
    if (-not $ucrtDlls -or $ucrtDlls.Count -eq 0) { break }

    $newCount = 0
    foreach ($dll in $ucrtDlls) {
        $dst = Join-Path $DEPLOY_DIR $dll
        if (-not (Test-Path $dst)) {
            $src = Join-Path $QT_BIN $dll
            if (Test-Path $src) {
                Copy-Item $src "$DEPLOY_DIR\" -Force
                $totalCopied++
                $newCount++
                Write-Host "  - $dll"
            }
        }
    }
    if ($newCount -eq 0) { break }
}
Remove-Item $scanScriptPath -Force -ErrorAction SilentlyContinue
Write-Host "  - Copied $totalCopied ucrt64 DLLs"

# 7. Copy translation files
Write-Host "Copying translation files..." -ForegroundColor Yellow
$transDst = "$DEPLOY_DIR\translations"
if (-not (Test-Path $transDst)) { New-Item -ItemType Directory -Path $transDst -Force | Out-Null }

$deepinTransSrc = "$BUILD_DIR\src\music-player"
if (Test-Path $deepinTransSrc) {
    Get-ChildItem "$deepinTransSrc\deepin-music_zh_CN*.qm" | ForEach-Object {
        Copy-Item $_.FullName "$transDst\" -Force
        Write-Host "  - $($_.Name)"
    }
}

$transSrc = @(
    "C:\msys64\ucrt64\share\dtk6\DDeclarative\translations\dtkdeclarative_zh_CN.qm",
    "C:\msys64\ucrt64\share\dtk6\DWidget\translations\dtkwidget_zh_CN.qm",
    "C:\msys64\ucrt64\share\qt6\translations\qtbase_zh_CN.qm",
    "C:\msys64\ucrt64\share\qt6\translations\qt_zh_CN.qm"
)
foreach ($src in $transSrc) {
    if (Test-Path $src) {
        Copy-Item $src "$transDst\" -Force
        Write-Host "  - $(Split-Path $src -Leaf)"
    }
}

# 8. Copy DCI icons
Write-Host "Copying DCI icons..." -ForegroundColor Yellow
$dciIconsSrc = "C:\Workspace\deepin\deepin-music\src\music-player\dsg\icons"
$dciIconsDst = "$DEPLOY_DIR\dsg\icons"
if (Test-Path $dciIconsSrc) {
    if (-not (Test-Path $dciIconsDst)) { New-Item -ItemType Directory -Path $dciIconsDst -Force | Out-Null }
    Copy-Item "$dciIconsSrc\*.dci" $dciIconsDst -Force
    Write-Host "  - dsg/icons/*.dci"
}

$totalDlls = (Get-ChildItem "$DEPLOY_DIR\*.dll" -ErrorAction SilentlyContinue).Count
Write-Host "`n=== Deploy Complete ($totalDlls DLLs) ===" -ForegroundColor Green
Write-Host "Run: $DEPLOY_DIR\deepin-music.exe"

5.2 增量快速部署 — deploy-fast.ps1

仅修改 deepin-music 代码重新编译后执行。只更新 exe、DLL 和翻译,跳过所有依赖和插件。将以下内容保存为 deepin-music/deploy-fast.ps1

# Deepin Music Fast Deploy Script
# Only updates build output, DTK libs, and translations
# All other files (DLLs, plugins, QML) are preserved from previous full deploy

$ErrorActionPreference = "Stop"

$BUILD_DIR = "C:\Workspace\deepin\deepin-music\build"
$DEPLOY_DIR = "C:\Workspace\deepin\deepin-music\deploy"
$DTK_LIB = "C:\msys64\ucrt64\lib"

Write-Host "=== Fast Deploy Deepin Music ===" -ForegroundColor Green

# 0. Kill running deepin-music process
$procs = Get-Process -Name "deepin-music" -ErrorAction SilentlyContinue
if ($procs) {
    $procs | Stop-Process -Force
    Write-Host "Killed deepin-music process" -ForegroundColor Yellow
    Start-Sleep -Seconds 1
}

# 1. Copy build output (the only things that change each rebuild)
Write-Host "Copying build output..." -ForegroundColor Yellow
Copy-Item "$BUILD_DIR\src\music-player\deepin-music.exe" "$DEPLOY_DIR\" -Force
Copy-Item "$BUILD_DIR\src\libdmusic\libdmusic.dll" "$DEPLOY_DIR\" -Force
Write-Host "  deepin-music.exe + libdmusic.dll"

# 2. Copy updated DTK libraries
Write-Host "Copying DTK libraries..." -ForegroundColor Yellow
$dtkDlls = @("dtk6declarative.dll", "libdtk6gui.dll", "libdtk6core.dll", "libdtk6widget.dll", "libdtk6log.dll")
foreach ($dll in $dtkDlls) {
    $src = Join-Path $DTK_LIB $dll
    if (Test-Path $src) {
        Copy-Item $src "$DEPLOY_DIR\" -Force
        Write-Host "  $dll"
    }
}

# 3. Copy updated translations
Write-Host "Copying translations..." -ForegroundColor Yellow
$transDst = "$DEPLOY_DIR\translations"
if (-not (Test-Path $transDst)) { New-Item -ItemType Directory -Path $transDst -Force | Out-Null }
$transSrc = "$BUILD_DIR\src\music-player"
if (Test-Path $transSrc) {
    Get-ChildItem "$transSrc\deepin-music_zh_CN*.qm" | ForEach-Object {
        Copy-Item $_.FullName "$transDst\" -Force
    }
    Write-Host "  deepin-music_zh_CN.qm"
}

$totalDlls = (Get-ChildItem "$DEPLOY_DIR\*.dll" -ErrorAction SilentlyContinue).Count
Write-Host "`n=== Fast Deploy Complete ($totalDlls DLLs) ===" -ForegroundColor Green
Write-Host "Run: $DEPLOY_DIR\deepin-music.exe"

注意deploy-fast.ps1 依赖之前已执行过 deploy.ps1。如果 deploy 目录不存在或缺少依赖 DLL,需要先执行完整部署。


6. 常见问题

Q: error: target not found: mingw-w64-ucrt-x86_64-base-devel

base-devel 是 MSYS2 自身的工具链包组,属于 msys 仓库,不带 mingw-w64-ucrt-x86_64- 前缀:

pacman -S base-devel
pacman -S mingw-w64-ucrt-x86_64-{cmake,gcc,pkg-config}

Q: cmake 找不到 Qt6

确保在 UCRT64 终端中执行,且设置了环境变量:

export CMAKE_PREFIX_PATH=/ucrt64

Q: VLC DLL 文件名不确定

查看实际安装的文件名:

ls /ucrt64/bin/libvlc*.dll
ls /ucrt64/bin/libavcodec*.dll
ls /ucrt64/bin/SDL2*.dll

根据实际文件名修改 dynamiclibraries.cpp 中的常量。

Q: 设置无法保存

DTK 的 QSettingBackend 在 worker 线程异步写入 config.ini,在 Windows 上不可靠。MusicSettings 已改为直接用 QSettings 读写,绕过 DTK backend。如果遇到设置丢失,删除 AppData/Local/deepin/deepin-music/config.ini 重启应用。


7. UCRT64 中无法安装的包

状态 解决方案
mpris-qt6 MSYS2 无,Linux D-Bus 专有 #ifdef Q_OS_LINUX 条件编译
librsvg-2.0 MSYS2 无 -DDTK_DISABLE_LIBRSVG=ON + dsvgrenderer.cpp 整体条件化
libstartup-notification MSYS2 无 startupnotificationmonitor.cpp 提供 Windows stub
xcb-util / xext / xi / x11 MSYS2 无 CMake 和代码中 if(LINUX) 条件化
gsettings-qt MSYS2 无 仅 Qt5 需要,Qt6 已跳过
libcups2-dev MSYS2 无 CUPS 打印代码 #ifdef Q_OS_LINUX 条件化

8. 效果预览

PixPin_2026-07-17_08-49-50.png

PixPin_2026-07-17_08-50-20.png

PixPin_2026-07-17_08-50-10.png

PixPin_2026-07-17_08-50-39.png

PixPin_2026-07-17_08-50-57.png

9. 各仓库代码修改详解

以下列出 fix/build_on_msys2 分支相对于 master 的所有差异,按仓库分组。每处修改说明了为什么改改了什么

8.1 dtkcore(8 文件,+87/-25)

src/CMakeLists.txt — D-Bus/Qt6 CorePrivate/iconv 链接条件化

为什么改:原代码无条件 find_package(DBus1 REQUIRED)target_link_libraries(... dbus-1),Windows 上没有 libdbus-1。同时 Windows 缺少 CorePrivate 模块和 iconv 库。

怎么改

-find_package(DBus1 REQUIRED)
+find_package(PkgConfig REQUIRED)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus)
 if(LINUX)
-  find_package(PkgConfig REQUIRED)
-  find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus)
-
+  find_package(DBus1 REQUIRED)

+  if("${QT_VERSION_MAJOR}" STREQUAL "6")
+    find_package(Qt6 REQUIRED COMPONENTS CorePrivate)
+  endif()

   target_link_libraries(${LIB_NAME} PUBLIC
     Qt${QT_VERSION_MAJOR}::Core
+    Qt${QT_VERSION_MAJOR}::DBus
     Qt${QT_VERSION_MAJOR}::Xml
+    Dtk${DTK_NAME_SUFFIX}::Log
   )
   target_link_libraries(${LIB_NAME} PRIVATE
     Qt${QT_VERSION_MAJOR}::CorePrivate
+    iconv
   )

-target_link_libraries(${LIB_NAME} PRIVATE dbus-1)
+if(LINUX)
+  target_link_libraries(${LIB_NAME} PRIVATE dbus-1)
+endif()

src/dconfig.cpp — DConfig 日志类别条件编译

为什么改D_DISABLE_DCONFIG 宏在 Windows 上已定义(见 dtkgui.cmake),Q_DECLARE_LOGGING_CATEGORY 需要对应条件化。

怎么改

+#ifdef D_DISABLE_DCONFIG
+Q_LOGGING_CATEGORY(cfLog, "dtk.dsg.config")
+#else
 Q_DECLARE_LOGGING_CATEGORY(cfLog)
+#endif

src/dsgapplication.cpp — D-Bus ApplicationManager 整体条件化

为什么改 等 Linux 头文件在 Windows 上不存在;D-Bus ApplicationManager 仅 Linux 可用。

怎么改

+#ifdef Q_OS_LINUX
 #include 
 #include 
+#include 
+#else
+#include 
+#endif
-#include 
+#include 

+// D-Bus 相关的静态函数 (callDBusIdentifyMethod 等) 包裹在 #ifdef Q_OS_LINUX
+// getSelfAppId() 中 /proc/self/cmdline 读取包裹在 #ifdef Q_OS_LINUX
+// getId() 方法在 Windows 上返回空 QByteArray

src/dsysinfo.cpp — Windows 头文件

+#ifdef Q_OS_WIN
+#include 
+#endif

src/filesystem/dstandardpaths.cpp — POSIX 函数条件化

为什么改getuid()getpwuid() 在 Windows 上不存在。

怎么改

+#include 
+#ifdef Q_OS_LINUX
 #include 
 #include 
+#endif

   // homePath():
+#ifdef Q_OS_LINUX
     return homePath(getuid());
+#else
+    return QDir::homePath();
+#endif

   // path(XDG::RuntimeDir):
+#ifdef Q_OS_LINUX
     return QStringLiteral("/run/user/") + QString::number(getuid());
+#else
+    return QDir::tempPath();
+#endif

   // homePath(uint uid):
+#ifdef Q_OS_LINUX
     struct passwd *pw = getpwuid(uid); ...
+#else
+    Q_UNUSED(uid)
+    return QDir::homePath();
+#endif

src/log/LogManager.cpp — JournalAppender/systemd 条件化

为什么改JournalAppender 和 systemd 检测仅 Linux 可用。

+#if defined(BUILD_WITH_SYSTEMD) && defined(Q_OS_LINUX)
 #include 
+#endif

+#if defined(BUILD_WITH_SYSTEMD) && defined(Q_OS_LINUX)
     JournalAppender* m_journalAppender = nullptr;
+#endif

   // shouldSkipConsoleAppender():
+#ifdef Q_OS_LINUX
     // systemd 检测逻辑
+#else
+    return false;
+#endif

tools/CMakeLists.txt — Linux 专有工具条件化

-add_subdirectory(dci)
-add_subdirectory(deepin-os-release)
+if(LINUX)
+  add_subdirectory(dci)
+  add_subdirectory(deepin-os-release)
+  add_subdirectory(settings)
+  add_subdirectory(ch2py)
+endif()
 add_subdirectory(qdbusxml2cpp)
-add_subdirectory(settings)
-add_subdirectory(ch2py)
 add_subdirectory(dconfig2cpp)

8.2 dtkgui(13 文件,+170/-39)

dtkgui.cmake — librsvg/DConfig/MGW 导出/tools 条件化

为什么改:librsvg 在 MSYS2 无包;DConfig 仅 Linux 可用;MinGW 缺少 DLL 导出宏。

-pkg_check_modules(librsvg REQUIRED IMPORTED_TARGET librsvg-2.0)
+if(NOT DTK_DISABLE_LIBRSVG)
+  pkg_check_modules(librsvg REQUIRED IMPORTED_TARGET librsvg-2.0)
+endif()

+if(NOT LINUX)
+  add_definitions(-DD_DISABLE_DCONFIG)
+endif()
+
+if(WIN32 AND NOT MSVC)
+  add_definitions(-DLIBDTKGUISHARED_LIBRARY)
+  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--export-all-symbols")
+endif()

-add_subdirectory(tools ${OUTPUT_DIR}/tools)
-add_subdirectory(examples ${OUTPUT_DIR}/examples)
+if(LINUX)
+  add_subdirectory(tools ${OUTPUT_DIR}/tools)
+  add_subdirectory(examples ${OUTPUT_DIR}/examples)
+endif()

include/global/dtkgui_global.h — DLL 导出宏

为什么改:MinGW 上类缺少导出宏会导致链接错误,需显式定义 LIBDTKGUISHARED_EXPORT

+#if defined(DTK_STATIC_LIB)
+#  define LIBDTKGUISHARED_EXPORT
+#else
+#if defined(LIBDTKGUISHARED_LIBRARY)
+#  define LIBDTKGUISHARED_EXPORT Q_DECL_EXPORT
+#else
+#  define LIBDTKGUISHARED_EXPORT Q_DECL_IMPORT
+#endif
+#endif

include/kernel/dpalette.h / include/util/ddciiconpalette.h — 导出宏修正

怎么改Q_GUI_EXPORT 改为无导出或 LIBDTKGUISHARED_EXPORT,避免 MinGW 上的 dllimport 冲突。

src/CMakeLists.txt — WaylandClient 移除/MGW 导出

为什么改:WaylandClient 在 Windows 上不存在;MinGW 需要 --export-all-symbols

-find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets WaylandClient)
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)

-    find_package(Qt6 REQUIRED COMPONENTS CorePrivate GuiPrivate WaylandClientPrivate)
+    find_package(Qt6 REQUIRED COMPONENTS CorePrivate GuiPrivate)

+target_compile_definitions(${LIB_NAME} PRIVATE LIBDTKGUI_LIBRARY QT_GUI_BUILD)
+if(WIN32 AND NOT MSVC)
+    target_link_options(${LIB_NAME} PRIVATE -Wl,--export-all-symbols)
+endif()

     Qt${QT_VERSION_MAJOR}::DBus
-    Qt${QT_VERSION_MAJOR}::WaylandClientPrivate

src/kernel/dguiapplicationhelper.cpp — DConfig 条件化

为什么改:DConfig 在 Windows 上不可用,所有 _d_dconfig-> 调用需要空指针保护。

+#ifndef D_DISABLE_DCONFIG
 Q_GLOBAL_STATIC_WITH_ARGS(OrgDeepinDTKPreference, _d_dconfig, (...));
+#define DTK_DCONFIG_PTR _d_dconfig
+#else
+static OrgDeepinDTKPreference* _d_dconfig_instance = nullptr;
+#define DTK_DCONFIG_PTR _d_dconfig_instance
+#endif

   // LoadManualServiceWorker::run():
+#ifdef Q_OS_UNIX
     QDBusInterface("com.deepin.Manual.Search", ...);
+#endif

   // initPaletteType():
+#ifndef D_DISABLE_DCONFIG
     DTK_CORE_NAMESPACE::DConfig dconfig("org.deepin.dtk.preference");
     int ct = dconfig.value("themeType", ...).toInt();
+#else
+    int ct = DGuiApplicationHelper::UnknownType;
+#endif

   // 所有 _d_dconfig-> 调用改为 DTK_DCONFIG_PTR ? DTK_DCONFIG_PTR-> : 默认值

src/kernel/dwindowmanagerhelper.cpp — XCB 条件化

+#ifdef Q_OS_LINUX
 #include 
+#endif

 void DWindowManagerHelper::setWmWindowTypes(...) {
+#ifdef Q_OS_LINUX
     // XCB 窗口类型设置
+#else
+    Q_UNUSED(window) Q_UNUSED(types)
+#endif
 }

src/plugins/dplatformwindowinterface.cpp — Windows 窗口接口 stub

为什么改:原代码所有方法返回空值/false,Windows 上需要真实的窗口行为。

+#ifdef Q_OS_WIN
+    return true;       // isEnabled / isEnabledNoTitlebar / setEnabledNoTitlebar
+#else
     return false;
+#endif

+// 新增 setWindowBlurArea 两个重载(Windows 返回 true,Linux 返回 false)

src/plugins/plugins.cmake — XCB/Treeland 条件化

-if(NOT DTK_DISABLE_XCB)
+if(LINUX AND NOT DTK_DISABLE_XCB)

+if(LINUX)
+  try_compile(...)
+else()
+  set(DTK_DISABLE_TREELAND ON)
+endif()

src/private/dplatforminterface_p.h / dplatformwindowinterface_p.h

怎么改:移除 LIBDTKGUISHARED_EXPORT(内部类无需导出);添加 setWindowBlurArea 虚方法和 m_enableBlurWindow 成员。

src/util/dicontheme.cpp — QRC 资源路径支持

为什么改:Windows 上 DCI 图标通过 QRC 资源加载(":/..."),原代码只处理文件系统路径。

+    bool isQrcPath = path.startsWith(QLatin1String(":/"));
+    if (isQrcPath) {
+        themePath = themeName.isEmpty() ? path : path + '/' + themeName;
+    } else {
+        // 原有文件系统路径逻辑
+    }

src/util/dsvgrenderer.cpp — 整个文件条件化

为什么改:librsvg 在 MSYS2 无包,SVG 渲染在 Windows 上由 Qt 自己处理。

+#ifdef Q_OS_LINUX
 #ifndef DTK_DISABLE_LIBRSVG
 // 原有 librsvg 实现
 #endif
+#endif // Q_OS_LINUX

8.3 dtkwidget(8 文件,+178/-43)

dtkwidget.cmake — MGW 导出/Svg 依赖

+if(WIN32 AND NOT MSVC)
+  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--export-all-symbols")
+endif()

     DBus
-    DBus)
+    DBus
+    Svg)

src/CMakeLists.txt — X11/XCB 条件化

为什么改:xcb-util、libstartup-notification、xext、xi、x11 在 MSYS2 无包。

-pkg_check_modules(XcbUtil REQUIRED IMPORTED_TARGET xcb-util)
-pkg_check_modules(StartupNotification REQUIRED IMPORTED_TARGET libstartup-notification-1.0)
-pkg_check_modules(Xext REQUIRED IMPORTED_TARGET xext)
-pkg_check_modules(Xi REQUIRED IMPORTED_TARGET xi)
-pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11)
+if(LINUX)
+  pkg_check_modules(XcbUtil REQUIRED IMPORTED_TARGET xcb-util)
+  pkg_check_modules(StartupNotification REQUIRED IMPORTED_TARGET libstartup-notification-1.0)
+  pkg_check_modules(Xext REQUIRED IMPORTED_TARGET xext)
+  pkg_check_modules(Xi REQUIRED IMPORTED_TARGET xi)
+  pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11)
+endif()

+    Qt${QT_VERSION_MAJOR}::Svg

-    PkgConfig::StartupNotification
-    PkgConfig::Xext / Xi / X11 / XcbUtil
+if(LINUX)
+  target_link_libraries(... PRIVATE PkgConfig::StartupNotification ...)
+endif()

src/widgets/dimageviewer.cpp — SVG 渲染 + abs() 类型修复

为什么改DSvgRenderer::toImage() 在 Windows 上不可用(librsvg 缺失);abs() 在 MinGW 上返回 int 导致 qFuzzyCompare 类型不匹配。

+#ifdef Q_OS_LINUX
                 image = render.toImage(render.defaultSize());
+#else
+                QSize sz = render.defaultSize();
+                if (!sz.isEmpty()) {
+                    image = QImage(sz, QImage::Format_ARGB32);
+                    image.fill(Qt::transparent);
+                    QPainter painter(&image);
+                    render.render(&painter);
+                }
+#endif

-    if (qFuzzyCompare(abs(angle), 180)) {
+    if (qFuzzyCompare(qreal(abs(angle)), qreal(180))) {

src/widgets/dprintpreviewdialog.cpp / dprintpreviewwidget.cpp — CUPS 条件化

为什么改:libcups2-dev 在 MSYS2 无包,CUPS 打印仅 Linux 可用。

+#ifdef Q_OS_LINUX
 #include 
+#endif

     // QCUPSSupport::setCupsOption() / printByCups() / foundColorModelByCups()
     // 全部包裹在 #ifdef Q_OS_LINUX

src/widgets/private/keyboardmonitor/dkeyboardmonitor.h/.cpp — X11 stub

为什么改:键盘监控依赖 X11/Xcb,在 Windows 上需要 stub 实现。

+#ifdef Q_OS_LINUX
 #include 
+#endif

+// .cpp: 非 Linux 提供完整 stub
+#else
+DKeyboardMonitor::DKeyboardMonitor() : QThread() {}
+bool DKeyboardMonitor::isCapslockOn() { return false; }
+bool DKeyboardMonitor::isNumlockOn() { return false; }
+bool DKeyboardMonitor::setNumlockStatus(const bool &) { return false; }
+void DKeyboardMonitor::run() {}
+#endif

src/widgets/private/startupnotifications/startupnotificationmonitor.cpp — X11 stub

为什么改:启动通知监控依赖 XCB 和 libsn,在 Windows 上需要 stub。

+#ifdef Q_OS_LINUX
 // X11/XCB 启动通知监控完整实现
+#else
+// Stub: 所有方法为空实现
+#endif

8.4 dtkdeclarative(13 文件,+174/-60)

CMakeLists.txt — DBus/MGW 导出

-find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core QuickControls2)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core QuickControls2 DBus)

+if(WIN32 AND NOT MSVC)
+  set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--export-all-symbols")
+endif()

examples/exhibition/CMakeLists.txt — 链接器标志

为什么改-z relro 等 GNU ld 选项在 MinGW 上不适用。

-set(CMAKE_EXE_LINKER_FLAGS "-z relro -z now -z noexecstack -pie -Wl,--as-needed")
+if(NOT WIN32)
+  set(CMAKE_EXE_LINKER_FLAGS "-z relro -z now -z noexecstack -pie -Wl,--as-needed")
+endif()

qt6/src/qml/AboutDialog.qml — Windows 图标 fallback

为什么改:DCI 图标主题在 Windows 上可能不包含应用图标,需要 SVG 回退。

+                visible: Qt.platform.os !== "windows"
+            }
+            Image {
+                id: logoFallback
+                visible: Qt.platform.os === "windows"
+                source: Qt.resolvedUrl("/dsg/img/deepin-music.svg")
+                ...

qt6/src/qml/TitleBar.qml — 窗口拖拽

为什么改:Windows 无边框窗口需要 DragHandler 实现标题栏拖拽。

+    DragHandler {
+        id: dragHandler
+        target: null
+        acceptedButtons: Qt.LeftButton
+        grabPermissions: PointerHandler.CanTakeOverFromAnything
+        enabled: !__isFullScreen
+        onActiveChanged: {
+            if (active) {
+                if (__dwindow && __dwindow.startSystemMove) {
+                    __dwindow.startSystemMove()
+                } else if (control.window && control.window.startSystemMove) {
+                    control.window.startSystemMove()
+                }
+            }
+        }
+    }

src/dquickwindow.cpp — Windows 无边框窗口/系统移动

为什么改:Windows 上 FramelessWindowHint 需要在原生窗口创建后设置;需要 startSystemMove() 方法支持标题栏拖拽。

-    if (!DPlatformHandle::setEnabledNoTitlebarForWindow(window, true)) {
-        qWarning() << "Failed to enable NoTitlebar for the window:"  << window;
-        return false;
-    }
+    DPlatformHandle::setEnabledNoTitlebarForWindow(window, true);

+#ifdef Q_OS_WIN
+    QPointer w = window;
+    QTimer::singleShot(0, q, [w]() {
+        if (w && w->handle()) {
+            w->setFlags(w->flags() | Qt::FramelessWindowHint);
+        }
+    });
+#endif

+void DQuickWindowAttached::startSystemMove() {
+    if (auto h = window()->handle()) { h->startSystemMove(); }
+}

src/private/dqmlglobalobject.cpp — 网站信息条件化

为什么改DSysInfo::distributionOrgWebsite() 仅 Linux 可用。

+#include 
+#include 
+
+#ifdef Q_OS_LINUX
 #include 
 #include 
-#include 
-#include 
+#endif

   // ensureWebsiteInfo():
+#ifdef Q_OS_LINUX
     const auto &deepinWebsiteInfo = DSysInfo::distributionOrgWebsite(...);
+#else
+    deepinWebsiteName = QStringLiteral("Deepin");
+    deepinWebsiteLink = QStringLiteral("https://www.deepin.org");
+#endif

src/private/dquickbehindwindowblur.cpp — OpenGL 空指针保护

为什么改:Windows 上 OpenGL context 可能为空导致崩溃。

-        QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
-        f->glEnable(GL_BLEND);
-        f->glBlendFunc(GL_ONE, GL_ZERO);
+        QOpenGLContext *ctx = QOpenGLContext::currentContext();
+        if (ctx) {
+            QOpenGLFunctions *f = ctx->functions();
+            if (f) {
+                f->glEnable(GL_BLEND);
+                f->glBlendFunc(GL_ONE, GL_ZERO);
+            }
+        }

src/private/dquickdciiconimage.cpp — DCI 图标 fallback

为什么改:Windows 上 DCI 图标可能不在主题搜索路径中,需要直接文件查找。

+#ifdef Q_OS_WIN
+    if (iconPath.isEmpty()) {
+        const QStringList searchPaths = DIconTheme::dciThemeSearchPaths();
+        for (const QString &path : searchPaths) {
+            QString testPath = path + "/" + iconName + ".dci";
+            if (QFileInfo::exists(testPath)) {
+                iconPath = testPath;
+                break;
+            }
+        }
+    }
+#endif

src/qml/TitleBar.qml — 鼠标拖拽

+            } else if (mouse.button === Qt.LeftButton && !__isFullScreen) {
+                if (__dwindow && __dwindow.startSystemMove) {
+                    __dwindow.startSystemMove()
+                } else if (control.window && control.window.startSystemMove) {
+                    control.window.startSystemMove()
+                }
+                mouse.accepted = true
+                return

8.5 deepin-music(23 文件,+404/-58)

src/libdmusic/CMakeLists.txt — VLC 路径/MPRIS/导出

为什么改:VLC 头文件路径硬编码为 /usr/include/vlc;mpris-qt6 仅 Linux 可用;MinGW 需要导出符号。

-add_definitions(-DLIBDMUSIC_LIBRARY)
+add_definitions(-DLIBDMUSIC_LIBRARY -DVLCQT_CORE_LIBRARY)
+set(CMAKE_AUTORCC OFF)

-include_directories(/usr/include/vlc)
-include_directories(/usr/include/vlc/plugins)
+find_path(VLC_INCLUDE_DIR vlc/vlc.h)
+if(VLC_INCLUDE_DIR)
+  include_directories(${VLC_INCLUDE_DIR})
+  if(EXISTS "${VLC_INCLUDE_DIR}/vlc/plugins")
+    include_directories(${VLC_INCLUDE_DIR}/vlc/plugins)
+  elseif(EXISTS "${VLC_INCLUDE_DIR}/plugins")
+    include_directories(${VLC_INCLUDE_DIR}/plugins)
+  endif()
+  if(EXISTS "${VLC_INCLUDE_DIR}/vlc")
+    include_directories(${VLC_INCLUDE_DIR}/vlc)
+  endif()
+endif()
+add_definitions(-include poll.h)

+if(WIN32 AND NOT MSVC)
+    target_link_options(${CMD_NAME} PRIVATE -Wl,--export-all-symbols)
+endif()

-pkg_check_modules(MPRIS REQUIRED IMPORTED_TARGET mpris-qt6)
+if(LINUX)
+  pkg_check_modules(MPRIS REQUIRED IMPORTED_TARGET mpris-qt6)
+endif()

-target_link_libraries(... PkgConfig::MPRIS ...)
+target_link_libraries(...)
+if(LINUX)
+  target_link_libraries(... PkgConfig::MPRIS)
+endif()

src/libdmusic/core/audioanalysis.cpp — TagLib 路径安全

为什么改:Windows 上 TagLib 需要宽字符路径 toStdWString();歌词路径检查需统一使用 / 分隔符。

src/libdmusic/core/dynamiclibraries.cpp/.h — 动态库名/avutil

为什么改:Windows 上库名不带 .so 后缀(QLibrary 自动加 .dll);ffmpeg 在 Windows 上需要单独加载 avutil。

+#ifdef Q_OS_WIN
+static const QString libvlccoreStr = "libvlccore";
+static const QString libvlcStr = "libvlc";
+static const QString libavcodecStr = "avcodec-62";
+static const QString libavformateStr = "avformat-62";
+static const QString libavutilStr = "avutil-60";
+#else
 static const QString libvlccoreStr = "libvlccore.so";
 static const QString libvlcStr = "libvlc.so";
 static const QString libavcodecStr = "libavcodec.so";
 static const QString libavformateStr = "libavformat.so";
+static const QString libavutilStr = "libavutil.so";
+#endif

+// .h: 新增 QLibrary avutilLib 成员
+// loadLibraries(): Windows 修正 libvlc 路径,新增加载 avutil
+#ifdef Q_OS_WIN
+    if (strlibvlc.contains("vlccore")) {
+        strlibvlc = strlibvlc.left(strlibvlc.lastIndexOf("/")) + "/libvlc.dll";
+    }
+#endif

src/libdmusic/global.cpp — 库路径查找

为什么改:Windows 上库在应用目录或 ucrt64/bin 中,需先搜索应用目录;后缀为 .dll 而非 .so

+#include 

 QString DmGlobal::libPath(const QString &strlib) {
-    QString path = QLibraryInfo::location(QLibraryInfo::LibrariesPath);
+    QStringList searchPaths;
+#ifdef Q_OS_WIN
+    searchPaths << QCoreApplication::applicationDirPath();
+#endif
+    searchPaths << QLibraryInfo::location(QLibraryInfo::LibrariesPath);

-    // 仅搜索 LibrariesPath
+    // 遍历所有搜索路径
+    for (const QString &path : searchPaths) {
+        dir.setPath(path);
+        QStringList list = dir.entryList(QStringList() << (strlib + "*"), ...);
+        if (list.contains(strlib)) { return path + "/" + strlib; }
+        list.sort();
+        for (int i = list.size() - 1; i >= 0; i--) {
+#ifdef Q_OS_WIN
+            if (list[i].contains(".dll")) { return path + "/" + list[i]; }
+#else
+            if (list[i].contains(".so")) { return path + "/" + list[i]; }
+#endif
+        }
+    }
 }

 bool DmGlobal::libExist(const QString &strlib) {
+#ifdef Q_OS_WIN
+    libName = strlib;
+#else
     if (strlib.contains(".so"))
         libName = strlib.mid(0, strlib.indexOf(".so"));
     else
         libName = strlib;
+#endif
 }

 void DmGlobal::initPlaybackEngineType() {
-    if (libExist("libvlc.so") && libExist("libavcodec.so")) {
+#ifdef Q_OS_WIN
+    if (libExist("libvlc") && libExist("avcodec-62")) {
+#else
+    if (libExist("libvlc.so") && libExist("libavcodec.so")) {
+#endif

src/libdmusic/player/playerengine.cpp — MPRIS 条件化

为什么改:MPRIS 是 Linux D-Bus 专有协议,Windows 无等价物。

+#ifdef Q_OS_LINUX
 #include 
+#endif

+#ifdef Q_OS_LINUX
     MprisPlayer *m_mprisPlayer = nullptr;
+#else
+    void *m_mprisPlayer = nullptr;
+#endif

+// 析构函数、setMprisPlayer()、resetDBusMpris() 全部包裹在 #ifdef Q_OS_LINUX

src/libdmusic/player/qtplayer.cpp — 音量修复

为什么改:Qt6 Multimedia 的 setVolume() 接受 0.0-1.0,原代码传入 0-100 整数导致音量异常。

-    m_audioOutput->setVolume(static_cast(10 * fadeInOutFactor));
+    m_audioOutput->setVolume(qBound(0.0, static_cast(m_volume) / 100.0 * fadeInOutFactor, 1.0));

-    m_audioOutput->setVolume(volume);
+    m_audioOutput->setVolume(volume / 100.0);

src/libdmusic/player/vlc/Instance.cpp — Windows VLC 插件路径/vsnprintf

为什么改:Windows 上 VLC 需要 VLC_PLUGIN_PATH 环境变量;vasprintf 在 MinGW 上不可用。

+#ifdef Q_OS_WIN
+    char buf[4096];
+    if (vsnprintf(buf, sizeof(buf), fmt, args) < 0) { return; }
+    QString message(buf);
+#else
     if (vasprintf(&result, fmt, args) < 0) { return; }
     QString message(result);
     free(result);
+#endif

+#ifdef Q_OS_WIN
+    QString pluginPath = QCoreApplication::applicationDirPath() + "/plugins/vlc";
+    if (QDir(pluginPath).exists()) {
+        qputenv("VLC_PLUGIN_PATH", pluginPath.toUtf8());
+    }
+#endif

+// vlc_set_user_agent / vlc_set_app_id 移到 null 检查之后

src/libdmusic/player/vlc/MediaPlayer.cpp — 空指针保护/winsock2

+#ifdef Q_OS_WIN
+#include 
+#endif
+// VLC 头文件在 Windows 上依赖 winsock2

+    if (!instance->core()) {
+        _vlcMediaPlayer = nullptr;
+        _vlcEvents = nullptr;
+        _vlcEqualizer = nullptr;
+        return;
+    }

src/libdmusic/player/vlc/sdlplayer.cpp — SDL 条件化/std::log2

为什么改:SDL 音频回调在 Windows 上不需要(VLC 直接输出);FFMAX 宏和 av_log2 函数在 Windows 上不可用。

+#include 

 SdlPlayer::SdlPlayer(VlcInstance *instance) {
+#ifdef Q_OS_LINUX
     // SDL 初始化、音频回调注册
+    if (vlc_audio_set_callbacks && vlc_audio_set_format_callbacks) {
+        vlc_audio_set_callbacks(...);
+        vlc_audio_set_format_callbacks(...);
+    }
+#else
+    // Windows: 使用 VLC 默认音频输出
+#endif
 }

 // 析构、open、play、pause、resume、stop、getVolume、setVolume 等方法
 // SDL 相关代码全部包裹在 #ifdef Q_OS_LINUX

-    desiredAS.samples = FFMAX(AUDIO_MIN_BUFFER_SIZE, 2 << Log2(...));
+    desiredAS.samples = qMax(AUDIO_MIN_BUFFER_SIZE, 2 << static_cast(std::log2(...)));

src/libdmusic/player/vlcplayer.cpp — 空指针保护/malloc_trim

+    if (!m_qvinstance->core()) {
+        delete m_qvinstance;
+        m_qvinstance = nullptr;
+        return;
+    }

+#ifdef Q_OS_LINUX
     malloc_trim(0);
+#endif

-    m_qvplayer->setVolume(volume);
+    if (m_qvplayer) { m_qvplayer->setVolume(volume); }

-    m_qvplayer->setMute(value);
+    if (m_qvplayer) { m_qvplayer->setMute(value); }

src/libdmusic/presenter.cpp — 文件管理器

为什么改:Linux 用 D-Bus FileManager1 打开文件管理器,Windows 用 explorer /select,

+#include 
+#include 
+#include 

src/music-player/CMakeLists.txt — Windows 资源/图标

+find_package(Qt6 COMPONENTS Svg REQUIRED)

+if(WIN32)
+  set(WIN_RESOURCES appicon.rc)
+endif()

-add_executable(${BIN_NAME} ${RCC_SOURCES} ${ALLSOURCE} ${QM_FILES})
+if(WIN32)
+  add_executable(${BIN_NAME} WIN32 ${RCC_SOURCES} ${ALLSOURCE} ${QM_FILES} ${WIN_RESOURCES})
+else()
+  add_executable(${BIN_NAME} ${RCC_SOURCES} ${ALLSOURCE} ${QM_FILES})
+endif()

src/music-player/main.cpp — 环境变量/图标/信号

为什么改:Windows 上需要自动设置 Qt 插件路径;图标从主题加载改为 QRC;SIGTERM 信号处理仅 Linux 可用。

+#include 
+#ifdef Q_OS_WIN
+#include 
+#endif
+#ifdef Q_OS_LINUX
 #include 
 #include 
+#endif

+#ifdef Q_OS_WIN
+    wchar_t exePath[MAX_PATH];
+    GetModuleFileNameW(NULL, exePath, MAX_PATH);
+    QString appDir = QFileInfo(QString::fromWCharArray(exePath)).absolutePath();
+    qputenv("QT_PLUGIN_PATH", (appDir + "/plugins").toUtf8());
+    qputenv("QT_QPA_PLATFORM_PLUGIN_PATH", (appDir + "/plugins/platforms").toUtf8());
+    qputenv("QML2_IMPORT_PATH", (appDir + "/qml").toUtf8());
+    qputenv("QT_QML_IMPORT_PATH", (appDir + "/qml").toUtf8());
+    qputenv("PATH", (appDir + ";" + currentPath).toUtf8());
+#endif

+#ifdef Q_OS_LINUX
     format.setRenderableType(QSurfaceFormat::OpenGLES);
     format.setVersion(3, 2);
+#endif

+#ifdef Q_OS_LINUX
     if (!app->setSingleInstance("deepinmusic")) { ... }
+#endif

+#ifdef Q_OS_WIN
+    qApp->setProductIcon(QIcon(":/dsg/img/deepin-music.svg"));
+#else
     qApp->setProductIcon(QIcon::fromTheme("deepin-music"));
+#endif

+#ifdef Q_OS_WIN
+    if (auto *window = qobject_cast(engine.rootObjects()[0])) {
+        window->setIcon(QIcon(":/dsg/img/deepin-music.svg"));
+    }
+#endif

+#ifdef Q_OS_LINUX
     static struct sigaction _sigact;
     sigaction(SIGTERM, &_sigact, NULL);
+#endif
 }

src/music-player/mainwindow/MainWindow.qml — 无边框/居中

为什么改:Windows 上需要无边框窗口标志和窗口居中。

-    flags: Qt.Window | ... | Qt.WindowTitleHint
+    flags: Qt.Window | ... | (Qt.platform.os === "windows" ? Qt.FramelessWindowHint : Qt.WindowTitleHint)

+        if (Screen.width > 0 && Screen.height > 0) {
+            x = (Screen.width - width) / 2
+            y = (Screen.height - height) / 2
+        }

src/music-player/mainwindow/WindowTitlebar.qml — 图标/菜单居中

为什么改:Windows 上 DCI 图标可能不可用,需要 SVG 回退;菜单需要屏幕居中。

+import org.deepin.dtk 1.0 as D

+    Loader {
+        id: iconLoader
+        sourceComponent: Qt.platform.os === "windows" ? fallbackIcon : dciIcon
+        Component {
+            id: dciIcon
+            D.DciIcon { ... }
+        }
+        Component {
+            id: fallbackIcon
+            Image { source: "qrc:/dsg/img/deepin-music.svg" ... }
+        }
+    }

+    // 菜单居中
+    onVisibleChanged: {
+        if (visible && Qt.platform.os === "windows") {
+            x = (Screen.width - width) / 2
+            y = (Screen.height - height) / 2
+        }
+    }

QML 对话框居中(CloseConfirmDialog / EqualizerDialog / SettingsDialog)

为什么改:Windows 上对话框默认不居中。

+    onVisibleChanged: {
+        if (visible && Qt.platform.os === "windows") {
+            x = (Screen.width - width) / 2
+            y = (Screen.height - height) / 2
+        }
+    }

src/music-player/appicon.rc — 应用图标资源

为什么改:Windows 可执行文件需要 .rc 资源文件嵌入图标。

IDI_ICON1 ICON "dsg/img/deepin-music.ico"
Reply Favorite View the author
All Replies
avatar
神末shenmo
deepin
Spark-App
Q&A Team
an hour ago
#1

卧槽,你花了多少token做的这玩意

这跟长征一样

Reply View the author
avatar
deepin-superuser
deepin
19 minutes ago
#2
神末shenmo

卧槽,你花了多少token做的这玩意

这跟长征一样

具体花了多少我不知道 差不多整整4天时间吧

按之前一天2-3亿的消耗来看 估计10-20亿

Reply View the author