今天我们主要的目标就是制作一个窗口页面,以及再优化一下鼠标的问题。
在前面制作的鼠标的过程,如果我们将鼠标往屏幕边缘移动时,发现并不能使得鼠标消失,它会一直处于屏幕的边缘处(不能像Windows这样子),因此,我们以下将继续修改这一部分:
在主函数中做如下修改:
//让鼠标动起来
mx += mdec.x;
my += mdec.y;
if(mx < 0)
{
mx = 0;
}
if(my < 0)
{
my = 0;
}
if(mx > binfo->scrnx - 1)
{
mx = binfo->scrnx - 1;
}
if(my > binfo->scrny - 1)
{
my = binfo->scrny - 1;
}
至此,我们可以将鼠标移出屏幕外边了,但是这这里可能会出现一些bug,这很可能是由于refresh_sub这个函数导致的,那么让我们进一步的修改一下:
sheet.c:
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;//创建一个临时图层
//如果refresh的范围超出了画面则修正,ctl中的xsize与ysize中包含的是vram的大小
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++)//从下往上,所有图层都描绘一遍
{...略
然后,进一步的,由于我们代码中每做一次关于图层的移动啥的都需要指定一次ctl,感觉可能有点麻烦,因此这里我们优化一下代码:
bootpack.h:
struct SHEET
{
unsigned char *buf;//记录图层上所描绘图画的地址
int bxsize, bysize, vx0, vy0, col_inv, height, flags;
//图层整体大小用bxsize、bysize表示;vx0, vy0 图层在vram画面上位置的相对坐标
//col_inv 表颜色的透明度; height 表图层高度;flag记录图层各种设定信息
struct SHTCTL *ctl;
};
sheet.c:
struct SHTCTL *shtctl_init(
{略:
for(i = 0; i < MAX_SHEETS; i++)
{
ctl->sheets0[i].flags = 0;//让所有的图层都标记未使用
ctl->sheets0[i].ctl = ctl;//记录所属,记录的是该图层属于哪一个管理表,并把地址给予
}
err:
return ctl;
}
void sheet_updown(struct SHEET *sht, int height)
{
struct SHTCTL *ctl = sht->ctl;
int h, old = sht->height; //存储之前的高度信息
//如果指定的高度过高或者过低,则进行修正
略
}
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)//如果正在显示//按图层信息刷新画面
{
/*由于鼠标图层的地址已经改变了,因此以下两条语句:(因为sub这个是只刷新指定范围内的)
如果只执行第一句的话,那么新鼠标无法显示(执行完后,因为鼠标图层改变了,不再源像位置了,因此图层只会刷新原先地方存在的图层的)
如果只执行第二句的话,那么可能会显示不完整
*/
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->ctl, sht, -1);//处于显示状态,先设定隐藏
}
sht->flags = 0; //为使用标志
return;
}
然后再修改相应的地方即可。
这里窗口制作与前面我们所制作的背景版以及鼠标是很像的,我们用于初始化窗口的代码就是参照前面两个进行改造的:
bootpack.c:
void make_window8(unsigned char *buf, int xsize, int ysize, char *title)
{
//x按钮功能,和init_moise_cursor8类似
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;
}
然后,我们还要在主函数中分配一个图层:
struct SHEET *sht_back, *sht_mouse, *sht_win;//准备图层区
unsigned char *buf_back, buf_mouse[256], *buf_win;//缓冲区,用于在其中描绘需要的图形
....略
shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);//传入内存管理表是因为要分配地址给图层
sht_back = sheet_alloc(shtctl);//分配给背景一个图层,背景图层位于最底层,sheets0[0]
sht_mouse = sheet_alloc(shtctl);//sheets0[1]
sht_win = sheet_alloc(shtctl);
buf_back = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);//分配给back一个与vram大小一样的地址
buf_win = (unsigned char *) memman_alloc_4k(memman, 160 * 68 );
sheet_setbuf(sht_back, buf_back,binfo->scrnx, binfo->scrny, -1);//没有透明色,由于我们已经给图层管理表的初始地址设定在了vram当中,因此任何图层都会在vram空间里
sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);//鼠标像素阵16x16,给予最不透明度
sheet_setbuf(sht_win, buf_win, 160, 68, -1); //无透明色
init_screen8(buf_back, binfo->scrnx, binfo->scrny);//往back地址中填入颜色
init_mouse_cursor8(buf_mouse, 99); //背景色99号,往mouse中填入颜色和透明度
make_window8(buf_win, 160, 68, "window");
putfonts8_asc(buf_win, 160, 24, 28, COL8_000000, "welcome to");
putfonts8_asc(buf_win, 160, 24, 44, COL8_000000, "Yuan-OS");
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);//背景图的高度设置为0,并且将sheets按高度顺序写入
sheet_updown(sht_win, 1);
sheet_updown(sht_mouse, 2);//鼠标图层高度设置为1
这里我们给窗口中增加一个count的计数器的功能:
只需在主函数中进行如下修改:
unsigned int memtotal, count = 0;
buf_win = (unsigned char *) memman_alloc_4k(memman, 160 * 52 );
sheet_setbuf(sht_win, buf_win, 160, 52, -1); //无透明色
make_window8(buf_win, 160, 52, "counter-Yuan-OS");
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=0
略..
上一部分中,我们建立的窗口在增加计数的过程中,可能会出现闪烁。这是因为,我们在刷新页面的过程中,是从背景开始逐步往上刷的因此在计数的过程中会先出现背景然后再出现窗口,这就有可能导致了闪烁情况的发送。至此,这里我们将利用一个map的内存空间。
该map空间的大小与vram一样,其中map会记录画面上的点是哪一个图层的像素,然后我们就可以利用这个map来直接刷新最上方,需要显示的窗口即可。
当我们采用map后,以后在刷新窗口的时候是可以避开鼠标所在的地方对VRAM进行写入处理的。
struct SHTCTL//图层管理
{
unsigned char *vram, *map;//vram x y 代表VRAM的地址和画面大小,map 用于表示画面上的点是哪一个图层信息的
int xsize, ysize, top;//top最上面图层的高度
struct SHEET *sheets[MAX_SHEETS];//记忆地址变量领域,由于shets0之中的图层比较混乱,因此这里将他图层能照高度升序进行写入
struct SHEET sheets0[MAX_SHEETS];//存放准备好的256个图层信息
};
接下来对sheet.c进行写入:
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize)
{
struct SHTCTL *ctl;
int i;
//返回一个空闲的addr地址,用sizeof计算该变量的地址空间大小,然后进行分配对应的内存空间大小
ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof (struct SHTCTL));
if(ctl == 0)
{
goto err;
}
ctl->map = (unsigned char *) memman_alloc_4k(memman, xsize * ysize);
if(ctl->map == 0)
{
memman_free_4k(memman, (int) ctl, sizeof (struct SHTCTL));
goto err;
}
略。。
}
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[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;//创建一个临时图层
//如果refresh的范围超出了画面则修正,ctl中的xsize与ysize中包含的是vram的大小
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;
略..
for(by = by0; by < by1; by++)
{
vy =sht->vy0 + by;
for (bx = bx0; bx < bx1; bx++)
{
vx = sht->vx0 + bx;//vy0 vx0代表图层在VRAM上画面的相对地址,用这个来让图层进行移动
if (map[vy * ctl->xsize + vx] == sid) //按照map中的内容判断是否写入刷新
{
vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];//写入vram中
}
}
}
}
return;
}
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);
}
return;
}
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);
}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;
}
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)//如果正在显示//按图层信息刷新画面
{
/*由于鼠标图层的地址已经改变了,因此以下两条语句:(因为sub这个是只刷新指定范围内的)
如果只执行第一句的话,那么新鼠标无法显示(执行完后,因为鼠标图层改变了,不再原来位置了,因此图层只会刷新原先地方存在的图层的)
如果只执行第二句的话,那么可能会显示不完整。
这里,由于画面移动了,因此在其之下的之前的图层会露出来,因此从最下面开始刷新;
然后在移动的目标处,比移动来的图层要低的图层没有说明变化,只是隐藏起来了因此直接刷移来的图层及其之上的即可
*/
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;
}
最后的画面如下:
总算完成了对图层进一步的优化了。不过对于窗口制作这个来说倒是意外的很简单!!!