Jul 30

抱歉, 居然半年多不更新.  其实不是不更新啊, 而是我迁移到了 http://microcai.org 了.

 

当然, 如果有比较好的文章, 我还是会 post 到这里的.

 

多谢支持.

 

 

Dec 29

 

一直免费给 Gentoo 打工。默默的维护 overlay。
昨天,哦,是前天了,刚刚过了12点。给群里一个人帮忙安装Gentoo,陆续的帮了几次,回答了几个问题。他说麻烦我很久了,得表示一下物质感谢。
于是就捐助了我一些物质。
虽然钱财是身外物,但是一直过着 0 收入生活的我,还是很感动。在此特别留纪念。

PS:

他还给我的 qbasic 编译器提交过补丁,如果你们关注 qbasic 编译器的提交历史就能找到他。 :)

Nov 17

 

编译原理:
 
        lexical analyze -> syntax analyze  -> build Abstruct Syntax Tree -> convert 
AST to  Concreat Syntax Tree -> Generating  "register reallocate" intermediate  
language -> Generating NATIVE assembly -> native machine code -> executable 
object .
 
为了学习编译原理, 找了个简单的语言编写编译器. 我选择了 QBASIC 这个人人都会的语言.
 
QBASIC 曾经有编译器, 在 16位时代的时候. 到了 32位时代, BASIC 语言就进入脚本行列, 解释执行
了. 到64位时代嘛 .. ... 恩恩, 没有编译器了. 解释器还算是有开源的几个.
 
QBASIC 语言漂亮,优雅别致. 简单易学. 大家不用它的原因无外忽无法和C 和谐共存. 而无法和C和谐
的原因很简单: 不是编译为本机代码.
 
所以, 我决定开始写一个QBASIC编译器, 能利用现代硬件, 利用上现代意义的优化能力的编译技术来
为 QBASIC 加速!
 
QBASIC 比 Go 语言简单多了, 如果能用编译解决效率问题, 自然打败Go不在话下.
 
幸运的是我们有 LLVM . 我只需要写个前端就可以了. 生成平台汇编的事情全权交给 llvm 打理了.
So, 这就是我的想法, 经过5 天紧张的开发, 已经实现基本功能了.
 
有人说我又写一个编译器, 是个国际笑话, so , 主题就如此了.
 
 
在这里宣布这样的事情是因为觉得可能找到共同兴趣的人能一起研究开发编译器.
 
我设定了3个目标:
 
        1 实现完整的 QBASIC 语言. 至少是除了不建议使用的功能, 其他都要实现.
 
        2 实现和C的无缝集成. 我将 BASIC 的函数调用约定设定为和C语言一致来解决这个问题. 因为是本
机码, 所以使用外部库的任务和C语言一样,交给链接器完成. 使用动态链接库不需要语言和运行时本
身的任何支持. 
 
        3 进行未来方向的语言扩展.
 
 
最后, 最重要的, show me the code 
请移步访问 https://github.com/microcai/llvm-qbasic
 
 
实现方面的细节考虑:
1)      QBASIC 支持数组, 数组的下标可以在定义的时候设定起始点. 第二, 数组自动扩展
        实现办法: [1] 数组实现为一个 STL 容器. 对下标的访问由编译器自动转化为对 operator [] 的调用.
operator 函数由编译器自动生成. [2] 内置类型的operator则由运行时实现. 以便进行快速实现更好
的算法 [2] 变量离开作用域后要自动调用构析函数释放资源. 这样数组才不会导致内存泄漏.
 
2) 字符串. QBASIC 的字符串是内置类型. 字符串可以进行很多操作. 内存自动管理. 字符串可以做 + 
法和比较运算.
        实现办法: 字符串实际上为一个结构体, 由运行时库实现. 编译器自动插入对运行时的调用来完成
字符串操作. 字符串离开作用域要自动调用释放函数释放内存
 
3) PRINT/INPUT 语句. 和C不一样, BASIC 把 IO 操作内置为语言的一部分.
        到目前为止我的编译器已经实现了部分的 PRINT 指令. 对打印到屏幕的PRINT语句直接转化为对
printf的操作. 这样一个不使用字符串,不输出到文件的QBASIC程序, 编译后将只依赖 C 运行时.
        PRINT 有个可选的参数 #(数字) , 表示打印到数字指示的文件中. 这个必须由运行时来完成了.
 
 
4) 所有的运行时我都放到了  libbrt -   the qBasic RunTime library  -里了. 目前为止没有任何实现. 运
行时默认将采取静态链接的方式. 对于生成脱离运行时的程序非常有用.
 
Oct 12

 

曾几何时,学会了C语言。安装了VC6这个神叉IDE。

然后被迫开始CPP路程。由于C++兼容C,所以一直在以C的方式写C++。然后慢慢的开始学写C++代码。写C++代码是从MFC开始的。慢慢的,我学会了用class,感觉是个比struct好用的多的结构体。再慢慢的,我学会了继承,还有... 多重继承。

MFC 就是我的导师。开始不停的向MFC学习。既然用了c++的继承,就认识到了继承的陷阱。继承后,构造函数和构析函数的执行次序,等等。还有虚继承后的各种问题和陷阱。

一一去了解。

 

在程序结构方面,开始向 MFC 看齐。喜欢把任何操作都包装到class里。设计class的时候,开始过度设计。总考虑到某天我会需要继承它。说不定还需要多重继承。等等。明明已经设计好class,却喜欢继续添加功能。永远用不到的功能。只因为我“未来可能需要在别的程序里用,可能需要继承它。”

任何一个程序,我都为它设计对象,全局的程序对象又有子对象,子又有子,子子孙孙无穷尽也~

成员变量一多,构造函数里成员变量初始化代码就变多。然后设计构造函数重载。写更多的用不到的构造函数。

我没有意识到,我已经犯了过度设计的毛病了

 

而c++另一个强大的功能“模板”我却一无所知。当我发现了c++还有模板功能的时候,我只是简单的尝试了一下,发现模板无法在VC下很好的被自动完成。又听说模板会导致代码膨胀,诸如此类。我只小用一下模板。就丢弃了模板。继续我过度设计的c++之路。

某一天,我开始Linux之路。

当我开始写出第一个GTK程序的时候,发现了C之美。C可以写出和C++一样的对象代码。而且没有了 C++的许多陷阱。

也正因为C,我开始摒弃了过度设计。所设计的API皆以现阶段够用为限。未来怎么样,未来再添加好了。没有了c++的包袱,再也不设计class了。都是简单的函数。函数完成一个“过程”。而不再是操作一个“对象”。让我把精力放到了“过程”本身,而不会再去设计“漂亮的对象”。

之后阅读到了Linus对C++的一篇讽刺邮件。对比自身,多多感触。c++确实是个容易写出糟糕代码的语言。

从此发誓不再用c++,只以C写代码。

 

再一次拾起C++,是因为c++11的发布,有了一些新的语法糖。我虽然不再打算写c++代码,但是没打算不再读别人的c++代码。如果别人用c++11写了新的代码,我确看不懂,不是损失是什么。我决定看一看c++11。

c++11最令我着迷的新特性就是lambda。还有新的for语法。当然,boost::bind 和 boost::function 进入了 std。成为的标准的一部分。我对 std::function 的印象只限于“更好的函数指针替代”。

lambda 一用,发现非常不错。好多麻烦的 static 函数都可以被匿名的 lambda取代了。我喜欢这种让名称空间更干净的感觉。

我开始纠结lambda了。动摇了。如果只用c++兼容C的那部分,然后lambda,就是带lambda的C嘛!这样自欺欺人的开始用C++。

参与了朋友的一个小项目。他是个重度c++粉。但是因为他的关系,我开始真正的拿起了boost。他说,没有boost他就没办法写代码。

这里有个小插曲,我在Linux下一直用eclipse编程,但是也因为boost把eclipse搞崩溃了,重拾了KDevelop。结果发现KDevelop已经不再是我第一次使用的时候(Kdevelop 3.? 的样子,好多年前的尝试。)那样使用正则匹配的自动完成了,真正的和eclipse一样是通过语法解析获得的自动完成列表。这导致KDevelop的自动完成功能异常强大,和 eclipse一样智能。所不同的是,eclipse在解析boost的代码的时候会时不时的崩溃,而且eclipse花了很久很久的假死状态解析boost头文件。毕竟是java开发的,速度还是不行啊!但是KDevelop完成boost头文件的解析只需要几秒钟。第一次打开cpp文件的时候,KDevelop在后台花了及秒钟解析了boost头文件,然后打代码的时候boost的所有功能就全部都能提示出来了。和eclipse一样的自动完成,速度快了几百倍,我马上切换到了KDevelop,并卸载了eclipse。

其实之前也尝试过boost,都被假死状态的eclipse弄怕了。所以关了eclipse的index功能没了boost的语法提升瞎写过。

但是换到KDevelop后那畅快淋淋的感觉,打出 boost:: 后那完善的提示,我马上开始全身心的试用boost。

就是这一试用,让我重回了c++的怀抱。重回c++后,我发现了c++正确的用法:

  • class只能作为基础,构建比int/long/char要高级点的对象。绝不能用来构造程序本身。像MFC那样搞个 CMyApp 绝对是对c++的侮辱。也就是说,程序主体仍是C语言式的。
  • 可以用class构建string,构建bigint,用重载为bigint编写大数乘法... 这才是 class 和运算符重载的意义。绝对不能肆意滥用。
  • 必要的时候编写模板代替宏。
  • 模板更好的用法是使用boost这样现成的模板库。
  • 设计回调函数,使用 std::function/boost::function。这样可以使用 std::bind/boost::bind 绑定任意多的参数给回调函数用,避免C里的void*user_data设计。
  • 虚回调函数绝对是个傻逼设计。迫使用户使用继承并进行虚成员函数重载。
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 通信实现输入法呢!

 

 

 

 

 

Jul 17

Linux 内核本身的 TTY 控制台代码十分的 bugy,  当然,能工作而已。

不过,最关键的问题是不支持 国际化。 不支持矢量字体,事实上在我打补丁前,都不能显示 CJK 字符。

TTY 暴露给用户空间的接口事实上也就是 /dev/ttyXX 字符设备。 XX 从 0 到 63

如果能在用户空间实现了 tty 设备,然后由用户空间的程序绘制图形到 /dev/fb0 不也就实现了控制台了么?!而且是 user mode 的!

意味着我们可以用 FreeType2 使用矢量字体,甚至可以用 Pango 进行多国语言的排版!

但是要如何实现在用户空间这边搞 tty设备呢? tty 设备虽然最大的用处类似 pipe , 在显示设备和cli 程序的 stdout 直接建立连接,但是包行了一系列的 ioctl 来实现各种特殊功能包括 ncurses 。这就不是 pipe 能实现的。如果简单的用  /dev/pts/ 来模拟,也会有问题。 pts 和 tty 之间还是有许多不相同的地方,而且如果要在 pts 实现,那做这个 pts master 的进程就只能是 pid 1 了,因为 agettty 就是 pid 1 fork  出来的。pts 只能父子继承。

所以得寻求替代方案,幸运的是,我们有 CUSE. 由 FUSE 提供的另一种强大功能。CUSE 能在  user space 实现一切字符设备的操作功能,于是我提出实现一个 Userspace TTY Daemon, aka uttyd .

uttyd 启动的时候将原有的 /dev/tty* 全部删除,然后自己建立新的 /dev/ttyXX 节点,当然  uttyd  要在 agetty 之前启动。

然后后续运行 agetty 的时候利用的 tty 就是 uttyd 实现的了。

uttyd 在内部实现了一个支持 UTF-8 的 VT100 兼容控制台,利用 SDL 在framebuffer 上直接绘图。

 

为何用 SDL 呢? SDL 足够轻量,而且可以在 X 环境下调试,如果是在 framebuffer 下,自动的使用 /dev/fb0 而不需要修改程序。

uttyd 利用 fontconfig 进行字体配置,并使用 FreeType2 进行渲染,最后用 SDL 绘制到屏幕上。

只要你系统里安装了中文字体,你就能实现中文控制台。

 

 

Jul 14

To use a GUI, you need windows, which in turn means you need a Window System.

Window System  such as X11, Quartz , and Presantation Manager (Which is used by OS/2 and the fellowing Microsoft Windows), and of-course the comming Wayland.

 

What is a Window System? In short:

Window System provide a way to manage windows, to efficently share the "Screen Space".

Window System provide a way to manage inputs, keyboard focus etc.

Window System provide a way so that Apps can Coperate, clipboard, drag&drop, window embed, etc.

But Window System Does Not:

Provide a way to draw your windows. This the job of Render System.

Provide a way to draw TEXT. This is the job of   Text Layout & Font Rendering System which in turn use Render System to draw the text.

Provide a way to use GPU. This is of-course the job of Render System.

Provide Standard Widgets, such as Buttons, Check Boxs, etc

Take an Example of Prsentation Manager:

The Window System is provicded as a Client DLL -- USER32.DLL, the server side is inside the kernel called win32k.sys.

USER32.DLL is only responsable for Managing Windows and Inputs. For Drawing, it goes to GDI32.DLL.

USER32.DLL Does not provice Standard Widget, Instead,  you use commctl32.DLL for bottons,dialogs etc.

If you want to use GPU,  you need DirectX or OpenGL, not USER32.DLL itself.

For TEXT,  it goes to Uniscribe.

 

Ok Ok, so Window System is just a way of Sharing Screen and Inputs, not a way of Rendering, right?

Yes, indeed. And Window System itself is built on top of the Render System. Not the otherway around.

You may say, "You give the example, but don't tell me the reason why we shoud not include render api for a window system."

Here is the reason:

To defined a render api, you limited the future hardware capability support. also you duplicated the work done by OpenGL. And you make a confliction beteween external render api and your render api. make it harder to choose other render pipe line. graphics driver bug will affect every application and the server itself.

 

 

Jul 14

X11 is a so called DISPLAY SERVER.

Wayland it not. Wayland is a protocol between Compositor and the Clients, there is not DISPLAY SERVER.

To be a DISPLAY SERVER, you need to be able to RENDER for clients. 

To be able to RENDER for clients, you need to DEFINE A RENDER API, which is a very bad idea

Wayland does not have a render api. you need to use OpenGL or what ever you like to render contents.

 

Feb 12

Turst me , OSS4 = ALSA + PulseAudio , and all implemented in kernel. very low latency.

OSS4 have kernel level mix feature which ALSA lack for years and that's why PuleAudio fucked to burn.

OSS4 also have per-process volume control which ALSA lack for years and that's why PuleAudio fucked to burn.

OSS4 is not a new API but a new implementation. So existing App won't need any modification. 
But when ALSA burn , every app need to re-write with the poor designed/doc-ed ALSA API.
When nearly lost app support ALSA, PA fuck the world again!!! And every app need to re-write with the PA api.

But when OSS4 came back. NO app need to re-write. OSS4 works as a drop in replacement!
Great!!!

And, OSS4 is pure kernel side. OSS4 don't need assistance from user-land.

So, pleace deprecated ALSA and PA immediately and include OSS4. 
And ALSA foundation rename to OSS-ng foundation and sopport OSS4.