前言
SDL原生支持 Windows、macOS、Linux、iOS、Android 等平台,对系统的依赖库很小,在仓库中附带的TestApp1.app,是在macos11.5编译的,放在mac26.5.1居然也可以运行。之前使用GTK做跨平台框架,GTK框架依赖无数个库,国内环境安装GTK开发库非常麻烦,而且mac默认不会安装GTK。
这次我们是认真的。
为了保持与旧项目兼容,而不是推倒重做,暴露在外的常用接口,比如
cpp
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
virtual bool OnCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
库外部的接口仍然使用windows的消息,看一下compat.h,就可以发现定义了常用的windows消息,包括虚拟键码等等。这样的话,我以前的项目随便修补一下,就可以实现跨平台了。
接下来我们开始从零搭建环境、编译库和依赖、编写一个简单的跨平台 Demo。重点是图文演示如何在Xcode构建demo。
一、编译
1,获取源码
git clone https://github.com/xfcanyue/DuiLib_DuiEditor.git
2, 编译SDL3
进入3rd目录
bash makeSDL.sh 或者 sh makeSDL.sh
3,编译DuiLib
进入DuiLib目录
sh makeCMake.sh
sudo cmake --install build
或者使用xmake编译
xmake
xmake install --root
MacOS可以使用xcode编译,项目路径:MacOS/DuiLib/DuiLib.xcodeproj
SDL3 和 DuiLib 一般会安装到 /usr/local/include 和 /usr/local/lib。
4,编译范例
进入目录Linux/gcc,
执行
sh makeCMake.sh
或者
xmake
编译成功之后,会在bin目录中生成TestDuiLib,双击可以直接运行。
这个demo在linux和macos都可以运行。
二、Linux
这个没什么好说的,直接参考Linux/gcc,其实就几个文件,main.cpp, App.h, App.cpp, MainFrame.h, MainFrame.cpp. 看一下入口代码:
cpp
#include "App.h"
int main(int argc, char* argv[])
{
//设置主窗口的名字
uiApp.SetAppName(_T("Test"));
//设置xml资源载入方式
uiApp.SetResType_File(_T("skin"));
//初始化duilib, 注册插件,创建主窗口
if (!uiApp.InitInstance(argc, argv))
return 0;
//进入消息循环
uiApp.Run();
//清理资源。
uiApp.ExitInstance();
return 0;
}
这里和windows版本基本没差别,目前跨平台部分还没支持zip资源包。WindowImplBase这个类只有在windows可以用,也可能windows也用不了了,因为我没维护这个类。强烈建议使用App MainFrame,为程序未来可能的跨平台铺好道路。
三、MacOS
mac应用分为命令版和app版,命令行版本请看Demo: MacOS/Test。
这里着重讲述一下如何构建一个App项目。
我假设你是新手,甚至是第一次使用macos,第一次打开Xcode,正在适应各种不习惯和别扭。
我的mac系统版本是 MacOS Big Sur 11.5,Xcode版本13.2.1,这是在VMWare安装的虚拟机。目前我只发现11.x可以安装虚拟机,其它都跑不起来,也可能是我的配置太差了。
我的电脑配置:
操作系统 WIN10
处理器 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz 2.80 GHz
内存 32.0 GB
硬盘 1T SSD
显卡 Intel(R) Iris(R) Xe Graphics (128 MB)
没有mac本本的同学可以尝试安装一个虚拟机试试。
假设现在SDL3已经安装完了。
如果刚才DuiLib已经使用cmake或xmake编译过了,略过这个步骤。
使用Xcode打开MacOS/DuiLib/DuiLib.xcodeproj,使用Xcode编译DuiLib的原因是方便调试。

点击Build。

当看到 Build Succeeded,表示编译完成了。在DuiLib/Lib目录中可以看到这个文件libDuiLib.a。
现在开始构造APP项目,
1,Xcode -- File -- New -- Project

这里一定是选App,然后点Next
2,App信息

Product Name : 项目名字
Organization Identifier :一般是公司名字,随便写一个,不填不行。
其它的照着图中就行。
3,创建项目

点 Create。
4,把不需要的文件都删了,刚才我们选的是Objective-C语言,我不会,也不想学。

把红色框住的文件都删了。选中 -- 右键 -- Delete -- Move to Trash。
Xcode的源文件解释:
.m:纯 Objective-C 文件(只能混写 C 和 ObjC,不能识别 C++ 类)。
.mm:Objective-C++ 文件,这种文件可以 C++ 和 Objective-C 混编。
.cpp:标准C++文件,不能混编Objective-C或Objective-C++
5,从TestApp文件夹中拷贝一些文件过来,最终文件结构应该是这样的。

6,把文件添加到项目中,可以用右键Add Files to "Demo",或者用鼠标拖进去。

7,现在文件树应该是这样的

8,点击文件树中的Demo -- Build Settings, 设置头文件和库文件搜索目录。
9,点击这里的"+",Add User-Defined Setting。

输入 CONFIGURATION_BUILD_DIR $(PROJECT_DIR)/../../bin
这里是定义输出目录,我们把最终程序输出到bin。如果不设置的话,你可能会找不到最终的输出程序。

10,嵌入动态库文件

我们这里选择嵌入但不签名。正常开发APP是要签名的,很重要。需要注册苹果开发账户,按年收费的,1年100美元。死要钱。。。。
11,预处理器宏定义,输入 DUILIB_SDL=1。

12,检查一下配置

编译的源文件和链接的库,如果"Link Binary With Libraries"多出了一列 Filters ,确保选择了所有平台。

这里表示哪些文件会打包进App,类似windows的资源文件。
13,编写入口函数。在文件数中双击main.m。
#include "App.h"
#import <Cocoa/Cocoa.h>
int main(int argc, char * argv[]) {
@autoreleasepool {
NSBundle *bundle = [NSBundle mainBundle];
NSString *bundlePath = [bundle bundlePath];
NSString *resDir = [bundle resourcePath];
// 如果资源放在 App 包的 Contents/Resources/skin 下
NSString *skinDir = [resDir stringByAppendingPathComponent:@"skin"];
//设置主窗口的名字
uiApp.SetAppName(_T("Test"));
//设置xml资源载入方式,选择一种就行
uiApp.SetResType_File(_T("skin")); //从文件夹中载入xml文件
CPaintManagerUI::SetInstancePath([bundlePath UTF8String]);
CPaintManagerUI::SetResourcePath([skinDir UTF8String]);
CPaintManagerUI::SetRenderEngineType(DuiLib_Render_Sdl);
//初始化duilib, 注册插件,创建主窗口
if (!uiApp.InitInstance(argc, argv))
return 0;
//进入消息循环
uiApp.Run();
//清理资源。
uiApp.ExitInstance();
}
return 0;
}
13,编译。Xcode -- Product -- Build。
编译失败。。。。。为啥。。。。。。
。。。。。。。
。。。。
擦,我忘记把main.m改名了。把main.m改成main.mm,表示C++ 和 Objective-C 混编。
再试一次。
正常来说,应该是成功了,可能会提示签名出错,无视就好了。
去bin目录找到Demo这个文件,双击可以运行。
14,在bin目录里面,右键Demo -- 显示包内容。

经典的DuiLib皮肤包skin,已经出现在里面了。
macos app 实际上是一个文件夹,把Demo这个文件拷贝到windows下,会直接显示成文件夹的样子。
其中MacOS/Demo,这其实也是个命令行程序,经过特殊的包装,变成了app。
所以,入口函数里面,这两句非常重要。
CPaintManagerUI::SetInstancePath([bundlePath UTF8String]);
CPaintManagerUI::SetResourcePath([skinDir UTF8String]);
如果直接使用CPaintManagerUI::GetInstancePath(),获取的路径是 Demo/Contents/MacOS,这和我们的预期不同。
同样的,CApp重写虚函数InitResource(),避免内部再次调用CPaintManagerUI::SetResourcePath(...)。
结语
现在,你可以用 DuiLib 构建同时运行在 Windows、macOS、Linux ,享受"一次编写,处处运行"的便捷。欢迎提交 Issue 或 PR,共同完善这个项目!
仓库地址:
GitHub - xfcanyue/DuiLib_DuiEditor: UIDesigner for duilib · GitHub