30天自制操作系统(day11)

第11天:制作窗口

1、内容1:添加一个窗口图层有哪些步骤?(格式:文字说明+对应代码)

步骤一:创建描绘窗口的函数。

void window(unsigned char* buf, int xsize, int ysize){
	static char closebtn[15][17] = {
		"@@@@@@@@@@@@@@@@@",
		"@$$$$$$$$$$$$$$$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQ@@QQQQ@@QQ$@",
		"@$QQQQ@@QQ@@QQQ$@",
		"@$QQQQQ@@@@QQQQ$@",
		"@$QQQQQQ@@QQQQQ$@",
		"@$QQQQQ@@@@QQQQ$@",
		"@$QQQQ@@QQ@@QQQ$@",
		"@$QQQ@@QQQQ@@QQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$$$$$$$$$$$$$$$@",
		"@@@@@@@@@@@@@@@@@@"
	};
	static char smallbtn[15][17] = {
		"@@@@@@@@@@@@@@@@@",
		"@$$$$$$$$$$$$$$$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQ@@@@@@@@@QQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$$$$$$$$$$$$$$$@",
		"@@@@@@@@@@@@@@@@@@"
	};
	static char bigbtn[15][17] = {
		"@@@@@@@@@@@@@@@@@",
		"@$$$$$$$$$$$$$$$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$QQ@@@@@@@@@QQ$@",
		"@$QQ@QQQQQQQ@QQ$@",
		"@$QQ@QQQQQQQ@QQ$@",
		"@$QQ@QQQQQQQ@QQ$@",
		"@$QQ@QQQQQQQ@QQ$@",
		"@$QQ@QQQQQQQ@QQ$@",
		"@$QQ@QQQQQQQ@QQ$@",
		"@$QQ@QQQQQQQ@QQ$@",
		"@$QQ@@@@@@@@@QQ$@",
		"@$QQQQQQQQQQQQQ$@",
		"@$$$$$$$$$$$$$$$@",
		"@@@@@@@@@@@@@@@@@@"
	};
	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_FFFFFF, 3, 3, xsize - 4, 20);//框
	boxfill8(buf, xsize, COL8_000000, 3, 21, xsize - 3, 21);//线
	boxfill8(buf, xsize, COL8_848484, 1, ysize - 2, xsize - 2, ysize - 2);//横
	boxfill8(buf, xsize, COL8_000000, 0, ysize - 1, xsize - 1, ysize - 1);//横
	for (y = 0; y < 15; y++) {
		for (x = 0; x < 17; x++) {
			c = closebtn[y][x];
			if (c == '@') {	c = COL8_000000;
			}else if (c == '$') {	c = COL8_848484;
			}else if (c == 'Q') {c = COL8_FFFFFF;
			}else {c = COL8_FFFFFF;
			}
			buf[(5 + y) * xsize + (xsize - 21 + x)] = c;
		}
	}
	for (y = 0; y < 15; y++) {
		for (x = 0; x < 17; x++) {
			c = smallbtn[y][x];
			if (c == '@') {	c = COL8_000000;
			}else if (c == '$') {	c = COL8_848484;
			}else if (c == 'Q') {c = COL8_FFFFFF;
			}else {c = COL8_FFFFFF;
			}
			buf[(5 + y) * xsize + (xsize - 21 - 40 + x)] = c;
		}
	}
	for (y = 0; y < 15; y++) {
		for (x = 0; x < 17; x++) {
			c = bigbtn[y][x];
			if (c == '@') {	c = COL8_000000;
			}else if (c == '$') {	c = COL8_848484;
			}else if (c == 'Q') {c = COL8_FFFFFF;
			}else {c = COL8_FFFFFF;
			}
			buf[(5 + y) * xsize + (xsize - 21 - 20 + x)] = c;
		}
	}
	return;
}

步骤二:定义图层包(结构体SHTCTL),建立图层(SHEET),建立缓冲区buf(地址)
在这里插入图片描述
步骤三:为窗口取得新生成的未被使用的图层,初始化图层
在这里插入图片描述
步骤四:为窗口图层分配内存
在这里插入图片描述
320*170是窗口的大小
步骤五:设置窗口图层的缓冲区大小和透明色
在这里插入图片描述
步骤六:调用描绘窗口函数和显示函数,即内容初始化
在这里插入图片描述
步骤七:设置可上下左右移动窗口图层函数,即设置图层显示位置的起始坐标
在这里插入图片描述
(mx,my)是窗口的左上角。
步骤八:设置窗口图层高度
在这里插入图片描述
步骤九:刷新屏幕显示。
在这里插入图片描述
0,0为左上角,后两个参数为右下角

2、内容2:教材202页,为什么鼠标移动到最右边后左边会出现鼠标图案,为什么右边只有一个鼠标,左边却有很多?

因为作者在内容一中只是改动了鼠标移动的边界,使鼠标可以在右边隐藏起来。这样虽然实现了,但是之前为了显示鼠标图层,sheet_refreshsub函数会把图层内容写入到VRAM中,因此,即使鼠标图层的一部分处在了画面之外,函数依然会刷新图层,包括画面以外的部分,但在右边已经没法写入到VRAM中,函数会自动将画面外的图层部分写入到左边所对应的VRAM中。所以鼠标移动最右边后,左边会出现鼠标图案,但在画面中的部分在每次移动之后都会刷新,将之前的释放,但在画面外的内容,在每次移动后,不会对之前的内容刷新释放,所以就会出现了右边只有一个鼠标,左边有很多。具体如下:
我们由上一天内容知道,图层移动的时候会调用sheet_refreshsub()和sheet_slide这两个函数:
在这里插入图片描述
在这里插入图片描述
对鼠标的刷新:
在这里插入图片描述
我们知道鼠标的坐标是经过修正的,所以传入的old图层的起始位置是在合理范围内。
在这里插入图片描述
查看refreshsub函数:
对我们有用的是最后的显示部分,因为不管鼠标在哪显示,都用用到像素点
在这里插入图片描述
如果按照书上的方法修改,根据像素点公式(一维计算)会导致刷新的像素点往后偏移16位(相当于往右),所以在屏幕左边会出现鼠标。对刷新old图层来说,传入的参数vx0=mx和vx1>xsize,对新图层来说,也是vx0=mx和vx1>xize,而且老图层和新图层的vx1值是相同的,因为当我们把鼠标放到最右边的时候,传入的mx都是同一个值,所以计算出的vx1也是同一个值(mx+16),这个时候两次调用的bx1相同,会先刷新一次老的,再刷新一次新的,那还是在原来的位置,所以鼠标只能在左边的固定范围内出现。刷新的时候因为屏幕的背景是按照一样的计算公式填充的,所以在边界移动的时候会有痕迹保留,也就说old的值还存在背景中,背景图层在刷新的时候会把“遗留”下来的一起刷新,可能是鼠标的颜色,也可能是黑色。
在这里插入图片描述
改正方法:在sheet_refreshsub函数里面的循环刷新之前,添加判断
在这里插入图片描述

3、内容3:教材216页,每个图层的sid是如何设置的?具体数值等于多少?举例说明,建议编程打印sid进行验证。

Sid变量首次出现在sheet_refreshmap函数中,保存的是每个图层相对于sheets0的位置,作者给出的解释是减法计算得出的(地址)图层号码,所以说实际上sid是一个地址,是sheet ID的缩写,出现在函数中是这种形式:
在这里插入图片描述
图像形式:相当于一个图层体包含的多个图层每个图层有固定的地址,用当前图层的地址减去最下面图层的地址就可以得出当前的sid号了
在这里插入图片描述
可以理解成图层的标号1,2,3………

4、内容4:教材216-217页,结合代码,解释刷新函数(sheet_refreshsub)的参数和实现逻辑。

void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1)
参数ctl是图层控制结构体指针,参数vx0、vy0、vx1、vy1是指定刷新图层的范围,参数h0是需要刷新的最低图层的高度,参数h1是需要刷新的最高图层的高度。
实现逻辑:
	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++) {//只对h0到h1的图层进行更新操作。
		sht = ctl->sheets[h];//当前图层的地址
		buf = sht->buf;//当前图层的缓冲区内容
		sid = sht - ctl->sheets0;
计算每个图层的sid,在叠加部分进行刷新,利用vx0~vy1对bx0~by1进行倒推
		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; }
根据vx0、vy0、vx1、vy1倒推bx0、by0、bx1、by1,得到刷新范围。
		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) {
判断是否可以绘制(比如说当前图层号码为2,那么只绘制2那层)
					vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
				}
			}
		}
	}
最后将缓存写到VRAM中,只有当前sid和map中该像素处sid相等,才将缓存写到VRAM中。

5、内容5:教材217页,结合代码,解释滑动函数(sheet_slide)的参数和实现逻辑,注意内部调用sheet_refreshmap和sheet_refreshsub的传参,特别是高度参数,为什么这样传。

void sheet_slide(struct SHEET *sht, int vx0, int vy0)
参数sht是图层指针,vx0、vy0是新图层左上角的坐标。
实现逻辑:
struct SHTCTL *ctl = sht->ctl;
首先得到图层控制结构体指针。
	int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
old_vx0,old_vy0表示原来的图层
	sht->vx0 = vx0;
	sht->vy0 = vy0
 vx0,vy0表示新的图层
然后得到移动前和移动后的图层的左上角坐标。
	if (sht->height >= 0) { 
如果正在显示,按照新图层的信息进行刷新
		sheet_refreshmap(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);
绘制原来图层位置的新地图,因为是原来的位置,图层移走了,无法判断图层移动之后的情况,所以旧图层下面和上面的正在显示的图层都需要重新刷新,要从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函数,因为移动前的图层已经移走了,所以需要对正在显示的移动前图层以下的图层进行描绘,所以高度是0~sht->height-1sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height, sht->height);
对于移动后的图层来说,只需要描绘移动的那一个图层即可,所以高度是sheet->height~sheet->height,也就是sheet->height的高度这一个图层。或者可以理解为新的图层地图只是在新图层的高度以上进行了刷新,所以新高度的地图已经绘制好了,直接按照地图刷新当前这一高度的图层就可以了
	}
这样传的原因是,只绘制了中间变化的图层,而不需要重新绘制鼠标图层,从而消除了鼠标因为自身不停的被覆盖再绘制所产生的闪烁。
发布了205 篇原创文章 · 获赞 110 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_40851744/article/details/103367807