请参看视频,已获得更详细的讲解和展示
Linux kernel Hacker, 从零构建自己的内核
有了图层基础,我们可以给操作系统提供多窗口功能,多窗口能显示出系统处理能力的强大,但也会引入新的问题。就以前几节我们创造的Message box为例,我们修改一下代码,不断的修改Box窗体内的字符,进而导致图层不断刷新,我们看看会有什么后果。改动的代码如下,在write_vga_desktop.c里做如下更改:
void CMain(void) {
....
for(;;) {
char* pStr = intToHexStr(counter);
counter++;
boxfill8(shtMsgBox->buf, 160, COL8_C6C6C6, 40, 28, 119, 43);
showString(shtctl, shtMsgBox, 40, 28, COL8_000000,pStr);
io_cli();
if (fifo8_status(&keyinfo) + fifo8_status(&mouseinfo) == 0) {
io_sti();
}
....
}
在系统入口函数的主循环中,我们增加一个计数,让该计数不断变化,然后把变化后的数值转换为字符串再写到Box的主窗体里,由于写字符串时,需要更新窗体图层,因此上面的代码会引发图层频繁的更新,这就导致一个非常严重的闪烁现象(在博客里无法截图展示,请读者观看视频)。这种情况不处理,会导致系统的用户体验极差,这样,我们辛苦做出来的系统就没人用了,这显然不是我们期望看到的,因此解决这个问题,迫在眉睫。
这个现象的产生,是因为我们在刷新Box窗体时,我们也同时在刷新底层桌面,事实上这毫无必要,假设一个窗体,它的高度是10,那么它刷新时,高度为0到9的窗体根本不需要跟着刷新,如果高度低的窗体跟着刷新,不但产生闪烁的效果,而且是毫无必要的浪费CPU资源,接下来我们的改进就是,当窗体刷新时,只刷新同一层高度或高度更高的图层,由此,代码改动如下, 在win_sheet.c中:
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0) {
....
for (h = h0; h <= ctl->top; h++) {
.....
}
.....
}
sheet_refreshsub 是用来刷新图层的,这次改动就增加了一个参数h0, 这个h0就表示当前要刷新的图层的高度,在for 循环不再从0开始,而是从h0开始,也就是从当前图层的高度往上进行刷新。
由于该函数更改了,其他调用它的函数也要做相应的修改,仍然在win_sheet.c中:
int sheet_refresh(struct SHTCTL *ctl, struct SHEET *sht, int bx0, int by0, int bx1, int by1) {
if (sht->height >= 0) {
//多添加一个图层高度的参数
sheet_refreshsub(ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1,
sht->vy0 + by1, sht->height);
}
return 0;
}
void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0) {
int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
sht->vx0 = vx0;
sht->vy0 = vy0;
if (sht->height >= 0) {
sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);
sheet_refreshsub(ctl, vx0, vy0, vx0+sht->bxsize, vy0+sht->bysize, sht->height);
}
}
void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0) {
int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
sht->vx0 = vx0;
sht->vy0 = vy0;
if (sht->height >= 0) {
//多添加一个图层高度的参数
sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);
sheet_refreshsub(ctl, vx0, vy0, vx0+sht->bxsize, vy0+sht->bysize, sht->height);
}
}
void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height) {
....
if (old > height) {
....
if (height >= 0) {
.....
//多添加一个图层高度的参数
sheet_refreshsub(ctl, sht->vx0, sht->vy0,
sht->vx0+sht->bxsize, sht->vy0+sht->bysize, height+1);
}
} else {
....
//多添加一个图层高度的参数
sheet_refreshsub(ctl, sht->vx0, sht->vy0,
sht->vx0+sht->bxsize, sht->vy0+sht->bysize,0);
}
else {
....
//多添加一个图层高度的参数
sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0+sht->bxsize, sht->vy0+sht->bysize,
height);
}
}
上面代码的改动不多,唯一需要改动的就是在调用sheet_refreshsub时,多添加一个参数,把当前更新图层所在的高度传进去。
有了上面代码的更新后,图层的闪烁现象就消除了。但如果我们把鼠标挪到 Message Box 的上头,我们会发现,鼠标自己出现了闪烁的现象,这是因为鼠标图层比Message Box 高,当鼠标与Box重叠时,Box自己的刷新会导致鼠标的刷新,要更改这个问题,需要做新的刷新算法改进,欲知后事,请看下回分解。