11.1、鼠标显示问题
在windows操作系统中,鼠标都是可以移动到最右边隐藏起来的,这个操作系统现在还不行,整个鼠标都会显示在屏幕上。
是因为这段代码,如果往右移动鼠标,鼠标起始位置里屏幕右边小于鼠标宽度16的时候,就会强制设置为16。
if (mx > binfo->scrnx - 16) {
mx = binfo->scrnx - 16;
}
if (my > binfo->scrny - 16) {
my = binfo->scrny - 16;
}
改成这样就好了:
if (mx > binfo->scrnx - 1) {
mx = binfo->scrnx - 1;
}
if (my > binfo->scrny - 1) {
my = binfo->scrny - 1;
}
但是随之出现了新问题:
会发现鼠标往右移除边框后,隐藏的部分会绘制到下一行,只是因为显存是线性的,320 x 200 是现实方式,但是像素在内存就是线性排布的。
- 解决办法
// sheet_refreshsub函数,左上角坐标小于0,就设置为0;当右下角坐标大于屏幕分辨率就设置为分辨率大小
// 修正刷新范围
if (vx0 < 0) { vx0 = 0; }
if (vy0 < 0) { vy0 = 0; }
if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }
if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }
11.2、小小的调整
作者对管理图层的结构体做了改动,
每个图层都有一个ctl了,以后函数调用的时候不用传递这个值了。当然这只是作者的想法,没有啥其他考量。
只要在shtctl_init初始化ctl的时候,就高速每个图层的ctl值。
struct SHEET {
unsigned char *buf;
int bxsize, bysize, vx0, vy0, col_inv, height, flags;
struct SHTCTL *ctl;
};
11.3、现实窗口
先在缓存中画出来:
void make_window8(unsigned char *buf, int xsize, int ysize, char *title)
{
static char closebtn[14][16] = {
"OOOOOOOOOOOOOOO@",
"OQQQQQQQQQQQQQ$@",
"OQQQQQQQQQQQQQ$@",
"OQQQ@@QQQQ@@QQ$@",
"OQQQQ@@QQ@@QQQ$@",
"OQQQQQ@@@@QQQQ$@",
"OQQQQQQ@@QQQQQ$@",
"OQQQQQ@@@@QQQQ$@",
"OQQQQ@@QQ@@QQQ$@",
"OQQQ@@QQQQ@@QQ$@",
"OQQQQQQQQQQQQQ$@",
"OQQQQQQQQQQQQQ$@",
"O$$$$$$$$$$$$$$@",
"@@@@@@@@@@@@@@@@"
};
int x, y;
char c;
boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, xsize - 1, 0 );
boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, xsize - 2, 1 );
boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, 0, ysize - 1);
boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, 1, ysize - 2);
boxfill8(buf, xsize, COL8_848484, xsize - 2, 1, xsize - 2, ysize - 2);
boxfill8(buf, xsize, COL8_000000, xsize - 1, 0, xsize - 1, ysize - 1);
boxfill8(buf, xsize, COL8_C6C6C6, 2, 2, xsize - 3, ysize - 3);
boxfill8(buf, xsize, COL8_000084, 3, 3, xsize - 4, 20 );
boxfill8(buf, xsize, COL8_848484, 1, ysize - 2, xsize - 2, ysize - 2);
boxfill8(buf, xsize, COL8_000000, 0, ysize - 1, xsize - 1, ysize - 1);
putfonts8_asc(buf, xsize, 24, 4, COL8_FFFFFF, title);
for (y = 0; y < 14; y++) {
for (x = 0; x < 16; x++) {
c = closebtn[y][x];
if (c == '@') {
c = COL8_000000;
} else if (c == '$') {
c = COL8_848484;
} else if (c == 'Q') {
c = COL8_C6C6C6;
} else {
c = COL8_FFFFFF;
}
buf[(5 + y) * xsize + (xsize - 21 + x)] = c;
}
}
return;
}
让它一直计数:
for (;;) {
count++;
sprintf(s, "%010d", count);
boxfill8(buf_win, 160, COL8_C6C6C6, 40, 28, 119, 43);
putfonts8_asc(buf_win, 160, 40, 28, COL8_000000, s);
sheet_refresh(sht_win, 40, 28, 120, 44);
(略)
}
但这还是有问题,这窗口一直在闪络,感觉不舒服。这是因为每次改变窗口的内容,所有图层都会刷新,改变内容不多,刷新的开销太大,导致一直在闪烁。如果能够少刷新一点就好了,只刷新改变的窗口以上图层就好了。
11.4、消除闪烁
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
把sheet_refreshsub改造一下,加一个参数h0,表示从h0图层开始刷新。
0图层:背景
1图层:counter窗口
2图层:鼠标图层
如果counter窗口文字发生改变,没有发生移动从而影响后面的图层,那就刷新它上面的1、2图层,背景就不会刷新,减少了刷新背景的开销。
但是随之会出现问题,鼠标放在上面,也会一直闪动,这是因为即使鼠标不动,刷新文字时,也会把上面的鼠标图层给刷新了,鼠标就一直被刷新重写,所以出现闪动。
11.5、消除闪烁2.0
- 改进
如果只是窗口发生内部自己发生变化,只刷新它自己的图层内容,覆盖它上面的图层东西不刷新就好了。 - 方法
改进SHTCTL结构体,增加一个map。
map的内容和显存里一样,区别是,显存写的是色号,map里写的是图层的序号——sid。
struct SHTCTL {
unsigned char *vram, *map;
int xsize, ysize, top;
struct SHEET *sheets[MAX_SHEETS];
struct SHEET sheets0[MAX_SHEETS];
};
sheet_refreshmap 和 sheet_refreshsub函数
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];
sid = sht - ctl->sheets0; /* 计算该图层的sid */
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) {
map[vy * ctl->xsize + vx] = sid;
}
}
}
}
return;
}
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1)
{
int h, bx, by, vx, vy, bx0, by0, bx1, by1;
unsigned char *buf, *vram = ctl->vram, *map = ctl->map, sid;
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 <= h1; h++) {
sht = ctl->sheets[h];
buf = sht->buf;
sid = sht - ctl->sheets0;
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 (map[vy * ctl->xsize + vx] == sid) {// 按照map里的当前图层的sid指导刷新
vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
}
}
}
}
return;
}
map的结构,画个图理解一下:
0是背景
1是新画的窗口
2是鼠标
可以发现,鼠标图层遮挡了一部分1图层的文字,1图层文字一直在改变,当因为文字改变导致刷新时,sheet_refreshsub中的 if 判断这个2图层不等于1图层sid,所以鼠标位置不会被反复重写,就避免了一直闪动的问题。
11.5.1、再说一下窗口移动问题:
void sheet_slide(struct SHEET *sht, int vx0, int vy0)
{
struct SHTCTL *ctl = sht->ctl;
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);
}
return;
}
跟之前一样,窗口移动:
sheet_refreshmap:
1、先把移动前位置的,所有图层的sid写入到map里(假设鼠标在背景上移动,移动前的位置会被填上0)
2、再把移动后位置的,当前图层的sid写入到map里(假设鼠标在背景上移动,不管移动到哪,移动后的位置范围会被填2)
sheet_refreshsub:依据map
3、刷新移动前的位置。(这是真正的写入显存)
4、刷新移动后的位置。
11.5.2、再说一个图层高度变化
void sheet_updown(struct SHEET *sht, int height)
{
struct SHTCTL *ctl = sht->ctl;
int h, old = sht->height;
if (height > ctl->top + 1) {
height = ctl->top + 1;
}
if (height < -1) {
height = -1;
}
sht->height = height;
if (old > height) {
if (height >= 0) {
for (h = old; h > height; h--) {
ctl->sheets[h] = ctl->sheets[h - 1];
ctl->sheets[h]->height = h;
}
ctl->sheets[height] = sht;
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 {
if (ctl->top > old) {
for (h = old; h < ctl->top; h++) {
ctl->sheets[h] = ctl->sheets[h + 1];
ctl->sheets[h]->height = h;
}
}
ctl->top--;
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 if (old < height) {
if (old >= 0) {
for (h = old; h < height; h++) {
ctl->sheets[h] = ctl->sheets[h + 1];
ctl->sheets[h]->height = h;
}
ctl->sheets[height] = sht;
} else {
for (h = ctl->top; h >= height; h--) {
ctl->sheets[h + 1] = ctl->sheets[h];
ctl->sheets[h + 1]->height = h + 1;
}
ctl->sheets[height] = sht;
ctl->top++;
}
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);
}
return;
}
高度变化,有两种情况
高度变低
变低之后还显示:需要把变化后的位置以上的图层在map中更新;把变低前位置的图层和变低后图层的上一个图层刷新到显存(为什么这么做:因为这种变化发生在变化前和与变化后上一个图层之间的关系,如果变化前还有图层,那也不影响它们。还有为什么是和变化后的上一个图层之间的关系,因为图层变低了,必然不会显示在上面,意思就是它不主要了,由它上面的图层来刷新就好了)。
变低之后不显示:在map中,把全部图层都要进行刷新;显存中从0图刷新到变化之前的下一个图层(为什么这么做:因为隐藏的图层可能之前遮挡了很多图层,甚至所有图层,所以需要全部刷新到map中;显存中刷新0图层到变化前图层的下一个,是因为变化之前位置的下一个,也就是它下面的图层之前收到它的遮挡,要进行刷新显示出来,如果之前在最上面的图层,那么就刷新所有图层了)。
高度变高
从隐藏到显示
从显示状态,由低到高:map和显存都只需要刷新变高之后的图层就好了(为什么:因为变高之后,比它低的图层就不用管了,)
总的来说:就是变高之后就刷新变高之后图层,变低之后需要照顾后面的图层,前面图层会挡住后面的图层,后面的图层要显示出来就要刷新。
以上是我的理解。