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;
}