請參看視頻,已獲得更詳細(xì)的講解和展示
Linux kernel Hacker, 從零構(gòu)建自己的內(nèi)核
上一節(jié)院尔,我們消除了因刷新而導(dǎo)致的嚴(yán)重閃爍旧烧,但問題并沒有從根子上解決茁肠,因?yàn)楫?dāng)我們把鼠標(biāo)挪動(dòng)不斷刷新自己的Message Box上面時(shí)泽腮,發(fā)現(xiàn)鼠標(biāo)居然變得閃動(dòng)起來御蒲,這是因?yàn)椋?dāng)窗體自身刷新時(shí)诊赊,它會把處于它上方的窗體也進(jìn)行刷新厚满,而這種操作其實(shí)是沒有必要的。我們看下面這種情況:
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
所以數(shù)字1代表的是標(biāo)號為1的窗口所對應(yīng)的像素點(diǎn)碧磅,數(shù)字2所代表的是編號為2的窗口所對應(yīng)的像素點(diǎn)碘箍,顯然,窗口1的右下邊部分被窗口2所覆蓋鲸郊,并且窗口2比窗口1的高度還要高丰榴,當(dāng)窗口1的像素進(jìn)行刷新時(shí),如果窗口2的內(nèi)容沒有變化的話严望,那么窗口2 完全不需要進(jìn)行刷新多艇,這就要求窗口1只能更新那些沒有被窗口2覆蓋的點(diǎn),也就是說像吻,窗口1只能更新一下部分像素點(diǎn):
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
也就是右下角那部分,他是不能去更新的复隆,這就要求拨匆,當(dāng)我們?nèi)ニ⑿嘛@存時(shí),必須知道哪一個(gè)像素點(diǎn)屬于窗口1挽拂,哪一個(gè)像素點(diǎn)屬于窗口2惭每,刷新時(shí),屬于窗口1的像素點(diǎn)亏栈,我就更新台腥,屬于窗口2的像素點(diǎn),我就忽略绒北,這樣才能解決上節(jié)看到的鼠標(biāo)閃爍的問題黎侈。這樣我們就需要在圖層的數(shù)據(jù)結(jié)構(gòu)中引入新的變量,用來記錄該圖層像素點(diǎn)所對應(yīng)的窗體編號闷游,代碼如下峻汉,win_sheet.h:
struct SHTCTL {
unsigned char *vram, *map;
int xsize, ysize, top;
struct SHEET *sheets[MAX_SHEETS];
struct SHEET sheets0[MAX_SHEETS];
};
上面的結(jié)構(gòu)體定義中贴汪,多增加了一個(gè)變量map, 它的作用就是用來記錄圖層每一個(gè)像素點(diǎn)所對應(yīng)的窗體編號的。在win_sheet.c中做如下更改:
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram,
int xsize, int ysize) {
....
//為圖層像素編號數(shù)值分配內(nèi)存
ctl->map = (unsigned char*)memman_alloc_4k(memman, xsize * ysize);
if (ctl->map == 0) {
memman_free_4k(memman, (int)ctl, SIZE_OF_SHTCTL);
return 0;
}
.....
}
在初始化圖層信息時(shí)休吠,為map變量分配內(nèi)存扳埂,以便用來記錄圖層像素點(diǎn)的窗口編號。如何確定每個(gè)窗體圖層所對應(yīng)的編號呢瘤礁,很簡單阳懂,所有的圖層都是從圖層控制器SHTCTL 的圖層數(shù)組sheets0中分配的,我們把圖層對象在這個(gè)數(shù)值中的下標(biāo)作為它的窗體編號柜思,我們在win_sheet.c里面添加一個(gè)函數(shù)叫refresh_map希太,用來記錄當(dāng)前需要刷新的窗口的像素所對應(yīng)的編號。
void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0) {
int h, bx, by, vx, vy, bx0, by0, bx1, by1;
unsigned char *buf, sid, *map = ctl->map;
struct SHEET *sht;
if (vx0 < 0) {vx0 = 0;}
if (vy0 < 0) {vy0 = 0;}
if (vx1 > ctl->xsize) {vx1 = ctl->xsize;}
if (vy1 > ctl->ysize) {vy1 = ctl->ysize;}
for (h = h0; h <= ctl->top; h++) {
sht = ctl->sheets[h];
//獲取圖層編號酝蜒,也就是圖層對象在圖層數(shù)組中的下標(biāo)
sid = sht - ctl->sheets0;
buf = sht->buf;
bx0 = vx0 - sht->vx0;
by0 = vy0 - sht->vy0;
bx1 = vx1 - sht->vx0;
by1 = vy1 - sht->vy0;
if (bx0 < 0) { bx0 = 0;}
if (by0 < 0) { by0 = 0;}
if (bx1 > sht->bxsize) {bx1 = sht->bxsize;}
if (by1 > sht->bysize) {by1 = sht->bysize;}
for (by = by0; by < by1; by++) {
vy = sht->vy0 + by;
for (bx = bx0; bx < bx1; bx++) {
vx = sht->vx0 + bx;
if (buf[by * sht->bxsize + bx] != sht->col_inv) {
//將圖層標(biāo)號設(shè)置到map變量里
map[vy * ctl->xsize + vx] = sid;
}
}
}
}
return;
}
這個(gè)函數(shù)跟以前的刷新函數(shù)refresh_sub幾乎一模一樣誊辉,是用來設(shè)置像素對應(yīng)的圖層編號的,假如窗口1的某個(gè)像素位于坐標(biāo)(20亡脑, 30)堕澄,那么我就在map[30xsize + 20] 處設(shè)置為1, xsize 是桌面的寬度霉咨,如果窗口2移動(dòng)后蛙紫,有像素點(diǎn)也移動(dòng)到了坐標(biāo)(20,30), 那么上面的代碼就會把map[30xsize + 20] 處設(shè)置為2,也就是窗口1的像素被窗口2所覆蓋了途戒。
有了像素所在位置對應(yīng)的窗口號后坑傅,我們就可以只刷新對應(yīng)窗口的像素點(diǎn),于是我們對窗口刷新函數(shù)做下列修改:
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1) {
int h, bx, by, vx, vy;
unsigned char *buf, c, *vram = ctl->vram, *map = ctl->map, sid;
struct SHEET *sht;
if (vx0 < 0) {vx0 = 0;}
if (vy0 < 9) {vy0 = 0;}
if (vx1 > ctl->xsize) {vx1 = ctl->xsize;}
if (vy1 > ctl->ysize) {vy1 = ctl->ysize;}
for (h = h0; h <= h1; h++) {
sht = ctl->sheets[h];
buf = sht->buf;
//獲得當(dāng)前圖層對應(yīng)的窗口號
sid = sht - ctl->sheets0;
for (by = 0; by < sht->bysize; by++) {
vy = sht->vy0 + by;
for (bx = 0; bx < sht->bxsize; bx++) {
vx = sht->vx0 + bx;
if (vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1) {
c = buf[by * sht->bxsize + bx];
//確定當(dāng)前坐標(biāo)對應(yīng)的素點(diǎn)是否屬于要刷新的窗口
if (map[vy * ctl->xsize + vx] == sid && c != sht->col_inv) {
vram[vy * ctl->xsize + vx] = c;
}
}
}
}
}
}
refresh_sub在刷新時(shí)喷斋,會遍歷給定高度(h0)以上的所有圖層唁毒,如果某個(gè)高度比當(dāng)前要刷新的窗口高度高,并且它覆蓋了當(dāng)前窗體的某部分星爪,那么兩個(gè)窗體的對應(yīng)像素就會重合浆西,高度高的窗體會把對應(yīng)坐標(biāo)處的像素標(biāo)號設(shè)置成自己的窗體標(biāo)號,也就是在上面的循環(huán)中顽腾,map[vy * ctl->xsize + vx] 所對應(yīng)的窗體標(biāo)號與當(dāng)前窗體的標(biāo)號就會不一樣近零,于是vram[vy * ctl->xsize + vx] = c;這一句就不會執(zhí)行,于是與當(dāng)前刷新窗體重合的抄肖,但高度更高的窗體就不用做沒必要的刷新久信。
其他對應(yīng)的圖層刷新函數(shù)也做相應(yīng)改動(dòng):
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_refreshmap(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);
sheet_refreshmap(ctl, vx0, vy0, vx0+sht->bxsize, vy0+sht->bysize, sht->height);
sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize,
old_vy0 + sht->bysize, 0, sht->height - 1);
sheet_refreshsub(ctl, vx0, vy0, vx0+sht->bxsize, vy0+sht->bysize, sht->height,
sht->height);
}
}
void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height) {
....
if (old > height) {
....
if (height >= 0) {
....
//窗體高度更新后,要更改像素點(diǎn)所在位置的窗體標(biāo)號
sheet_refreshmap(ctl, sht->vx0, sht->vy0, sht->vx0+sht->bxsize, sht->vy0+sht->bysize,
height+1);
sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0+sht->bxsize, sht->vy0+sht->bysize,
height+1, old);
} else {
....
//窗體高度更新后漓摩,要更改像素點(diǎn)所在位置的窗體標(biāo)號
sheet_refreshmap(ctl, sht->vx0, sht->vy0, sht->vx0+sht->bxsize, sht->vy0+sht->bysize,
0);
sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0+sht->bxsize, sht->vy0+sht->bysize,
0, old - 1);
}
} else {
....
//窗體高度更新后裙士,要更改像素點(diǎn)所在位置的窗體標(biāo)號
sheet_refreshmap(ctl, sht->vx0, sht->vy0, sht->vx0+sht->bxsize, sht->vy0+sht->bysize,
height);
sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0+sht->bxsize, sht->vy0+sht->bysize,
height, height);
}
}
做完上面的修改后,鼠標(biāo)閃爍的問題就可以消失了幌甘。