跳转至

桌面端跨平台

SOUI 的桌面端覆盖 Windows / Linux / macOS。三者的共同点是:应用进程拥有自己的入口和事件循环,因此不需要任何外部桥接库——Windows 直接用系统 Win32;Linux 与 macOS 由 swinx 在模块内部实现全部平台 API。

平台分层与 swinx 的定位见 跨平台开发概述

三平台对比

维度 Windows Linux macOS
是否编译 swinx 否(用系统 Win32)
swinx 平台层 swinx/src/platform/linux swinx/src/platform/cocoa
构建片段 CMakeLists.txt Windows 分支 swinx/linux.cmake swinx/macos.cmake
窗口系统 原生 Win32 窗口 X11(通过 xcb) Cocoa / AppKit(SNsWindow
swinx 2D 后端 系统 GDI(无翻译层) Cairo(src/gdi/cairo Core Graphics(src/gdi/apple
事件泵 系统消息队列 poll() + xcb_poll_for_event() SwinXApplicationNSApplication 子类)
WCHAR_SIZE 2 4 4
托盘图标 原生 Shell_NotifyIcon _NET_SYSTEM_TRAYSTrayIconMgr STrayIconMgr(AppKit)
输入法 原生 IMM32 xcb-imdkit(XIM) SImContext(AppKit 文本输入)

统一的入口模型

桌面端三平台共用同一个 Win32 风格入口 _tWinMain;非 Windows 平台只需一个薄 main() 转发。以 games/cnchess/client/main.cc 为例:

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpstrCmdLine, int)
{
    SApplication app(hInstance);
    // 注册皮肤 / 窗口类
    app.RegisterSkinClass<SSkinAni>();
    app.RegisterWindowClass<CChessBoard>();

    SAppCfg cfg;
    cfg.SetRender(Render_Skia)          // 选择 SOUI 渲染工厂
       .SetImgDecoder(ImgDecoder_Stb)
       .SetLog(TRUE);

#ifdef _WIN32
    cfg.SetSysResPeFile(appDir + _T("/soui-sys-resource.dll"));   // 资源来自 PE 文件
    cfg.SetAppResPeHandle(hInstance);
#else
    cfg.SetSysResZip(strSysRes, "");                              // 或 SetSysResFile(目录)
    cfg.SetAppResZip(strAppRes, "");                              // 或 SetAppResFile(目录)
#endif

    cfg.DoConfig(&app);

#ifndef _WIN32
    AddFontResource(strFont.c_str());   // 非 Windows 需显式加载字体
#endif

    CMainDlg dlgMain(theme);
    dlgMain.Create(GetActiveWindow());
    dlgMain.SendMessage(WM_INITDIALOG);
    dlgMain.ShowWindow(SW_SHOWNORMAL);
    return app.Run(dlgMain.m_hWnd);
}

// Linux / macOS 的薄入口(Windows 由 CRT 直接调用 _tWinMain)
#if !defined(_WIN32) || defined(__MINGW32__)
int main(int argc, char **argv)
{
    HINSTANCE hInst = GetModuleHandle(NULL);
    return _tWinMain(hInst, 0, NULL, SW_SHOWNORMAL);
}
#endif

注意 Linux 与 macOS 都是直接转发:swinx 的窗口系统连接(xcb / NSApplication)在 SConnection 首次使用时惰性建立,不需要应用显式初始化。

资源加载的平台差异

SAppCfg 为不同平台提供了不同的资源来源接口,这是桌面端唯一必须区分平台的地方:

平台 系统资源(soui-sys-resource) 应用资源(uires)
Windows SetSysResPeFile("soui-sys-resource.dll") SetAppResPeHandle(hInstance)
Linux SetSysResZip(...)SetSysResFile(目录) SetAppResZip(...)SetAppResFile(目录)
macOS 同 Linux,路径指向 .app Bundle 内的 Resources 同 Linux

字体方面:Windows 通常直接使用系统字体;Linux 与 macOS 建议在 DoConfig 之后用 AddFontResource() 显式加载随包分发的字体文件(如 simsun.ttc),避免不同发行版字体缺失导致的排版差异。

跨平台编码建议

  1. 用预定义宏隔离平台差异,不要在业务代码里直接调用平台原生 API:

    #ifdef _WIN32
        // Windows 专属
    #elif defined(__APPLE__)
        // macOS / iOS 共用
        #ifdef __IOS__
            // 仅 iOS
        #endif
    #elif defined(__ANDROID__)
        // Android
    #elif defined(__OHOS__)
        // 鸿蒙
    #else
        // Linux
    #endif
    

    其中 __IOS____OHOS__ 是 SOUI 构建体系添加的宏(分别在根 CMakeLists.txtswinx/mobile.cmake 中定义),不是编译器内置宏。

  2. 差异收敛到入口处:让 SAppCfg(资源来源、渲染工厂、日志)承担所有平台判断,业务窗体、控件、布局、消息处理保持平台无关。

  3. 不要假设 wchar_t 是 2 字节:跨平台代码统一用 SStringT / _T(),需要固定宽度时用 SStringA / SStringW 并注意 WCHAR_SIZE
  4. 路径分隔符与大小写:Linux 文件系统区分大小写,资源文件名与 XML 中的引用必须完全一致。

各平台细节

  • Windows:原生 Win32 路径、Windows 独有构建选项与资源方式
  • Linux:xcb / Cairo 依赖、内置第三方库、音频与对话框回退策略
  • macOS:Cocoa 平台层、NSApplication 接管、Framework 依赖与部署版本