第11天 制作窗口
2020.4.10
1. 鼠标显示问题(harib08a)
-
harib07d中鼠标移动到屏幕最右边:
- 鼠标应该向右或向下移动到屏幕之外隐藏起来。
-
修改HariMain函数中的代码
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; }
-
在harib08a中
make run
:
- 只要鼠标一跑到画面外就会产生下图红框中的问题。
2. 实现画面外的支持(harib08b)
-
显然,只有sheet_refreshsub函数执行把图层内容写入VRAM的工作。
-
修改sheet_refreshsub函数:
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1) { int h, bx, by, vx, vy, bx0, by0, bx1, by1; unsigned char *buf, c, *vram = ctl->vram; 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 = 0; h <= ctl->top; h++) { …… } return; }
-
在harib08b中
make run
:
- 问题解决。
3. shtctl的指定省略(harib08c)
-
重新定义结构体SHEET:
struct SHEET { unsigned char *buf; int bxsize, bysize, vx0, vy0, col_inv, height, flags; struct SHTCTL *ctl; };
- 加入了
struct SHTCTL *ctl;
这个ctl代表此图层属于哪个图层管理器。
- 加入了
-
shtctl_init函数也需要修改:
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize) { struct SHTCTL *ctl; int i; ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof (struct SHTCTL)); /*为图层管理器分配内存。*/ if (ctl == 0) { goto err; } ctl->vram = vram; ctl->xsize = xsize; ctl->ysize = ysize; ctl->top = -1; /* 没有图层 */ for (i = 0; i < MAX_SHEETS; i++) { ctl->sheets0[i].flags = 0; /* 标记图层未使用 */ ctl->sheets0[i].ctl = ctl; /* 记录图层所属于的图层管理器 */ } err: return ctl; }
-
sheet_updown也需要修改:
void sheet_updown(struct SHEET *sht, int height) { struct SHTCTL *ctl = sht->ctl; /*获取此图层的图层管理器*/ int h, old = sht->height; …… }
-
同理,修改sheet_refresh、sheet_slide和sheet_free函数,让他们都不指定ctl(即省略参数ctl):
void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1) { if (sht->height >= 0) { sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1); } return; } void sheet_slide(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(sht->ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize); sheet_refreshsub(sht->ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize); } return; } void sheet_free(struct SHEET *sht) { if (sht->height >= 0) { sheet_updown(sht, -1); } sht->flags = 0; return; }
-
这样修改以后,bootpack.h中的函数声明也就由
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize); struct SHEET *sheet_alloc(struct SHTCTL *ctl); void sheet_setbuf(struct SHEET *sht, unsigned char *buf, int xsize, int ysize, int col_inv); void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height); void sheet_refresh(struct SHTCTL *ctl, struct SHEET *sht, int bx0, int by0, int bx1, int by1); void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0); void sheet_free(struct SHTCTL *ctl, struct SHEET *sht);
变成了
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize); struct SHEET *sheet_alloc(struct SHTCTL *ctl); void sheet_setbuf(struct SHEET *sht, unsigned char *buf, int xsize, int ysize, int col_inv); void sheet_updown(struct SHEET *sht, int height); void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1); void sheet_slide(struct SHEET *sht, int vx0, int vy0); void sheet_free(struct SHEET *sht);
-
继续修改HariMain:
- 上述声明发生变化的有:
sheet_updown sheet_refresh sheet_slide sheet_free
- 将HariMain函数中调用这些函数的地方修改:
- 共9处
- 上述声明发生变化的有:
-
make run
一切正常
4. 显示窗口(harib08d)
-
制作窗口:和制作鼠标类似,先准备一张图层,然后在图层缓冲区绘制一个类似窗口的图片即可。
-
制作窗口的函数make_window8:
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; }
- 只是对graph.c中的init_screen8函数进行改造。
- buf是缓冲区地址,xsize和ysize代表窗口图层的大小,title是窗口的标题。
- 其中关闭按钮‘x’也是修改init_mouse_cursor8获得的。
-
修改HariMain:
void HariMain(void) { …… struct SHEET *sht_back, *sht_mouse, *sht_win; /*图层sht_win*/ unsigned char *buf_back, buf_mouse[256], *buf_win; /*buf_win*/ …… init_palette(); shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny); sht_back = sheet_alloc(shtctl); sht_mouse = sheet_alloc(shtctl); sht_win = sheet_alloc(shtctl); /*在图层管理器中为窗口申请图层*/ buf_back = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny); buf_win = (unsigned char *) memman_alloc_4k(memman, 160 * 68); /*为窗口图层分配内存空间,12KB*/ sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1); sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99); sheet_setbuf(sht_win, buf_win, 160, 68, -1); /* 窗口没有透明色 */ init_screen8(buf_back, binfo->scrnx, binfo->scrny); init_mouse_cursor8(buf_mouse, 99); make_window8(buf_win, 160, 68, "window"); /*制作窗口*/ /*向窗口中写入字符串*/ putfonts8_asc(buf_win, 160, 24, 28, COL8_000000, "Hi,"); putfonts8_asc(buf_win, 160, 24, 44, COL8_000000, "Stranger!"); sheet_slide(sht_back, 0, 0); mx = (binfo->scrnx - 16) / 2; my = (binfo->scrny - 28 - 16) / 2; sheet_slide(sht_mouse, mx, my); sheet_slide(sht_win, 80, 72); /*窗口在屏幕上的位置*/ sheet_updown(sht_back, 0); sheet_updown(sht_win, 1); /*窗口图层的高度*/ sheet_updown(sht_mouse, 2); sprintf(s, "(%3d, %3d)", mx, my); putfonts8_asc(buf_back, binfo->scrnx, 0, 0, COL8_FFFFFF, s); sprintf(s, "memory %dMB free : %dKB", memtotal / (1024 * 1024), memman_total(memman) / 1024); putfonts8_asc(buf_back, binfo->scrnx, 0, 32, COL8_FFFFFF, s); sheet_refresh(sht_back, 0, 0, binfo->scrnx, 48); …… }
-
make run
:
还不错哦。(29228-12=29216KB)
5. 小实验(harib08e)
-
修改窗口图层、背景图层和鼠标图层的高度:
sheet_updown(sht_back, 0); sheet_updown(sht_mouse, 1); sheet_updown(sht_win, 2);
-
make run
:
鼠标在窗口的下面,这说明sheet.c运行正常。
6. 高速计数器(harib08f)
-
做完实验,将鼠标高度改回去。
-
现在,做一个计数器,计数结果在窗口图层上显示。
-
修改HariMain:
void HariMain(void) { …… unsigned int memtotal, count = 0; /*计数变量count*/ …… init_palette(); shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny); sht_back = sheet_alloc(shtctl); sht_mouse = sheet_alloc(shtctl); sht_win = sheet_alloc(shtctl); buf_back = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny); buf_win = (unsigned char *) memman_alloc_4k(memman, 160 * 52); /*为计数窗口开辟内存空间,依旧是12KB*/ sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1); sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99); sheet_setbuf(sht_win, buf_win, 160, 52, -1); /* 计数窗口缓冲区 */ init_screen8(buf_back, binfo->scrnx, binfo->scrny); init_mouse_cursor8(buf_mouse, 99); make_window8(buf_win, 160, 52, "counter"); /*计数窗口图层的标题*/ …… 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); /*到这里结束*/ io_cli(); if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) { io_sti(); /*不做HLT!!!*/ } else { …… } } }
- 注意,在for中的if语句,将
io_stihlt();
改成了io_sti();
。也就是说,在本次中,我们不让CPU休息,不采用HLT。 - 让CPU在电力许可的范围内权利计数,因此叫高速计数器。
- 注意,在for中的if语句,将
-
make run
:
的确能够计数,但是计数的内容在闪烁。 -
显然,上图中出现的颜色是背景图层的颜色。出现这种闪烁的原因也就明了了:
在刷新sht_win的特定区域的时候,总是先刷新特定区域的背景图层,再刷新窗口图层,那么这样指定会闪烁。
7. 消除闪烁(1)(harib08g)
-
当某一图层发生变化时,其余图层可以认为没有发生变化。
-
只刷新发生变化的图层也是不行的,因为该图层上面的图层也可能因为该图层的变化而导致显示内容的改变。
-
因此,需要对该图层以及该图层上方的图层进行刷新。
-
修改完成刷新工作的sheet_refreshsub函数:
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0) { …… for (h = h0; h <= ctl->top; h++) { …… } return; }
- 添加了参数h0,只对图层高度>=h0的图层进行刷新。
-
对所有调用过sheet_refreshsub的函数进行修改:
- sheet_refresh函数:
void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1) { if (sht->height >= 0) { sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1, sht->height); /*当前图层及以上*/ } return; }
- sheet_slide函数:
void sheet_slide(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(sht->ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0); /*图层移动会导致下面的图层露出,因此从高度为0开始*/ sheet_refreshsub(sht->ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height); /*新移来的图层不会对比其低的图层产生变化,因此从当前图层的高度开始*/ } return; }
- sheet_updown函数:
void sheet_updown(struct SHEET *sht, int 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_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height + 1); /*把当前图层往下移动,只需要刷新当前图层以上的图层即可*/ } 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_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, 0); /*把当前图层隐藏,因此需要从0开始*/ } } 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_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height); /*当前图层往上移,因此,需要刷新当前图层及当前图层以上的图层。*/ } return; }
- sheet_refresh函数:
-
由于sheet_updown、sheet_refresh、sheet_slide的参数没发生变化,因此HariMain不要修改。
-
make run
:
闪烁消失了。继续移动鼠标,不好,当鼠标移动到数字上的时候,鼠标在闪烁!
8. 消除闪烁(2)(harib08h)
-
鼠标图层一直位于最上方。因此,每当计时器图层发生变化时,计时器图层和鼠标图层都会刷新,先刷新计时器图层再刷新鼠标图层,因此鼠标会闪烁。
-
解决方法:刷新窗口时,避开鼠标所在区域对VRAM的写入处理。刷新窗口时,不再需要重新绘制鼠标,速度也会稍微提高。
-
首先开辟一块大小和VRAM一样的内存空间map。
-
修改结构体SHTCTL:
struct SHTCTL { unsigned char *vram, *map; int xsize, ysize, top; struct SHEET *sheets[MAX_SHEETS]; struct SHEET sheets0[MAX_SHEETS]; };
-
修改shtctl_init函数:
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize) { …… ctl->map = (unsigned char *) memman_alloc_4k(memman, xsize * ysize); /*为map开辟64KB的内存空间*/ if (ctl->map == 0) { /*分配失败*/ memman_free_4k(memman, (int) ctl, sizeof (struct SHTCTL));/*释放已经分配的内存空间*/ goto err; } …… err: return ctl; }
- map用来表示画面上的点是属于哪个图层。
- map用来表示画面上的点是属于哪个图层。
-
编写函数sheet_refreshmap用来向map中写入图层号码:
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; /* 用进行了减法操作的地址作为图层号码使用 */ 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写入,否则map保持原来的值*/ map[vy * ctl->xsize + vx] = sid; } } } } return; }
- 这个函数类似于sheet_refreshsub。
- 使用
sht - ctl->sheets0
作为图层号码。【这个在这里是新颖的!】
-
修改sheet_refreshsub函数,在刷新屏幕特定区域的时候同时刷新map中的值。
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; …… for (h = h0; h <= h1; h++) { sht = ctl->sheets[h]; buf = sht->buf; sid = sht - ctl->sheets0; /*图层号码*/ …… 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进行写入操作。*/ vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx]; } } } } return; }
- 以后程序会根据map的值向VRAM写入。所以有时没有必要从当前图层刷新到最上面图层,因此需要指定h0和h1。这也就是参数h1。
- 图层变化时,需要先修改map!
-
修改sheet_refresh:
void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1) { if (sht->height >= 0) { sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1, sht->height, sht->height); /*因为sheet_refreshsub根据map中的值来刷新,所以,我们只刷新当前图层就可以了。*/ } return; }
-
修改sheet_slide:
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); /*移动前的地方需要从高度为0的地方向上刷新map*/ sheet_refreshmap(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height); /*移动后的地方需要从当前图层向上刷新map*/ sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0, sht->height - 1); /*移动前的地方,根据map中对应的值,重新绘制因为此图层移走而露出的下层图层们。*/ sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height, sht->height); /*对于移动后的地方,根据map的值,重新绘制当前图层即可。*/ } return; }
-
修改sheet_updown:
void sheet_updown(struct SHEET *sht, int 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); /*刷新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; }
-
make run
:
不再闪烁。
9. 不知道写点啥
- 2020.4.11 16:44
- 那就从今天开始吧,希望明天不会再次说这句话。