Aug 27

UNIX 说,一切都是文件。

偏偏我们每天面对的屏幕不是文件,窗口不是文件。

 

cli 程序都是从 stdin 获得输入,偏偏为何 gui  程序要从所谓的 “窗口消息” 获得呢?

“从一切都是文件” 的角度而言,这是不可取的。

我来提出一个构思:

该设计由两部分组成,一个内核实现的虚拟文件系统 X , 一个在用户空间实现的 compositor. 

compositor 是可以运行时替换的 :) 事实上没有 compositor 程序一样执行,只是没输出。

实现一个虚拟的文件系统, X, 将它挂载到  /dev/X 

然后在  X 下创建一个文件就是创建了窗口了,但是为了容纳 "子窗口" 的概念,所以窗口是一个文件夹。子文件夹就是子窗口。窗口文件夹下有个 pixmap 的文件, 就是窗口内容了。程序通过写入该文件就等于是向窗口绘制了。如果不是 pixmap 文件,可以建立一个叫 postscript 的文件,使用 postscript 描述窗口内容也行。更可以建立一个到别的窗口的 pixmap 的符号链接,表示拷贝窗口内容。还有一个 prop 的只读文件,里面存放的是窗口的各种属性。要改变属性,只能通过 ioctl 作用于代表窗口的那个文件夹,或者有 OS 自动修改。

窗口管理器可以通过操纵该目录来实现。

OpenGL 绘制,创建好窗口后,创建 pixmap 文件,然后打开它,获得一个 fd ,  使用该 fd 创建一个 OpenGL 上下文即可。建议的 API 是   XglCreateContextfd(int fd, XglConfig * config);

但是,。。。。 pixmap 的内容并不会跑到屏幕上。

要让窗口的内容跑屏幕上,你需要一个 compositor , compositor 通过读取 pixmap 文件将窗口最终合成到屏幕上。bitmap 文件的内容 compositor 可以不用读取,那样会很低效。

bitmap 的内容通常是gui程序使用 OpenGL 绘制的,pixmap 内容虽然可以被直接读取,但是这是不高效的。 compositor 通过 ioctl 可以直接获得该 pixmap 对应的 buffer 指针,然后可以用这个BUFFER指针创建 OpenGL 纹理贴图,绘制到最终的屏幕上。这中间不存在打开图像数据的来回拷贝。如果 pixmap 不是 OpenGL 绘制的,那么直接创建纹理贴图可能会失败,但是可以直接打开,进行块拷贝操作就可以了,内核已经为 pixmap 自动启用共享内存。所以开销虽然比直接创建纹理贴图大,但是也比通过 socket 传输pixmap 小很多。

compositor 使用 fnotify 文件系统通知API 获得窗口的更新情况。

如果  窗口下面没有 pixmap 文件,而是 postscript 文件,那就需要 compositor 自行对 postscript 文件进行渲染了。 如果你的显卡居然带有硬件 postscript 解释器, compositor 都可以利用呢!

窗口移动对应用程序是透明的,是 compositor 在移动窗口。

窗口pixmap不能改变大小,要改变大小,必须重新创建窗口的 pixmap 。但是在重新创建窗口前, compositor 可以选择使用原来的窗口 pixmap ,知道它任何合适的时候要求应用程序重新创建。

这个过程很简单, 直接 delete pixmap , 应用程序就必须重建它。可以是 compositor 在delete,也可以是 程序自己。

字体:

如果是 postscript 输出的程序,可以使用 postscript 字体。:) 

如果是 OpenGL 输出,则必须使用 freetype 这类程序。可以使用 OpenGL 后端的  cairo  pango 简化字体的使用。 

那么,鼠标消息呢? 键盘消息呢?

答案是,/dev/X/input . 直接打开这个文件就可以了。

但是 ... ... 如果没有 compositor  这个读取文件却没有任何数据。

为何呢? 因为这个文件的数据是 compositor 准备的。每个窗口文件夹下都有一个只写的文件(只有 compositor 组和程序所有者所在组有权限写入) , message , 这个是由 compositor 写入的。写入该文件的消息就会被程序从 /dev/X/input 读取到。 每个程序只能读取到compositor发给自己的输入。除非广播信息,那是 compositor 直接写入 /dev/X/input 的,会被每个程序读取到。

如果一个程序需要读取全局消息,比如全局快捷键,那只能和  compositor 商量去了。建议的办法是 compositor 提供 dbus 接口让程序告诉 compositor 它要获得的键盘消息。

输入法:

输入法由 compositor 提供 compositor 加载  IM MODULE 由 IM MODULE 提供输入法。

简单的输入 compositor 将结果直接反馈给程序做输入就可以了。

如果程序想有预编辑的话,必须和 compositor 商量。一般程序使用带编辑功能的 widget 而不是自己实现。该 widget 由 UI toolkit 提供。UI toolkit 可以使用IM MODULE 的形式和嵌入 compositor的 IM MODULE 通过 dbus 通信来实现复杂的预编辑功能。建议该 dbus 接口规范化,使得不同的 IM MODULE 可以相互通信。

compositor 的 IM MODULE 也可以继续和另一个真正的 IM engine 通信实现输入法呢!