KDE4 for OS X 会受到用户欢迎吗?

KDE4 的[第一个 alpha 版本](http://www.kdecn.org/announcements/announce-4.0-alpha1.php)发布了,我们可以找到 for OS X 的[二进制文件下载](http://ranger.users.finkproject.org/kde/index.php/Home)。有个问题早在 KDE 社群宣布 native 的 OS X 移植时就被提起,我现在才有空瞎说点看法。

Mac OS X 的用户可能是世界上对软件的界面一致性 (consistency) 要求最高的用户,也许是因为选择 Mac 并热爱 Mac 的人的审美喜好也类似,所以他们的嗅觉也惊人的一致而灵敏,对软件的移植而言,他们能热切地赞扬优秀的 port,比如 [Skype](http://www.skype.com/download/skype/macosx),同样也有大量移植软件因为距离 OS X 用户的界面要求太远而不被关注。常见的跨平台 GUI Toolkit: GTK+, Qt 和 wxWidgets 都是如此,除非软件本身缺少替代品,比如 WireShark 和 [aMule](http://www.amule.org/),用户才会勉强接受它。

与 Windows 开发者喜欢创造千奇百怪的界面 skin 不同,也与 Linux 开发者干脆懒得认真开发界面不同,OS X 的开发者的倾向是紧跟 Apple 的步伐。Apple 放弃 [Brushed Metal](http://indiehig.com/wiki/UI_Elements_to_Avoid) 风格,他们也立即放弃,Apple 在 Mail 里用 [Unified Aqua](http://indiehig.com/wiki/Unified_Aqua) 工具栏,他们也赶紧用,Apple 在 iLife 和 iTunes 里开始用 [Polished Metal](http://indiehig.com/wiki/Polished_Metal) 和 [HUD 窗口](http://indiehig.com/wiki/HUD),并放弃 Drawer,他们也一一复制,造就的结果就是用户口味特别刁钻,稍有点异味都能品出来,比如 Shiira 的开发者最近伴随 2.0 版本的发布提供了 [HUD Framework](http://shiira.jp/hmblkappkit/en.html),但却[有人声称](http://dev.lipidity.com/apple/shiira-2-released-offers-hud-framework) CSSEdit 的 HUD 窗口“ blow these away by a mile”——就是这么苛刻,不过,如果你也是个对 GUI 设计精益求精的开发者,你就能习惯它。

大多数的跨平台 GUI Toolkit 最大的问题在于,它们能达到的只能是所有平台支持的“最大公约数”,而永远无法在任意一个平台下发挥最大效能,所以它们只好设置自己的 Design Guidelines,为了让不同平台下都呈现相同的外观和习惯。它们可能去为了 [Apple Human Interface Guidelines](http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGIntro/chapter_1_section_1.html) 乃至 [indieHIG](http://indiehig.com/wiki/Main_Page) 去修改自己的界面设计习惯吗?恐怕很难。

而且 KDE 的习惯又是以复制 Windows 为主,包括 File Choose Dialog 的设计,OK, Cancel Button 的位置,等等,从而就更难让 OS X 用户满意了。

事实上,跨平台的 GUI Framework 从来就没有达到过真正的跨平台的接受度,一般都只能在一个平台下拥有大量用户,其他平台下则寥寥无几。我觉得还是在软件内部封装一个跨平台 GUI 层,根据不同的需求在不同平台下调用不同的框架来实现会比较好,比如 vim 和 emacs 的做法就是如此。

现在如何在 Mac OS X 中写一套输入法 (一)

[zonble](http://zonble.twbbs.org/) 曾经有一篇系列文章 ([1](http://zonble.twbbs.org/archives/2005_07/789.php), [2](http://zonble.twbbs.org/archives/2005_07/790.php), [3](http://zonble.twbbs.org/archives/2005_07/791.php)),比较简略的介绍了如何在 Mac OS X 下如何开发一个输入法,其中有一部分解释不足的地方,我希望在本文补足,如果你还没有读过 zonble 的原文,请先读读。

第一部分,关于资源文件。

我们都知道,Resource Fork 是 Mac OS Classic 时代的残余,通过 Resource 来作程序的定制 (比如 l10n) 也是在 Classic 时代推荐的,在 Modern Carbon 与 Cocoa 已经被 .nib 文件替代了,然而,Mac OS 的系统 Component 的编写直到现在仍然未有变化,仍然需要 Component 程序有一个 Resource (.rsrc) 文件,Component Manager 利用这个 Resource 文件了解 Component 的名字、入口点和图标等信息。

然而在现在的 Mac OS X 中的几个改进值得注意:

首先,[TN2012](http://developer.apple.com/technotes/tn/tn2012.html#TNTAG83) 指出,虽然原本 Component Manager 支持你将 Component 编译为一个 .dylib,将 .rsrc 作为它的 resource fork 使用,但对于 Mach-O Component 这个方法已经不_推荐_了,建议以 Bundle 的形式来存放,也就是将 .rsrc 文件存放在 .component/Contents/Resources/ 目录下。

顺便说一句,也许有人会把 Components 和 Bundles 的概念混淆,其实两者的差异是很大的,Component 是系统启动/用户重新 Login 时就会载入的代码,放在 /Library/Components (或者 /System/Library/Components 和 ~/Library/Components) 下的 .component 文件都会被尝试载入。如果你不 Logout/Login,那这些代码_不会_被重新载入。

而 [Bundle](http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFBundles/CFBundles.html) 只不过是一种用目录来组织文件的形式,通过放置 PkgInfo 和 Info.plist 文件,使系统从外在把这个目录当作整个一个文件来处理。

所以 Component 和 Bundle 其实是两个正交的概念:Component 不一定要用 Bundle 来存储,Bundle 存储的也不一定是 Component (有可能是 .app, .framework)。

其次,TN2012 同样提到,Mach-O 格式的 Component 是唯一支持 Intel 架构的二进制格式,老旧的 CFM 格式 Component 已经不_应该_再使用。所以我们应该修改 .rsrc 的源格式 .r 文件,使之支持 Intel 架构。

在介绍怎么修改之前,先扯开一点,说说 .r 和 .rsrc。和 Windows 一样,资源文件是要编译以后才能使用的,源文件格式称为 Rez Input Format,而目标文件格式显然称为 Rez Output Format。用来编译和反编译的分别是 /Developer/Tools 目录下的 Rez 和 DeRez 两个命令行程序。

显然,我们需要关心的只是源格式。然而这个源格式非常的灵活,因为不只是 Component 要用到 resource,其他的东西也要用到,比如应用程序的 menu 信息,就曾经是用 resource 来指定的 (和 Win32 编程一致)。

资源有两个需要注意的概念,分别是 Resource Type 和 Resource ID。在 .r 文件中,我们通常这样写:

resource ‘abcd’ (123)
{
… 资源的内容
}

resource ‘efgh’ (456)
{
… 资源的内容
}

其中 `’abcd’` 和 `’efgh’` 就是 Resource Type,通常这四个字符是 Apple 预定义的,虽然也可以随便起,但 Component Manager 只会寻找它所认识的资源类型:`’thng’` (是 thing 的缩写,不知道为什么起这个名字)。而 123 和 456 是 Resource ID,其实只要你在这个 .r 里定义的同一个 Resource Type 的 Resource ID 不重复就行了,所以不同类型的资源其实可以用同一个 ID,后面我们会看到,BIM.r 就是如此的。

而中间省略的部分,资源的内容,根据不同的 Resource Type 而定,在 [TN1004](http://developer.apple.com/technotes/tn/tn1004.html) 中,给出了 `’thng’` 的内容格式。我们会在下面逐一解释。

关于 Resource 的资料,可以参考 Inside Macintosh 的 [Resource Manager](http://developer.apple.com/documentation/macos8/Files/ResourceManager/resourcemanager.html) 部分。

下面是我们解释 BasicInputMethod 的资源文件 BIM.r:

#define UseExtendedThingResource 1
#include
#include “BIMScript.h”

首先,必须在包含 Carbon 的头文件之前定义 UseExtendedThingResource,否则,Component Manager 就不支持扩展的 thng 格式,这是 m68k 到 Power PC 转换时出现的变化,在 Inside Macintosh 的 Component Manager 一章里没有介绍,但 TN1004 里解释了。包含 BIMScript.h 这个文件是为了引用一些常量 (资源文件可以引用头文件,这一点和 Win32 也是一样的)。

resource ‘thng’ (128)
{
‘tsvc’,
‘inpm’,
‘appl’,
0x8000 + kBIMScript * 0x100 +
kBIMLanguage,

到了 thng 部分,Resource ID 128 其实是随便起的。前四个项目 zonble 的文章都解释了,注意在 [TN2128](http://developer.apple.com/technotes/tn2005/tn2128.html#TNTAG4) 还介绍了如何创建 Palette 类型的输入法的方法,第一步就是要将这里的第二项 `’inpm’` 改为 `’cplt’`。而第四项,Component 的 Flags 是你的 International 设置里看到的所有输入法时右侧的 Script 一栏的基础。需要注意,如果你希望写一个通用的输入法框架 (类似 SCIM 或 OpenVanilla),像下面这样在 BIMScript.h 里定义常量会比较合适:

#define kBIMScript smUnicodeScript
#define kBIMLanguage langUnspecified

其中 script 的定义可以让你在 International 设置里看到 Script 是 Unicode,language 的定义的用途我还不太清楚,欢迎补充。

关于可以定义的 script 和 language 常量,可以看 [Script Manager](http://developer.apple.com/documentation/Carbon/Reference/Script_Manager/index.html) 的参考手册,它的 API 虽然几乎全部 deprecated 了,但定义的常量还是有用的。

kAnyComponentFlagsMask,
‘dlle’, kBaseResourceID,
‘STR ‘, kBaseResourceID,
‘STR ‘, kBaseResourceID + 1,
‘ICON’, kBaseResourceID,

下面定义了几个重要的参数:Component 的入口点函数应该在 `’dlle’` 资源里找,Component 的名称字符串,应该在 ID 为 kBaseResourceID 的 `’STR ‘` 资源里找,Component 的信息字符串,必须在 kBaseResourceID + 1 的 `’STR ‘` 资源里找,如果你看完了上面关于资源的解释,这就应该很好理解了。让我们看看实际这几个资源的定义:

resource ‘dlle’ (kBaseResourceID) {
“BIMComponentDispatch”
};

resource ‘STR ‘ (kBaseResourceID)
{
“Basic Input Method”
};

resource ‘STR ‘ (kBaseResourceID + 1)
{
“A Really Cool Input Method”
};

就一目了然了,入口点是一个叫 `BIMComponentDispatch` 的函数,名称是 `”Basic Input Method”`,信息是 `”A Really Cool Input Method”`。

回来继续看 ‘thng’ 资源的内容:

#if UseExtendedThingResource
0x00010000,
componentHasMultiplePlatforms,
kBaseResourceID
{
0x8000 + kBIMScript * 0x100 +
kBIMLanguage,
‘dlle’,
kBaseResourceID,
platformIA32NativeEntryPoint,
}
#endif

首先,我们注意到,这部分是 Power PC 开始扩展的内容,所以应当用条件编译括起来,然后是这个 Component 的版本,这里是 1.0。第 3 项是 Icon Family 的资源 ID,什么是 Icon Family 呢?在 OS Classic 里出现的这个概念是指,你可以把一堆 Icon 组织为一个 Family,分别是不同大小、色深的图标,当载入资源时,你只需要指定载入哪个 Family 的 ICON,就可以自动根据你要用到 Icon 的位置,判断出应该用这个 Family 里的具体哪个图标了。Basic Input Method 的 Icon 原来真的是在这里定义的,不过正如 zonble 原文提到,我们应该只能看到 `’kcs8’` 这个图标了,这是一个 32×32 的 256 色图标。

不过,现在我们不必使用 zonble 给出的修改资源文件的方法来修改图标了,[TN2128](http://developer.apple.com/technotes/tn2005/tn2128.html#TNTAG6) 中解释,通过资源文件来设置图标的方法已经不建议再用,你可以通过在 Component 的 Info.plist 中指定 `tsInputMethodIconFileKey` 一项来定义图标。你可以参考 QIM 的作法,修改 Xcode 项目的 Products/BasicInputMethod.component/Contents/Info.plist 文件,添加下面几句:

CFBundleIconFile
Icon.icns
tsInputMethodIconFileKey
Icon.icns

其中 Icon.icns 就是 .component/Contents/Resources 下放置的一个图标文件,支持 .tiff 和 .icns。

接下来的 { } 中部分是一个专门描述模块的 Platform Info 的部分,其 4 项依次是 Component Flags,’dlle’ 和 ‘dlle’ 资源的 ID,最后一部分最重要,描述编译在哪个平台下执行,这里指定 IA32,就是只给 Intel Macs 用,你也可以参照 TN2012 来编译 Universal Binary 的 Resource。

至此,资源文件的介绍就到这里。使用官方下载的 BasicInputMethod 的项目加上我这里附上的 [BIM.r](http://jjgod.org/code/BIM.r),应当可以编译出一个放在 /Library/Components/ 下便能顺利执行的输入法模块来,希望这篇介绍能帮助更多的朋友开发开放源代码的 OS X 输入法。