进程消息通讯和回车键处理
1.简介
我们顺利的完成了进程间的相互切换 但当前存有的一个问题是
如果我们把输入焦点转移到命令行控制台 然后在通过Tab键 把输入焦点切换回文本框
此时存在一个问题是 命令行控制台的输入指针居然还存在
这就涉及到了进程间的通讯 一个进程将信号发送给另一个进程
让对方及时采取某些动作
由于我们在设计时 为每个进程准备了一个接收信息的队列 因此进程间相互发送消息是
可以把消息放入到接收方的队列中 当接收方从队列中获取数据时 就能收到其他进程发送过来的信息
进而及时处理
2.代码
我们先定义两个消息
一个消息对应进程终止
一个消息对应进程恢复
这两个消息定义在multi_task.h中
代码如下
#define PROC_RESUME 0x57 #define PROC_PAUSE 0x58 void send_message(struct TASK *sender, struct TASK *receiver, int msg);
上面代码还定义了进程间发送消息的函数send_message
第一个输入参数是发送消息的进程对象
第二个参数是接收消息的进程对象
第三个参数是要发送的消息
在主进程中 当接收到Tab键时
如果是输入焦点是切换到控制台进程 那么主进程就通过send_message给对方进程
发送一个PROC_RESUME消息 告诉对方获得了执行权限
如果输入焦点是从控制台进程切换回主进程
那么主进程就给对方发送PROC_PAUSE消息 让对方赶紧为挂起做准备
代码如下
write_vga_desktop.c
void CMain(void) { .... for(;;) { .... if (fifo8_status(&keyinfo) + fifo8_status(&mouseinfo) + fifo8_status(&timerinfo) == 0) { .... else if (data == 0x0f) { int msg = -1; if (key_to == 0) { key_to = 1; make_wtitle8(shtctl, shtMsgBox,"task_a", 0); make_wtitle8(shtctl, sht_cons, "console", 1); set_cursor(shtctl, shtMsgBox, cursor_x, COL8_FFFFFF); msg = PROC_RESUME; } else { key_to = 0; make_wtitle8(shtctl, shtMsgBox, "task_a",1); make_wtitle8(shtctl, sht_cons, "console", 0); fifo8_put(&task_cons->fifo, 0x58); msg = PROC_PAUSE; } sheet_refresh(shtctl, shtMsgBox, 0, 0, shtMsgBox->bxsize, 21); sheet_refresh(shtctl, sht_cons, 0, 0, sht_cons->bxsize, 21); send_message(task_a, task_cons, msg); } .... } .... } .... }
我们再看看send_message的实现
它的逻辑其实很简单 它首先把消息放入到接收进程的数据队列中 一旦进程的数据队列有数据输入时
对应的进程就会被唤起 然后发送进程把自己挂起 将CPU权限转移给接收进程
代码如下
multi_task.c void send_message(struct TASK *sender, struct TASK *receiver, int msg) { fifo8_put(&receiver->fifo, msg); task_sleep(sender); }
一旦进程接收到消息后 它会在它的主循环中把消息取出 根据不同消息采取不同操作 例如对应控制台进程
如果接收到挂起消息 那么它应该把光标从窗口上抹掉 然后把控制器重新交还给主进程
如果接收到的是进程恢复消息 那么它可以重新绘制光标 并启动光标计时器 进而实现光标的重新闪动
代码如下
write_vga_desktop.c
void console_task(struct SHEET *sheet) { .... for(;;) { .... else { io_sti(); i = fifo8_get(&task->fifo); if (i <= 1 && cursor_c >= 0) { if (i != 0) { timer_init(timer, &task->fifo, 0); cursor_c = COL8_FFFFFF; } else { timer_init(timer, &task->fifo, 1); cursor_c = COL8_000000; } timer_settime(timer, 50); } else if (i == PROC_RESUME) { cursor_c = COL8_FFFFFF; timer_init(timer, &task->fifo, 0); timer_settime(timer, 50); } else if (i == PROC_PAUSE) { set_cursor(shtctl, sheet, cursor_x, COL8_000000); cursor_c = -1; task_run(task_main, -1, 0); } else if (i == 0x0e && cursor_x > 8) { set_cursor(shtctl, sheet, cursor_x,COL8_000000); cursor_x -= 8; set_cursor(shtctl, sheet, cursor_x, COL8_000000); } else { char c = transferScanCode(i); if (cursor_x < 240 && c!=0 ) { set_cursor(shtctl, sheet, cursor_x, COL8_000000); char s[2] = {c, 0}; showString(shtctl, sheet, cursor_x, 28, COL8_FFFFFF, s); cursor_x += 8; } } if (cursor_c >= 0) { set_cursor(shtctl, sheet, cursor_x, cursor_c); } .... } .... }
上面的部分是 进程间消息通讯
下面的部分是 回车键的处理
系统能够响应回车键的话 我们就可以实现从控制台启动应用程序
主要实现都在write_vga_desktop.c中
#define KEY_RETURN 0x1C void console_task(struct SHEET *sheet) { struct TIMER *timer; struct TASK *task = task_now(); int i, fifobuf[128], cursor_x = 16, cursor_c = COL8_000000; int cursor_y = 28; .... for(;;) { .... else if (i == KEY_RETURN) { if (cursor_y < 28 + 112) { set_cursor(shtctl, sheet, cursor_x, cursor_y, COL8_000000); cursor_y += 16; cursor_x = 16; showString(shtctl, sheet, 8, cursor_y, COL8_FFFFFF, ">"); } } else if (i == 0x0e && cursor_x > 8) { set_cursor(shtctl, sheet, cursor_x, cursor_y, COL8_000000); cursor_x -= 8; set_cursor(shtctl, sheet, cursor_x, cursor_y, COL8_000000); } else { char c = transferScanCode(i); if (cursor_x < 240 && c!=0 ) { set_cursor(shtctl, sheet, cursor_x, cursor_y,COL8_000000); char s[2] = {c, 0}; showString(shtctl, sheet, cursor_x, cursor_y, COL8_FFFFFF, s); cursor_x += 8; } } if (cursor_c >= 0) { set_cursor(shtctl, sheet, cursor_x, cursor_y, cursor_c); } .... } } void set_cursor(struct SHTCTL *shtctl, struct SHEET *sheet, int cursor_x, int cursor_y ,int cursor_c) { boxfill8(sheet->buf, sheet->bxsize, cursor_c, cursor_x, cursor_y, cursor_x + 7, cursor_y + 15); sheet_refresh(shtctl, sheet, cursor_x, cursor_y, cursor_x+8, cursor_y + 46); }
我们对代码做的改动有 先定义回车键扫描码的数值0x1C 当回车键按下后
主进程会把扫描码发送给控制台进程的主函数 在控制台进程主函数中 一旦接受到回车键消息时
把cursor_y的值加上16 也就是一个字符的高度 cursor_y将作为新的显示纵坐标
我们原来显示字符时 纵坐标都是写死的 为28
现在我们把纵坐标改为可变动的情况 一旦受到回车键 系统便知道需要在新的一行显示信息
于是我们让控制台进程在窗口中另起一行 也就是将输入坐标向下移动16个字符的距离
然后无论是字符还是光标
他们的显示都在新坐标下进行
set_cursor用来绘制光标 原先光标的纵坐标是定死为28的 现在我们把纵坐标当做一个参数来处理
每次窗口想要绘制光标时 需要把光标所在的纵坐标传入
有了上面代码后 我们每次在命令行窗口中点击回车键时
命令行窗口会新起一行 字符和光标都会在新一行中显示
3.编译运行