Archive for May 2007

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

KDE4 的第一个 alpha 版本发布了,我们可以找到 for OS X 的二进制文件下载。有个问题早在 KDE 社群宣布 native 的 OS X 移植时就被提起,我现在才有空瞎说点看法。

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

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

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

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

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

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

zonble 曾经有一篇系列文章 (1, 2, 3),比较简略的介绍了如何在 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 指出,虽然原本 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 只不过是一种用目录来组织文件的形式,通过放置 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 中,给出了 'thng' 的内容格式。我们会在下面逐一解释。

关于 Resource 的资料,可以参考 Inside Macintosh 的 Resource Manager 部分。

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

#define UseExtendedThingResource 1
#include <Carbon/Carbon.r>
#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 还介绍了如何创建 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 的参考手册,它的 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 中解释,通过资源文件来设置图标的方法已经不建议再用,你可以通过在 Component 的 Info.plist 中指定 tsInputMethodIconFileKey 一项来定义图标。你可以参考 QIM 的作法,修改 Xcode 项目的 Products/BasicInputMethod.component/Contents/Info.plist 文件,添加下面几句:

<key>CFBundleIconFile</key>
<string>Icon.icns</string>
<key>tsInputMethodIconFileKey</key>
<string>Icon.icns</string>

其中 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,应当可以编译出一个放在 /Library/Components/ 下便能顺利执行的输入法模块来,希望这篇介绍能帮助更多的朋友开发开放源代码的 OS X 输入法。

找到一个不错的 Linux 发行版

最近因为开发内核程序的原因,需要找一个很小的发行版,放在 qemu 虚拟机里测试。

找了找,发现 Core GNU/Linux 很不错,光盘启动安装,把硬盘挂载上,分区,执行安装脚本,chroot,装上 grub 就搞定了,总共也只用 200M 左右的空间,主要软件都是 bleeding-edge 的,很方便。

网志改名

网志改名,其实也早该修改了,这个 web log 的内容将不再以 web 标准的宣传为主题,事实上,很早就不是了。

之所以今天来改,是因为以前只是觉得厌倦,但现在发生的 XHTML 到 HTML 5 这件事情让我对“web 标准”的宣传感到恶心了,我不能理解为什么那些所谓的“专家”,上一分钟还在向人推销 XHTML 是多么的好,下一分钟就翻脸不认大唱 HTML 5 的赞歌。脸皮之厚,堪称神奇。

既然如此,我也懒得帮他们一起圆下一个谎,反正是浏览器厂商你方唱罢我登场,慢慢唱吧,我写我的程序去。

Helvetica: The Film

helvetica.png

这个 blog 的长期读者也许读过我翻译的 The Scourge of Arial 一文,其中介绍了 Helvetica 的由来。而今年年初,看到 Helvetica: The Film 的公开,忍不住将它称做“年度最期待纪录片”。

目前它已经在欧美各处公映,却没有看到在中国的任何一处上映的计划,如果你能提供帮助:

If you work with a film festival, museum, cinema, or arts group and would like to organize a screening, please contact us via email: helvetica (at) swissdots.com

因为,我们想看 Helvetica我们不能错过它

管理大型的 Cocoa 项目

NetNewsWire 的作者 Brent Simmons 最近的一篇文章介绍了如何有效的管理大型 Cocoa 项目。他的建议可以总结为下面几条:

为了改善代码的可读,可查找性,应该遵循:

  1. 只对那些没有明显关系的对象之间的交互使用 Notification。
  2. Key-Value Observing 也是很危险的,应该只对 Preferences 项目使用这一特性。
  3. 只将 Binding 用于很简单的情形,复杂的 TableView 还是用 datasource/delegate 比较好。

管理代码时可以使用的技巧:

  1. #pragma mark 来划分代码的区域
  2. 用 Ctrl-2 来列出当前打开文件的符号 outline。
  3. 用 Shift-cmd-D 来快速打开指定文件。
  4. 用 opt-cmd-T 来将当前打开的文件和左侧的目录树同步。
  5. ctrl+double click 打开符号的定义,opt+double click 打开符号的文档。
  6. 在文件系统中用平面方式组织文件,不划分多层目录,在 Xcode project 中用 group 来划分层级结构。

对于有 Cocoa 开发经验的人,尤其是管理过这种超过 200 个源文件的较大项目的人来说,这些经验是很有用的。

用 mac + shntool + lame 转换 APE 到 MP3

这里介绍在 Mac OS X 中如何用 mac, shntool 和 lame 来完成 ape - mp3 的工作,只需要一个命令就可以完成。

mac 用我以前贴的那个。

shntool 可以 sudo port install shntool,但它的来源网站似乎被 GFW 了,你可以自行下载编译:


$ wget http://www.vi.kernel.org/pub/mirrors/gentoo/source/distfiles/shntool-3.0.2.tar.gz
$ tar zxf shntool-3.0.2.tar.gz
$ cd shntool-3.0.2; ./configure; make; sudo make install

lame 可以用 macports 上的:

$ sudo port install lame

然后这样处理 APE:

$ shntool split -f CDImage.cue -t '%p - %t' -o 'cust ext=mp3 lame --quiet - %f' CDImage.ape

其中 -t ‘%p - %t’ 是指定生成文件的标题,%p 表示 Performer,%t 表示 Title,都是从 cue 中读出的。’cust ext=mp3 lame –quiet - %f’ 表示输出文件用 lame 处理,默认参数,你当然可以在这里调整 lame 的参数。

举例如下:

~/Music/OST - Joe Hisaishi - Kikujiro no natsu [APE] jjgod$ shntool split -f CDImage.cue -t “%p - %t” -o ‘cust ext=mp3 lame –quiet -  %f’ CDImage.ape
shntool [split]: warning: discarding initial zero-valued split point
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Summer.mp3] (6:25.32) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Going Out.mp3] (1:17.35) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Mad Summer.mp3] (2:55.13) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Night Mare.mp3] (1:50.52) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Kindness.mp3] (1:58.23) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - The Rain.mp3] (5:38.70) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Real Eyes.mp3] (3:16.52) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Angel Bell.mp3] (3:13.20) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Two Hearts.mp3] (2:01.33) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Mother.mp3] (2:13.57) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - River Side.mp3] (6:14.40) : 100% OK
Splitting [CDImage.ape] (40:14.15) –> [Joe Hisaishi - Summer Road.mp3] (3:08.38) : 100% OK

-f 参数指定读取切割点的文件,如果不指定则从标准输入读取,所以,如果 cue 文件是 GBK 编码的,我们可以:

$ iconv -f gbk -t utf-8 CDImage.cue | shntool split -t "%p - %t" -o 'cust ext=mp3 lame --quiet - %f' CDImage.ape

就完成了 GBK 到 UTF-8 的转码。速度还是挺快的,估计它用 pipe 来把 mac 的输出送给 lame,因此这两个进程可以并行跑,CPU 能跑到 120% 的样子。

这个办法的好处是非常简单,不需要另外写程序,唯一的缺点是只能依靠 cue 的信息,也没有自动加上 MP3 的 ID3 Tag,不太方便直接导入 iTunes 中,不过我们可以写个后期处理的程序 (比如利用 cue 给出的一些信息,或者用 shntool 输出的 WAVE 信息,调用 freedb/cddb 的服务,或者根据 mp3 来调用 MusicBrainz 的服务) 给它加上 tag。