macOS¶
macOS 的平台 API 全部在 swinx 内部实现(swinx/src/platform/cocoa),不依赖外部桥接库,也不使用 g_platformAPI。swinx 会自己创建并接管 NSApplication,因此业务代码只需要写一个 _tWinMain。
构建选择¶
# swinx/CMakeLists.txt
elseif(CMAKE_SYSTEM_NAME MATCHES Darwin)
include(macos.cmake)
swinx/macos.cmake 编译的源码集合:
file(GLOB SRCS
src/*.cpp
src/cmnctl32/*.cpp
src/cmnctl32/*.c
src/platform/cocoa/*.mm # Cocoa 平台层(Objective-C++)
src/gdi/apple/*.cpp # Core Graphics 2D 后端
)
整个 swinx 以 ARC 编译并使用 libc++:
target_compile_options(swinx PRIVATE "-fobjc-arc")
target_compile_options(swinx PRIVATE -stdlib=libc++)
target_link_options(swinx PRIVATE -stdlib=libc++)
Framework 依赖¶
find_package(Iconv REQUIRED)
find_library(COCOA_LIBRARY Cocoa)
find_library(QUARTZ_LIBRARY QuartzCore)
find_library(IOKit_LIBRARY IOKit)
find_library(Carbon_LIBRARY Carbon)
find_library(Audio_LIBRARY AudioToolbox)
find_library(CoreFoundation_LIBRARY CoreFoundation)
find_library(ImageIO_LIBRARY ImageIO)
| Framework | 用途 |
|---|---|
| Cocoa(AppKit) | 窗口、视图、菜单、托盘、拖放 |
| QuartzCore / CoreGraphics | 2D 绘制(Core Graphics 由 Cocoa 伞式框架带入) |
| CoreFoundation | RunLoop、字符串与基础类型 |
| Carbon | 少量遗留 API(键盘布局等) |
| IOKit | 设备与显示信息 |
| AudioToolbox | PlaySound 音效播放 |
| ImageIO | 图片解码 |
| Iconv | 字符编码转换(系统提供) |
字体与图形基础库仍来自 swinx/thirdparty(fontconfig、freetype、pixman-1)。
NSApplication 由 swinx 接管¶
swinx/src/platform/cocoa/os_state.mm 定义了 NSApplication 子类 SwinXApplication 与配套的 AppDelegate,并在 OsState 构造时完成初始化:
OsState::OsState() {
@autoreleasepool {
SwinXApplication *nsApp = [SwinXApplication sharedApplication];
[nsApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[nsApp activateIgnoringOtherApps:YES];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[nsApp setDelegate:appDelegate];
[nsApp finishLaunching];
...
}
}
要点:
- 注意用的是
finishLaunching而不是NSApplicationMain/run。这样 AppKit 完成启动初始化后不会进入自己的事件循环,主导权仍在 SOUI 的GetMessage循环手中。 - 事件由
SwinXApplication拦截后转换成 Win32 消息投递给 SOUI。 AppDelegate处理applicationShouldTerminateAfterLastWindowClosed:、applicationDockMenu:等生命周期与 Dock 菜单回调。
因此 macOS 上的入口与 Linux 完全一样,是直接转发的薄 main():
#if !defined(_WIN32) || defined(__MINGW32__)
int main(int argc, char **argv)
{
HINSTANCE hInst = GetModuleHandle(NULL);
return _tWinMain(hInst, 0, NULL, SW_SHOWNORMAL);
}
#endif
这一点与 iOS 不同:iOS 的
UIApplicationMain是阻塞且永不返回的,必须通过swinx_ios_entry()包装(见 iOS 适配)。
窗口:SNsWindow¶
SNsWindow.mm 定义的 SNsWindow 是一个 NSView 子类,同时实现两个协议:
@interface SNsWindow : NSView <NSDraggingDestination, NSTextInputClient>
NSDraggingDestination:接收系统拖放(draggingEntered:、performDragOperation:),通过SNsDataObjectProxy与 swinx 的IDataObject互转,把NSPasteboard数据映射到 Win32 剪贴板格式。NSTextInputClient:实现输入法组合输入(insertText:replacementRange:、markedText等),转换为WM_IME_*/WM_CHAR消息。
2D 绘制:Core Graphics¶
GDI 调用由 swinx/src/gdi/apple/gdi.cpp 翻译到 Core Graphics(CGBitmapContext / CGContext),与 iOS 共用同一份实现。
同样需要区分层次:Core Graphics 是 swinx 的 2D 后端,SOUI 上层通常仍用 Render_Skia 渲染工厂绘制 UI,最终经 Core Graphics 上屏。这里不涉及 Metal。
桌面集成特性¶
| 特性 | 实现文件 | 机制 |
|---|---|---|
| 托盘图标 | STrayIconMgr.mm |
[[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength] |
| 拖放 | SNsWindow.mm + SNsDataObjectProxy.mm |
NSPasteboard ↔ IDataObject 双向代理 |
| 剪贴板 | SClipboard.mm |
NSPasteboard |
| 输入法 | imm.mm + SImContext.h |
NSTextInputClient |
| 通用对话框 | dlghelper.mm |
NSOpenPanel / NSSavePanel 等 |
| 系统主题 | os_state.mm |
监听 NSApplicationDidBecomeActiveNotification 等通知 |
部署版本¶
两处都会影响最低系统版本,注意它们的生效顺序:
# swinx/macos.cmake:swinx 自身的下限
if(NOT CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "10.13")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
endif()
# 根 CMakeLists.txt:SOUI 主体的设定
if(CMAKE_SYSTEM_NAME MATCHES Darwin)
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0")
...
set(CMAKE_CXX_STANDARD 11)
endif()
macos.cmake 中给出的下限依据是:渐变 API(CGShadingCreateAxial)需要 10.2+、ARC 需要 10.6+、libc++ 需要 10.7+,取最高并上浮到 10.13。如需支持更低版本,可显式传入 -DCMAKE_OSX_DEPLOYMENT_TARGET=...,但需自行验证。
资源加载¶
macOS 与 Linux 用同一套接口,路径指向 .app Bundle 内的资源目录:
cfg.SetSysResZip(bundleRes + "/soui-sys-resource.zip", "");
cfg.SetAppResZip(bundleRes + "/uires.zip", "");
// 或 SetSysResFile / SetAppResFile 指向解开的目录
同时建议随 Bundle 分发字体并显式加载:
AddFontResource(bundleRes + "/simsun.ttc");
构建¶
cmake -S . -B build -G Xcode
cmake --build build --config Release
动态库配置下 swinx 会加上 -Wl,-all_load 与 MACOSX_RPATH,以确保 fontconfig / freetype 的符号被完整导出。