24.1 窗口切换(1)
需要实现的功能:当按下F11键盘按键时,将最下面的窗口调整到最上面显示。回顾一下,按键的判断是在文件bootpack.c中,且F11对应的编码是0x57,因此在函数中检查键盘数据时增加一个判断即可实现该功能。
void HariMain(void)
{
(中略)
for (;;) {
(中略)
if (fifo32_status(&fifo) == 0) {
(中略)
} else {
(中略)
if (256 <= i && i <= 511) { /*键盘数据*/
(中略)
/*从此开始*/ if (i == 256 + 0x57 && shtctl->top > 2) { /* F11 */
sheet_updown(shtctl->sheets[1], shtctl->top - 1);
/*到此结束*/ }
(中略)
} else if (512 <= i && i <= 767) { /*鼠标数据*/
(中略)
} else if (i <= 1) { /*光标用定时器*/
(中略)
}
}
}
}
24.2 窗口切换(2)
需要实现的功能:通过鼠标点击位置,判断哪个图层应该在最上面,举例点击第2层区间时将调整第2和3层的显示顺序。回顾一下,在文件bootpack.c文件中主函数中有对鼠标数据的判断,因此只需要在其中增加一个循环,检查究竟哪个图层应该在最上面。
void HariMain(void){
(中略)
int j, x, y; /*这里!*/
struct SHEET *sht; /*这里!*/
(中略)
for (;;) {
(中略)
if (fifo32_status(&fifo) == 0) {
(中略)
} else {
(中略)
if (256 <= i && i <= 511) { /*键盘数据*/
(中略)
} else if (512 <= i && i <= 767) { /*鼠标数据*/
if (mouse_decode(&mdec, i - 512) != 0) {
/*鼠标指针移动*/
(中略)
if ((mdec.btn & 0x01) != 0) {
/*按下左键*/
/*从此开始*/ /*按照从上到下的顺序寻找鼠标所指向的图层*/
for (j = shtctl->top - 1; j > 0; j--) {
sht = shtctl->sheets[j];
x = mx - sht->vx0;
y = my - sht->vy0;
if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
sheet_updown(sht, shtctl->top - 1);
break;
}
}
/*到此结束*/ }
}
}
} else if (i <= 1) { /*光标用定时器*/
(中略)
}
}
}
}
24.3 移动窗口
需要实现的功能:类似windows,当鼠标左键长按窗口标题时,可实现窗口的移动,松开左键时则不移动。思路:结合24.2节的代码,并且原函数中已实现了鼠标的实时坐标,仅需要记录移动前的鼠标位置,并设置一个标记,若鼠标左键长按标题位置时移动窗口,否则更新移动前鼠标位置。
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){
sheet_updown(sht, shtctl->top-1);
if(3 <= x && x < sht->bxsize-3 && 3<= y && y < 21){//判断是否在窗口的标题栏
mmx = mx;
mmy = my;
}
break;
}
}
}
}else{/*窗口移动模式*/
y = my - mmy;
x = mx - mmx;
sheet_slide(sht, sht->vx0+x, sht->vy0+y);
mmy = my;
mmx = mx;
}
}else{/*没有按下左键*/
mmx = -1;/*返回通常模式*/
}
24.4 用鼠标关闭窗口
需要实现的功能:点击窗口上的“x”按钮,关闭窗口。该功能可以参考点击窗口标题实现移动操作的代码,仅需在其中增加一个判断,并将按下组合键 Shift+F1 时强制关闭窗口的代码添加到上述判断中即可。
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){
sheet_updown(sht, shtctl->top-1);
if(3 <= x && x < sht->bxsize-3 && 3<= y && y < 21){//判断是否在窗口的标题栏
mmx = mx;
mmy = my;
}
if(sht->bxsize-21 <= x && x < sht->bxsize-5 && 5<= y && y < 19){//点击"x"关闭窗口
if(sht->task != 0){/*该窗口是应用程序窗口*/
cons = (struct CONSOLE *) *((int *) 0x0fec);
cons_putstr0(cons, "\nBreak(mouse) :\n");
io_cli();
task_cons->tss.eax = (int)&(task_cons->tss.esp0);
task_cons->tss.eip = (int) asm_end_app;
io_sti();
}
}
break;
}
}
}
}
24.5 将输入切换到应用程序窗口
需要实现的功能:按下Tab键时将键盘输入切换到当前输入窗口下面一层的窗口中,若当前窗口为最下层,则切换到最上层窗口。
对于键盘输入的控制,之前用的是
key_to这个变量,改成使用key_win这个变量存放当前处于输入模式的窗口地址。
//改变窗口标题栏的颜色
void change_wtitle8(struct SHEET *sht, char act){
int x, y, xsize = sht->bxsize;
char c, tc_new, tbc_new, tc_old, tbc_old, *buf = sht->buf;
if(act){
tc_new = COL8_FFFFFF;
tbc_new = COL8_000084;
tc_old = COL8_C6C6C6;//标题栏颜色
tbc_old = COL8_848484;//背景色
}else{
tc_new = COL8_C6C6C6;
tbc_new = COL8_848484;
tc_old = COL8_FFFFFF;
tbc_old = COL8_000084;
}
for(y = 3; y <= 20; y++){
for(x = 3; x <= xsize-4; x++){
c = buf[y*xsize+x];
if(c == tc_old && x <= xsize-22)
c = tc_new;
else if(c == tbc_old)
c = tbc_new;
buf[y*xsize+x] = c;
}
}
sheet_refresh(sht, 3, 3, xsize, 21);
return;
}
//控制窗口标题栏的颜色和task_a窗口的光标
int keywin_off(struct SHEET *key_win, struct SHEET *sht_win, int cur_c, int cur_x){
change_wtitle8(key_win, 0);
if(key_win == sht_win){
cur_c = -1;/*删除光标*/
boxfill8(sht_win->buf, sht_win->bxsize, COL8_FFFFFF, cur_x, 28, cur_x+7, 43);
}else{
if((key_win->flags&0x20) != 0)
fifo32_put(&key_win->task->fifo, 3); /*命令行窗口光标OFF */
}
return cur_c;
}
int keywin_on(struct SHEET *key_win, struct SHEET *sht_win, int cur_c){
change_wtitle8(key_win, 1);
if(key_win == sht_win){
cur_c = COL8_000000;/*显示光标*/
}else{
if((key_win->flags&0x20) != 0)
fifo32_put(&key_win->task->fifo, 2); /*命令行窗口光标ON */
}
return cur_c;
}
/* Harimain函数中的部分更新代码 */
if(i == 256 + 0x0f){/* 按下Tab键后,循环遍历所有窗口,使得当前窗口的颜色与其他窗口颜色不同*/
cursor_c = keywin_off(key_win, sht_win, cursor_c, cursor_x);
j = key_win->height-1;
if(j == 0)j = shtctl->top-1;
key_win = shtctl->sheets[j];
cursor_c = keywin_on(key_win, sht_win, cursor_c);
}
24.6 用鼠标切换输入窗口
需要实现的功能:当鼠标选中某一窗口的标题,使该变成最上面窗口,并调整标题颜色。按照24.5节中的思路,只需要在接收鼠标数据时增加一个选中的窗口是否为最上面的窗口判断,若不是则调整标题颜色(鼠标点击标题成为最上面窗口的功能已经在第24.2节中实现)。
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){
sheet_updown(sht, shtctl->top-1);
/*从这里开始*/ if(key_win != sht){
cursor_c = keywin_off(key_win, sht_win, cursor_c, cursor_x);/*调整原key_win的图层光标为OFF*/
key_win = sht;
cursor_c = keywin_on(key_win, sht_win, cursor_c);/*调整刚被选中的图层光标为ON*/
/*到这里结束*/ }
if(3 <= x && x < sht->bxsize-3 && 3<= y && y < 21){//判断是否在窗口的标题栏
mmx = mx;
mmy = my;
}
(中略)
}
}
}
}
24.7 定时器API
获取定时器(alloc):EDX=16 EAX=定时器句柄(由操作系统返回)
设置定时器的发送数据(init):EDX=17 EBX=定时器句柄 EAX=数据
定时器时间设定(set):EDX=18 EBX=定时器句柄 EAX=时间
释放定时器(free):EDX=19 EBX=定时器句柄
按照上述的设定,先在a_nask.nas文件中增加相关函数的汇编程序,然后在console.c文件中增加判断,最后写一个程序调用定时器相关函数。
/* a_nask.nas */
_api_alloctimer: ;int api_alloctimer(void);
MOV EDX,16
INT 0X40;
RET
_api_inittimer: ;void api_inittimer(int timer, int data);
PUSH EBX
MOV EDX,17
MOV EBX,[ESP+8] ;timer
MOV EAX,[ESP+12] ;data
INT 0X40;
POP EBX
RET
_api_settimer: ;void api_settimer(int timer, int time);
PUSH EBX
MOV EDX,18
MOV EBX,[ESP+8] ;timer
MOV EAX,[ESP+12] ;time
INT 0X40;
POP EBX
RET
_api_freetimer: ;void api_freetimer(int timer);
PUSH EBX
MOV EDX,19
MOV EBX,[ESP+8] ;timer
INT 0X40;
POP EBX
RET
/* console.c文件中的hrb_api函数增加部分 */
else if(edx == 16){//分配计时器
reg[7] = (int)timer_alloc();//reg[7]=eax
}else if(edx == 17){//初始化计时器
//是因为在向应用程序传递FIFO数据时,需要先减去256。
timer_init((struct TIMER *)ebx, &task->fifo, eax+256);
}else if(edx == 18){//设置计时器
timer_settime((struct TIMER *)ebx, eax);
}else if(edx == 19){//释放计时器
timer_free((struct TIMER *)ebx);
}
24.8 取消定时器
需要实现的功能:在应用程序结束的同时取消由应用程序分配的定时器。由于定时器只有一个标记状态的flags,没有是谁设置的定时器的标记,所以在TIMER结构体中增加flags2(当是应用程序申请的定时器,将flags2置为1)。
timer_alloc函数中对flags2进行初始化,初始值为0。hrb_api中当edx==16时,表明应用程序申请了一个定时器,所以需要将flags2置为1,即((struct TIMER *) reg[7])->flags2 = 1。并在释放图层后,清除所有由应用程序分配的定时器。
/* timer.c */
int timer_cancel(struct TIMER *timer){//删除计时器timer
int e;
struct TIMER *t;
e = io_load_eflags();
io_cli();/*在设置过程中禁止改变定时器状态*/
if(timer->flags == TIMER_FLAGS_USING){
if(timerctl.t0 == timer){/*第一个定时器的取消处理*/
t = timer->next;
timerctl.t0 = t;
timerctl.next = t->timeout;
}else{
/*非第一个定时器的取消处理*/
/*找到timer前一个定时器*/
t = timerctl.t0;
for(;;){
if(t->next == timer)
break;
t = t->next;
}
}
t->next = timer->next;
timer->flags = TIMER_FLAGS_ALLOC;
io_store_eflags(e);
return 1;
}
io_store_eflags(e);
return 0; /*不需要取消处理*/
}
void timer_cancelall(struct FIFO32 *fifo){//删除所有使用fifo内存的计时器
int e, i;
struct TIMER *t;
e = io_load_eflags();
io_cli();
for(i = 0; i < MAX_TIMER; i++){
t = &timerctl.timers0[i];
// 删除正在运行的、且由应用程序创建的计时器
if(t->flags != 0 && t->flags2 != 0 && t->fifo == fifo){
timer_cancel(t);
timer_free(t);
}
}
io_store_eflags(e);
return;
}
/* console.c */
if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
(中略)
start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));
shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
for (i = 0; i < MAX_SHEETS; i++) {
sht = &(shtctl->sheets0[i]);
if ((sht->flags & 0x11) == 0x11 && sht->task == task) {
/*找到应用程序残留的窗口*/
sheet_free(sht); /*关闭*/
}
}
timer_cancelall(&task->fifo); /*这里!*/
memman_free_4k(memman, (int) q, segsiz);
} else {
(中略)
}