《30天自制操作系统》笔记(09)——绘制窗口

《30天自制操作系统》笔记(09)——绘制窗口

进度回顾

上一篇中介绍了图层式窗口管理的思路和算法。在此基础上,本篇就解决绘制窗口及其简单的优化问题。

这里稍微吐槽一下《30天自制操作系统》原作者。全书我刚刚看了三分之一,写得确实不错,但是我能感受到原作者是习惯用汇编语言和汇编思维来写程序的。虽然书里尽量使用了C语言,但给我一种用C写汇编的感觉。也可能是原作者故意简化了OS开发过程,方便初学者理解吧。幸好这在我预料之中,一开始我就打算先看完这本书然后自己再从零设计OS的。

看似立体的窗口其实是画出来的

从第一次见到电脑屏幕上的窗口开始,我就想知道它是怎么做到的。一直在怀疑,现在终于确信了看似立体的窗口、按钮什么的,全是画出来的。


 1 void make_window8(unsigned char *buf, int xsize, int ysize, char *title)
2 {
3 static char closebtn[14][16] = {
4 "[email protected]",
5 "[email protected]",
6 "[email protected]",
7 "[email protected]@[email protected]@[email protected]",
8 "[email protected]@[email protected]@[email protected]",
9 "[email protected]@@@[email protected]",
10 "[email protected]@[email protected]",
11 "[email protected]@@@[email protected]",
12 "[email protected]@[email protected]@[email protected]",
13 "[email protected]@[email protected]@[email protected]",
14 "[email protected]",
15 "[email protected]",
16 "[email protected]",
17 "@@@@@@@@@@@@@@@@"
18 };
19 int x, y;
20 char c;
21 boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, xsize - 1, 0 );
22 boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, xsize - 2, 1 );
23 boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, 0, ysize - 1);
24 boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, 1, ysize - 2);
25 boxfill8(buf, xsize, COL8_848484, xsize - 2, 1, xsize - 2, ysize - 2);
26 boxfill8(buf, xsize, COL8_000000, xsize - 1, 0, xsize - 1, ysize - 1);
27 boxfill8(buf, xsize, COL8_C6C6C6, 2, 2, xsize - 3, ysize - 3);
28 boxfill8(buf, xsize, COL8_000084, 3, 3, xsize - 4, 20 );
29 boxfill8(buf, xsize, COL8_848484, 1, ysize - 2, xsize - 2, ysize - 2);
30 boxfill8(buf, xsize, COL8_000000, 0, ysize - 1, xsize - 1, ysize - 1);
31 putfonts8_asc(buf, xsize, 24, 4, COL8_FFFFFF, title);
32 for (y = 0; y < 14; y++) {
33 for (x = 0; x < 16; x++) {
34 c = closebtn[y][x];
35 if (c == ‘@‘) {
36 c = COL8_000000;
37 } else if (c == ‘$‘) {
38 c = COL8_848484;
39 } else if (c == ‘Q‘) {
40 c = COL8_C6C6C6;
41 } else {
42 c = COL8_FFFFFF;
43 }
44 buf[(5 + y) * xsize + (xsize - 21 + x)] = c;
45 }
46 }
47 return;
48 }

这个绘制"关闭"按钮的代码和绘制鼠标的代码多么相似!其实都是画出来的立体效果而已。

当然,这么静态地在主函数里绘制一次还不够,还要给窗口添加一个图层,并注册到OS的sheetctrl链表中。这样,当窗口发生激活、移动时,sheetctrl就会刷新这个窗口了。

优化1-减少需要刷新的图层

把"每次刷新都遍历所有图层"改进为"仅对内容发生变化的图层及其以上的图层进行刷新"。


1 void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
2 {
3 /* 略 */
4 for (h = h0; h <= ctl->top; h++) {
5 /* 略 */
6 }
7 return;
8 }

调用sheet_refreshsub()函数的位置都要相应修改。

 1 void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1)
2 {
3 if (sht->height >= 0) { /* 如果正在显示,则按新图层的信息进行刷新 */
4 sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1, sht->height);
5 }
6 return;
7 }
8
9 void sheet_slide(struct SHEET *sht, int vx0, int vy0)
10 {
11 int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
12 sht->vx0 = vx0;
13 sht->vy0 = vy0;
14 if (sht->height >= 0) { /* 如果正在显示,则按新图层的信息进行刷新 */
15 sheet_refreshsub(sht->ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);
16 sheet_refreshsub(sht->ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height);
17 }
18 return;
19 }
20
21 void sheet_updown(struct SHEET *sht, int height)
22 {
23 /* 略 */
24
25 /* 以下主要是对sheets[]的重新排列 */
26 if (old > height) { /* 比以前低 */
27 if (height >= 0) {
28 /* 中间的提起 */
29 for (h = old; h > height; h--) {
30 ctl->sheets[h] = ctl->sheets[h - 1];
31 ctl->sheets[h]->height = h;
32 }
33 ctl->sheets[height] = sht;
34 sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height + 1);
35 } else { /* 隐藏 */
36 if (ctl->top > old) {
37 /* 把上面的降下来 */
38 for (h = old; h < ctl->top; h++) {
39 ctl->sheets[h] = ctl->sheets[h + 1];
40 ctl->sheets[h]->height = h;
41 }
42 }
43 ctl->top--; /* 正在显示的图层减少了一个,故最上面的高度也减少 */
44 sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, 0);
45 }
46 } else if (old < height) { /* 比以前高 */
47 if (old >= 0) {
48 /* 中间的图层往下降一层 */
49 for (h = old; h < height; h++) {
50 ctl->sheets[h] = ctl->sheets[h + 1];
51 ctl->sheets[h]->height = h;
52 }
53 ctl->sheets[height] = sht;
54 } else { /*从隐藏状态变为显示状态 */
55 /* 把在上面的图层往上提高一层 */
56 for (h = ctl->top; h >= height; h--) {
57 ctl->sheets[h + 1] = ctl->sheets[h];
58 ctl->sheets[h + 1]->height = h + 1;
59 }
60 ctl->sheets[height] = sht;
61 ctl->top++; /* 显示中的图层增加了一个,故最上面的高度也增加 */
62 }
63 sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height);
64 }
65 return;
66 }

修改相应的位置

在sheet_slide函数里,图层的移动有时会导致下面的图层露出,所以要从最下面开始刷新。另一方面,在移动目标处,比新移来的图层位置还要低的图层没有什么变化,而且只是隐藏起来了,所以只要刷新移动的图层和它上面的图层就可以了。

优化2-借助图层map

这是个空间换时间的优化方式。

首先,开辟一块内存,大小和VRAM一样,我们称之为map。这个map用来表示画面上的点是哪个图层的像素,所以它相当于图层的地图。

文字不好表达清楚,看图则一目了然。

当刷新图层1的时候,如果对照这个map刷新,就不会覆盖被其他图层挡住的部分了。

原理就是这样,并不高深。代码也很简单,而且不必要看了。

效果如下。

总结

本篇终于能够画出类似windows应用程序窗口那样的窗口。原来那么立体的窗口真是一点一点画出来的。解决了10多年的疑问,真舒服。

原作者对绘制窗口这个问题进行了多次优化,效果很好。不知道windows的窗口绘制是如何优化的,不知道《30天自制操作系统》后续章节还有没有继续优化?革命尚未成功,同志仍需努力啊。

时间: 2024-11-13 23:07:39

《30天自制操作系统》笔记(09)——绘制窗口的相关文章

30天自制操作系统笔记(第四天)

这一节讲的最出彩的地方是c语言的地址. 而要理清c语言地址,又必须追根溯源,看看汇编里内存地址的使用. MOV AL,0X15 MOV [1024],AL MOV BYTE[1024],0X15 这两种指令效果相同,都是在这个内存地址里存入一个数据,而学过汇编的我们知道,直接往内存某地址存入数据时,要说明填入的数据大小,或者说数据类型,不然机器不知道怎么填入该数据,到底是按照8位填入,还是十六位填入.因此,这个byte必不可少,而前面的指令,由于AL已经明确了是八位,因此不用说明. 好了,接下来

《30天自制操作系统》笔记(08)——叠加窗口刷新

<30天自制操作系统>笔记(08)--叠加窗口刷新 进度回顾 上一篇中介绍了内存管理的思路和算法,我们已经可以动态申请和释放内存了.这不就是堆(Heap)么.在此基础上,本篇要做一段程序,一并解决窗口和鼠标的叠加处理问题. 问题 在之前的<<30天自制操作系统>笔记(05)--启用鼠标键盘>篇,已经能够移动鼠标了.但是遗留了如下图所示的一个小问题. 我们希望的情形是这样的: 实际上,当前版本的OS还没有窗口图层的东西.本篇要做一段程序,一并解决窗口和鼠标的叠加处理问题.

《30天自制操作系统》读书笔记(4) 绘图

暑假果然是滋生懒散的温床. (╯‵□′)╯︵┻━┻ 好久不动都忘记之前做到哪里了, 上次好像做到了C语言的引入, 这一节所做的东西都相当轻松, 将会绘制出操作系统的基本界面. 绘图的原理 按照书中所说, 将值写入到显存中就能在屏幕上显示相应的像素, 在asmhead.nas 中有这一段: 1 CYLS EQU 0x0ff0 ; 设定启动区 2 LEDS EQU 0x0ff1 3 VMODE EQU 0x0ff2 ; 关于颜色数目的信息,颜色的位数 4 SCRNX EQU 0x0ff4 ; 分辨率

《30天自制操作系统》笔记(01)——hello bitzhuwei’s OS!

<30天自制操作系统>笔记(01)--hello bitzhuwei's OS! 最初的OS代码 1 ; hello-os 2 ; TAB=4 3 4 ORG 0x7c00 ; 指明程序的装载地址 5 6 ; 以下这段是标准FAT32格式软盘专用的代码 7 8 JMP entry 9 DB 0x90 10 DB "HELLOIPL" ; freeparam 启动区的名称可以是任意的字符串(8字节) 11 DW 512 ; 每个扇区(sector)的大小(必须为512字节)

《30天自制操作系统》笔记(04)——显示器256色

<30天自制操作系统>笔记(04)--显示器256色 进度回顾 从最开始的(01)篇到上一篇为止,已经解决了开发环境问题和OS项目的顶层设计问题. 本篇做一个小练习:设置显卡显示256色. 原理 设置显卡模式 调用BIOS中断命令INT 0x10,设置显卡模式为VGA图形模式,320*200*8位彩色模式,调色板模式.代码如下. 1 MOV AL,0x13 ; VGA图形模式,320*200*8位彩色模式 2 MOV AH,0x00 3 INT 0x10 设置调色板 256色的调色板是这样一个

《30天自制操作系统》读书笔记(5) GDT&amp;IDT

梳理项目结构 项目做到现在, 前头的好多东西都忘了, 还是通过Makefile重新理解一下整个项目是如何编译的: 现在我们拥有这么9个文件: ipl10.nas    InitialProgramLoader, 占用了软盘的第一个扇区并符合启动盘的规范, 默认被载入地址是0x7c00 到 0x7e00, 负责将10个柱面读入到0x8200到0x34fff (10个柱面共10*2*18 = 360 个扇区但是第一个没有被读入); asmhead.nas     包含一些暂时未知的设定; naskf

《30天自制操作系统》读书笔记(2)hello, world

让系统跑起来 要写一个操作系统,我们首先要有一个储存系统的介质,原版书似乎是06年出版的,可惜那时候没有电脑,没想到作者用的还是软盘,现在的电脑谁有软驱?不得已我使用一张128M的SD卡来代替,而事实上你用的是U盘还是软盘对我们的操作系统没有影响,缺点是你的U盘刷入系统后容量只能是1440 MB,即当年流行的3.5英寸软盘的大小,当然不用担心,再格式化一次(用DiskGeniu),就可以恢复. 我做事情的话,总是怕自己的努力的结果白费了,害怕辛辛苦苦看完这本书但是发现做出来的东西现在根本没法用,

多定时器处理1(30天自制操作系统--读书笔记)

自认为写过很多MCU程序,但总是回头想想,我所了解的MCU编程思想大体有两种,其中具体的想法我得再找时间写下来. 总想总结出一个可扩展的,易移植的写法,但能力还没到这个层次.但<30天自制操作系统>这本书确实给我了一个思路,就像我已经写过的两篇读书笔记. 将两个独立的内容--FIFO和内存动态管理做到高度模块化,尤其是其中数据结构模型的设计更是我学习的好例子. 今天要学习的设计内容是多定时器处理.原书对这部分的处理讲的很详细,由浅入深,看得我由衷佩服作者,也可能是因为我水平低,稍稍看出点门道来

内存管理(30天自制操作系统--读书笔记)

今天继续读书笔记,“挑战内存管理”(30天自制操作系统). 为什么对这块内容敢兴趣呢,因为曾经遇到这么一个问题.在STM32程序中想使用队列,可不是上篇讲的FIFO,而是使用了较大的内存空间,又想做队列的顺序存取管理. 在这个队列里用到了malloc,动态申请内存,一开始是直接申请不到内存,后来在启动脚本里更改了设置堆的地址值,可以申请成功,但发现申请几次后,也申请不到内存. 果然MCU级别的程序,内存这块处理起来就没有windows程序那么随心所欲了.讲了这么多,开始正题吧. 1.相关数据结构