30天自制操作系统(第26天)

26.1   提高窗口移动速度(1

导致窗口移动速度慢的原因有很多,其中之一就是 sheet_refreshmap的速度太慢。这个函数在 sheet_slide中被调用了两次,如果能提高它的速度效果应该会很明显。思路:由于col_inv表示透明色,且在bootpack.c文件中将背景图层的透明色号设置为-1,像窗口都是有大量透明色,所以没有透明色图层需要全部刷新,有透明色的图层只需要刷新部分。
void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0){
	(中略)	
	for(h=h0;h<=ctl->top;h++){
		(中略)
		if(sht->col_inv == -1){// 无透明色图层
			for (by = by0; by < by1; by++) {
				vy = sht->vy0 + by;
				for (bx = bx0; bx < bx1; bx++) {
					vx = sht->vx0 + bx;// 不是透明色表明该位置有图形像素,更新对应map中的数据
					map[vy * ctl->xsize + vx] = sid;
				}
			}
		}else{// 有透明色的图层
			for (by = by0; by < by1; by++) {
				vy = sht->vy0 + by;
				for (bx = bx0; bx < bx1; bx++) {
					vx = sht->vx0 + bx;// 不是透明色表明该位置有图形像素,更新对应map中的数据
					if (buf[by * sht->bxsize + bx] != sht->col_inv) {
						map[vy * ctl->xsize + vx] = sid;
					}
				}
			}
		}
	}
	return;
}

26.2   提高窗口移动速度(2

需要实现的功能:原代码只能每次写入一个字节,若能每次向map中写入4个字节的数据,将会极大提高速率。思路:根据 map[vy * ctl->xsize + vx] = sid,且map的数据类型为char,当向内部传入32位数据时,它将自动向后移位赋值(参考了汇编中的MOV操作)。
为确保被创建的sht图层的显示地址(即对应结构体中的vx0和vy0)为4的倍数,所以修改创建时的坐标(hrb_api函数中sheet_slide(sht, ((shtctl->xsize - esi) / 2) & ~3, (shtctl->ysize - edi) / 2);)。还有主函数中的鼠标移动时,也会用到图层数据,所以也需要进行修改。(注:为何要删掉代码mmx=mx呢?是因为mmx、mmy与mmx2都是鼠标按下时才被确定的数据,而窗口移动时调用sheet_slide函数会修改图层的窗口坐标,其当前窗口的x坐标仅和mmx、mmx2有关系,而这两个数据是不随着鼠标变化的,后者y坐标用的是sht->vy0+y进行更新的,每次都会随着鼠标my坐标更新sht->vy0数值。)
/*                console.c                */
if(sht->col_inv == -1){// 无透明色图层
	if((sht->vx0&3) == 0 && (bx0&3) == 0 && (bx1&3) == 0){//4字节
		bx1 = (bx1-bx0)/4;//MOV次数
		sid4 = sid | sid << 8 | sid << 16 | sid << 24;//每次写入4字节
		for (by = by0; by < by1; by++) {
			vy = sht->vy0 + by;
			vx = sht->vx0 + bx0;
			p = (int *) &map[vy * ctl->xsize + vx];
			for(bx = 0; bx < bx1; bx++)
				p[bx] = sid4;
		}	
	}else{//1字节
		for (by = by0; by < by1; by++) {
			vy = sht->vy0 + by;
			for (bx = bx0; bx < bx1; bx++) {
				vx = sht->vx0 + bx;// 不是透明色表明该位置有图形像素,更新对应map中的数据
				map[vy * ctl->xsize + vx] = sid;
			}
		}
	}
}else{// 有透明色的图层
	for (by = by0; by < by1; by++) {
		vy = sht->vy0 + by;
		for (bx = bx0; bx < bx1; bx++) {
		    vx = sht->vx0 + bx;// 不是透明色表明该位置有图形像素,更新对应map中的数据
			if (buf[by * sht->bxsize + bx] != sht->col_inv) {
				map[vy * ctl->xsize + vx] = sid;
			}
		}
	}
}

/*            bootpack.c文件的HariMain函数代码片段            */
if(mmx < 0){
    /*如果处于通常模式,窗口不移动*/
	/*按照从上到下的顺序寻找鼠标所指向的图层*/
	for(j = shtctl->top-1; j > 0; j--){
		(中略)
		if(0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize){
			if(sht->buf[y*sht->bxsize+x] != sht->col_inv){
				(中略)
				if(3 <= x && x < sht->bxsize-3 && 3<= y && y < 21){//判断是否在窗口的标题栏
					mmx = mx;
					mmy = my;
/*这里*/			mmx2 = sht->vx0;
				}
				(中略)
}else{/*窗口移动模式*/
	y = my - mmy;
	x = mx - mmx;
/*这里*/sheet_slide(sht, (mmx2+x+2)&~3, sht->vy0+y);
    mmy = my;
}

26.3   提高窗口移动速度(3

思路:由于函数 sheet_refreshsub与函数sheet_refreshmap的代码基本上相似,所以能否借鉴26.2节中的思路,每次写入4字节数据呢?有以下几种情况:1.图层的vx0(图层显示窗口的x轴坐标)是4的倍数 :①先写入前面非4倍数的显示数据②接着写入中间4的倍数部分③最后写入后面非4倍数的显示数据;2.否则就逐个写入显示数据。
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, bx2, sid4, i, i1, *p, *q, *r;
	(中略)
	for(h=h0;h<=h1;h++){
		(中略)
		if((sht->vx0&3)==0){//图层的x坐标是4的倍数
			i = (bx0+3)/4;
			i1= bx1/4;
			i1= i1-i;
			sid4 = sid|sid<<8|sid<<16|sid<<24;
			for(by = by0; by < by1; by++){
				vy = sht->vy0+by;
				for (bx = bx0; bx < bx1 && (bx&3) != 0; bx++){//先写入前面非4倍数的地址数据
					vx = sht->vx0+bx;
					if(map[vy*ctl->xsize+vx] == sid)
						vram[vy*ctl->xsize+vx] = buf[by*sht->bxsize+bx];
				}
				vx = sht->vx0+bx;
				p = (int*)&map[vy*ctl->xsize+vx];
				q = (int*)&vram[vy*ctl->xsize+vx];
				r = (int*)&buf[by*sht->bxsize+bx];
				for(i = 0; i < i1; i++){//中间4的倍数部分
					if(p[i] == sid4)q[i] = r[i];
					else{
						bx2 = bx+i*4;
						vx = sht->vx0+bx2;
						if(map[vy*ctl->xsize+vx+0] == sid)
							vram[vy*ctl->xsize+vx+0] = buf[by*sht->bxsize+bx+0];
						if(map[vy*ctl->xsize+vx+1] == sid)
							vram[vy*ctl->xsize+vx+1] = buf[by*sht->bxsize+bx+1];
						if(map[vy*ctl->xsize+vx+2] == sid)
							vram[vy*ctl->xsize+vx+2] = buf[by*sht->bxsize+bx+2];
						if(map[vy*ctl->xsize+vx+3] == sid)
							vram[vy*ctl->xsize+vx+3] = buf[by*sht->bxsize+bx+3];
					}
				}
				for(bx += i1; bx < bx1; bx++){//最后写入后面非4倍数的地址数据
					vx = sht->vx0+bx;
					if(map[vy*ctl->xsize+vx] == sid)
						vram[vy*ctl->xsize+vx] = buf[by*sht->bxsize+bx];
				}
			}
		}else{
			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) {
						vram[vy*ctl->xsize+vx] = buf[by*sht->bxsize+bx];
					}
				}
			}
		}
	}
	return;
}

26.4   提高窗口移动速度(4

为什么明明已经放开了鼠标键,窗口却还在挪动呢?这是因为伴随图层移动所进行的绘图操作非常消耗时间,导致系统来不及处理FIFO中的鼠标移动数据。 那么可以在接收到鼠标移动数据后不立即进行绘图操作,可以等 FIFO 为空时再进行绘图操作。
增加了 new_mx new_wy 两个变量,并将原来的 sheet_slide (sht_mouse, mx, my ; 改成了 new_mx = mx; new_my = my;,也就是说,我们并不真的移动鼠标图层的位置,而是将移动后的坐标暂且保存起来,当 FIFO为空时,再执行sheet_slide sht_mouse, new_mx, new_my ;
窗口移动我们也采用相同的方法,只不过有一点小小的区别,代表 FIFO为空时不需要执行 sheet_slide 的值从 -1 变成了 0x7fffffff。
if (fifo32_status(&fifo) == 0) {
	/* FIFO为空,当存在搁置的绘图操作时立即执行*/
	if(new_mx >= 0){
		io_sti();
		sheet_slide(sht_mouse, new_mx, new_my);
		new_mx = -1;
	}else if(new_wx != 0x7fffffff){
		io_sti();
		sheet_slide(sht, new_wx, new_wy);
		new_wx = 0x7fffffff;
	}else{
		task_sleep(task_a);
		io_sti();
	}
} else {
    (中略)
else if (512 <= i && i <= 767) {/* 鼠标数据 */
	if (mouse_decode(&mdec, i - 512) != 0) {
	    /* 更新鼠标的位置 */
		mx += mdec.x;
		my += mdec.y;
		new_mx = mx;
		new_my = my;
		(中略)
		if((mdec.btn&0x01) != 0){// 如果左键按下,则移动窗口
			if(mmx < 0){
				for(j = shtctl->top-1; j > 0; j--){
					sht = shtctl->sheets[j];
					y = my - sht->vy0;
					x = mx - sht->vx0;
					if(0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize){
						if(sht->buf[y*sht->bxsize+x] != sht->col_inv){
							(中略)
							if(3 <= x && x < sht->bxsize-3 && 3<= y && y < 21){//判断是否在窗口的标题栏
								mmx = mx;
								mmy = my;
								mmx2 = sht->vx0;
								new_wy = sht->vy0;
							}
							(中略)
						}
					}
				}
			}else{/*窗口移动模式,更新图层的位置*/
				y = my - mmy;
				x = mx - mmx;
				new_wx = (mmx2 + x + 2) & ~3;
				new_wy = new_wy + y;
				mmy = my;
			}
		}else{/*没有按下左键*/
			mmx = -1;/*返回通常模式*/
			if(new_wx != 0x7fffffff){
				sheet_slide(sht, new_wx, new_wy);/*刷新图层位置*/
				new_wx = 0x7fffffff;
			}
		}
	}
}

26.5   启动时只打开一个命令行窗口

需要实现的功能:按下"shift+F2"组合键时,弹出新的一个命令窗口。思路:原代码已经实现了按下"shift+F1"组合键的功能,所以在接收键盘数据时增加一个判断即可,只有满足新创建的命令窗口不存在且同时按下组合键的要求才可以。open_console函数创建命令窗口,原程序在25.4节中,在本节中进行了封装。
if(i == 256 + 0x3c && key_shift != 0 && sht_cons[1] == 0){/* Shift+F2 开启新命令窗口 */
	sht_cons[1] = open_console(shtctl, memtotal);
	sheet_slide(sht_cons[1], 32, 4);
	sheet_updown(sht_cons[1], shtctl->top);
	/*自动将输入焦点切换到新打开的命令行窗口*/
	keywin_off(key_win);
	key_win = sht_cons[1];
	keywin_on(key_win);
}

26.6   增加更多的命令行窗口

需要实现的功能:按下"shift+F2"组合键时,弹出新的窗口,可以一直创建。思路:特地定义sht_cons[]这个变量保存了一个值,实际上却根本没有用到!按照之前的设定,key_win是指向当前窗口,与sht_cons[]功能相似,所以干脆直接用key_win变量。
void HariMain(void)
{
    (中略)
    struct SHEET *sht_back, *sht_mouse; /*删掉了sht_cons[2]*/
    (中略)
    /* sht_cons */
    key_win = open_console(shtctl, memtotal);
    (中略)
    sheet_slide(sht_back, 0, 0);
    sheet_slide(key_win, 32, 4); /*这里!*/
    sheet_slide(sht_mouse, mx, my);
    sheet_updown(sht_back, 0);
    sheet_updown(key_win, 1); /*这里!*/
    sheet_updown(sht_mouse, 2);
    keywin_on(key_win);
    (中略)
    for (;;) {
        (中略)
        if (fifo32_status(&fifo) == 0) {
            (中略)
        } else {
            (中略)
            if (256 <= i && i <= 511) { /*键盘数据*/
                (中略)
                if (i == 256 + 0x3c && key_shift != 0) { /* Shift+F2 */
                    /*自动将输入焦点切换到新打开的命令行窗口*/
/*从此开始*/         keywin_off(key_win);
                    key_win = open_console(shtctl, memtotal);
                    sheet_slide(key_win, 32, 4);
/*到此结束*/         sheet_updown(key_win, shtctl->top);
                    keywin_on(key_win);
                }
                (中略)
            } else if (512 <= i && i <= 767) { /*鼠标数据*/
                (中略)
            }
        }
    }
}

26.7   关闭命令行窗口(1

需要实现的功能:在命令行窗口中,输入“exit”命令就可以关闭当前窗口思路:首先在bootpack.c文件中需要将创建该窗口时所占用的内存空间全部释放出来,然后还需要释放窗口的图层和任务结构。 console.c文件中增加对输入字符的判断,首先需要取消控制光标闪烁的定时器,然后将FAT用的内存空间释放,最后调用close_console关闭命令行窗口和自身的任务( 需要让 task_a来替执行关闭任务这个操作,从命令行窗口任务向task_a任务发送一个数据,请task_a帮忙关闭命令行窗口任务, task_a FIFO 地址保存在 0x0fec 这个地址)。
还需要修改 HariMain 使其能够处理来自命令行窗口的 768 1023的数据,另外,从现在开始可能会出现画面上一个窗口都没有的情况,该处的代码就不做展示了。
/*                bootpack.c                */
struct SHEET *open_console(struct SHTCTL *shtctl, unsigned int memtotal){
    (中略)
    task->cons_stack = memman_alloc_4k(memman, 64 * 1024); /*从此开始*/
    task->tss.esp = task->cons_stack + 64 * 1024 - 12; /*到此结束*/
    (中略)
}

void close_constask(struct TASK *task){
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    task_sleep(task);
    memman_free_4k(memman, task->cons_stack, 64 * 1024);
    memman_free_4k(memman, (int) task->fifo.buf, 128 * 4);
    task->flags = 0; /*用来替代task_free(task); */
    return;
}

void close_console(struct SHEET *sht){
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct TASK *task = sht->task;
    memman_free_4k(memman, (int) sht->buf, 256 * 165);
    sheet_free(sht);
    close_constask(task);
    return;
}

/*                console.c                */
void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, int memtotal){
    (中略)
    } else if (strcmp(cmdline, "exit") == 0) {
        cmd_exit(cons, fat);
    } else if (cmdline[0] != 0) {
        (中略)
}

void cmd_exit(struct CONSOLE *cons, int *fat){
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct TASK *task = task_now();
    struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
    struct FIFO32 *fifo = (struct FIFO32 *) *((int *) 0x0fec);
    timer_cancel(cons->timer);
    memman_free_4k(memman, (int) fat, 4 * 2880);
    io_cli();
    fifo32_put(fifo, cons->sht - shtctl->sheets0 + 768); /* 768〜1023 */
    io_sti();
    for (;;) {
        task_sleep(task);
    }
}

26.8   关闭命令行窗口(2

需要实现的功能:点击"x",就可以关闭当前窗口思路:在bootpack.c函数中有判断没有点击应用程序窗口的"x"的程序,只需要添加else部分。 鼠标的点击是在 task_a 中处理的,如果直接调用 close_console,由窗口自身所管理的释放定时器及 FAT内存空间的部分就难以实现了。所以,选择向命令行窗口发送通知数据这种方式。
/*                bootpack.c                */
if(sht->bxsize-21 <= x && x < sht->bxsize-5 && 5<= y && y < 19){//点击"x"关闭窗口
	if((sht->flags & 0x10) != 0){/*该窗口不是应用程序窗口*/
		task = sht->task;
		cons_putstr0(task->cons, "\nBreak(mouse) :\n");
		io_cli();
		task->tss.eax = (int)&(task->tss.esp0);
		task->tss.eip = (int) asm_end_app;
		io_sti();
	}else{//是命令行窗口 /*从这里开始*/
		task = sht->task;
		io_cli();
		fifo32_put(&task->fifo, 4);
		io_sti(); /*到这里结束*/
	}
}

/*                console.c                */
void console_task(struct SHEET *sheet, int memtotal)
{
    (中略)
    for (;;) {
        (中略)
        if (fifo32_status(&task->fifo) == 0) {
            (中略)
        } else {
            (中略)
            if (i == 4) { /*点击命令行窗口的“×”按钮*/ /*从此开始*/
                cmd_exit(&cons, fat);
            } /*到此结束*/
            (中略)
        }
    }
}

26.9   start命令

需要实现的功能:输入"start color2",就可以运行color2程序,弹出对应窗口思路:可以新创建一个命令窗口,并部分写入"color2"数据。
void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, int memtotal){
    (省略)
    } else if (strncmp(cmdline, "start ", 6) == 0) {
    cmd_start(cons, cmdline, memtotal);
    } else if (cmdline[0] != 0) {
    (省略)
}

void cmd_start(struct CONSOLE *cons, char *cmdline, int memtotal){
    struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
    struct SHEET *sht = open_console(shtctl, memtotal);
    struct FIFO32 *fifo = &sht->task->fifo;
    int i;
    sheet_slide(sht, 32, 4);
    sheet_updown(sht, shtctl->top);
    /*将命令行输入的字符串逐字复制到新的命令行窗口中*/
    for (i = 6; cmdline[i] != 0; i++) {
        fifo32_put(fifo, cmdline[i] + 256);
    }
    fifo32_put(fifo, 10 + 256); /*回车键*/
    cons_newline(cons);
    return;
}
  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值