消除图层刷新而导致的严重闪烁-1

请参看视频,已获得更详细的讲解和展示
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自己的刷新会导致鼠标的刷新,要更改这个问题,需要做新的刷新算法改进,欲知后事,请看下回分解。

    原文作者:望月从良
    原文地址: https://www.jianshu.com/p/83f7408d9184
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞