第11天:制作窗口

本文详细介绍了Windows操作系统中鼠标显示的优化过程,包括防止鼠标超出屏幕边界、解决闪烁问题以及图层管理的改进。通过修改代码,解决了鼠标隐藏部分绘制到下一行的问题,并通过调整sheet_refreshsub函数实现减少闪烁。此外,引入了图层结构体SHTCTL,增加了图层映射map,以提高图层刷新效率并避免鼠标闪烁。同时,讨论了图层高度变化时的处理策略,确保图层显示的正确性。
摘要由CSDN通过智能技术生成

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是鼠标
map

可以发现,鼠标图层遮挡了一部分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和显存都只需要刷新变高之后的图层就好了(为什么:因为变高之后,比它低的图层就不用管了,)

总的来说:就是变高之后就刷新变高之后图层,变低之后需要照顾后面的图层,前面图层会挡住后面的图层,后面的图层要显示出来就要刷新。

以上是我的理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值