30天开辟操作系统 第24天 -- 窗口操作

打印 上一主题 下一主题

主题 905|帖子 905|积分 2715

一、窗口切换

1.0

前天开始我们的应用程序可以表现本身的窗口了,如今画面上随处都是窗口,我们急需能够 切换窗口顺序的功能,使得在需要的时候可以查 看最下面的窗口的内容。这个功能看起来不难,我们立刻来实现它。 不过,一上来就实现“点击鼠标切换窗口”的功能另有点难,以是我们先从用键盘切换的方法入手吧,即按下F11时,将最下面的谁人窗口放到最上面。
要实现这一功能我们首先需要知道F11的按键编码:F11的按键编码为0x57(F12为0x58)。知道了这些,接下来只要稍微修改下bootpack.c即可 ,只需要添加3行代码。
  1. void HariMain(void)
  2. {
  3.         ...
  4.         if (i == 256 + 0x57 && shtctl->top > 2) {        /* F11 */
  5.                                         sheet_updown(shtctl->sheets[1], shtctl->top - 1);
  6.                                 }
  7.         ...
  8. }
复制代码
恐怕没大家都记不清图层的操作方法了吧,我们还是稍微详细地讲解一下。 sheet_updown (shtct1->sheets [1],shtct1->top -1); 这句代码的功能是将从下面数第2个图层(最下面一个图层shtctl->sheets[O]是配景)的高度 提拔为shtctl →>top- 1。 Shtctl ->top这个高度存放的是最上面一个图层的高度,这个图层永世是绘制鼠标指针用的,我们不能将窗口放在比鼠标还高的位置上(搞个恶作剧倒是挺有趣的),因此将窗口高度设置为鼠标图层的下面一层。
我们来试试看能不能成功,-- “make run" – 运行成功了!

2.0

这次我们来实现Windows那样的用鼠标点击来切换窗口的功能。这个功能会让task_a中窗口移动的功能失效,不过不要紧,我们在下一节会重新编写窗口移动功能的,大家不必担心。 当鼠标点击画面的某个地方时,怎样才能知道鼠标所点击到的是哪个图层呢?
我们需要按照从上到下的顺序,判定鼠标的位置落在哪个图层的范围内,并且还需要确保该位置不是透明色地区。
  1. void HariMain(void)
  2. {
  3.         int j, x, y;        /*这里!*/
  4.         struct SHEET *sht;        /*这里!*/
  5.         ...
  6.         for (;;) {
  7.                 ...
  8.                 if ((mdec.btn & 0x01) != 0) {
  9.                                                         /* 按下左键 */
  10.                                                         /* 按照从上到下的顺序寻找鼠标所指向的图层 */
  11.                                                         /*从此开始*/
  12.                                                         for (j = shtctl->top - 1; j > 0; j--) {
  13.                                                                 sht = shtctl->sheets[j];
  14.                                                                 x = mx - sht->vx0;
  15.                                                                 y = my - sht->vy0;
  16.                                                                 if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
  17.                                                                         if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
  18.                                                                                 sheet_updown(sht, shtctl->top - 1);
  19.                                                                                 break;
  20.                                                                         }
  21.                                                                 }
  22.                         /*到此结束*/                }
  23.                                                 }
  24.                                         }
  25.                 ...
  26. }
复制代码
make run 然后运行lines.hrb, 我们来点击一个窗口试试看。哦!成功了!

如果点击下令行窗口将其切换到最上面的话,别的窗口就全都被遮住了,这样无法用鼠标点击切换了,还好我们可以按F11将被遮住的窗口切换出来,F11这个功能还真是挺方便的呢。
2 移动窗口

窗口切换的功能已经做的差不多了,这次我们来实现窗口的移动。之前我们只能移动task_ a的窗口,这次的目标是实现像Windows一样的窗口移动功能。不过要如何才能实现呢? 当鼠标左键点击窗口时,如果点击位置位于窗口的标题栏地区,则进入“窗口移动模式” 使窗口的位置追随鼠标指针的移动,当放开鼠标左键时,退出“窗口移动模式”,返回通常模式。 要实现窗口的移动,我们需要记录鼠标指针所移动的间隔,为此我们添加了两个变量:mmx和mmy,这两个变量所记录的是移动之前的坐标。由于鼠标指针mm是“move mode”的缩写,因此我们规定当mmx为负数时代表当前不处于窗口移动模式。 由于鼠标不会跑到画面以外,因此我们规定当mmx为负数时代表当前不处于窗口移动模式。
  1. void HariMain(void)
  2. {
  3.         ...
  4.         int j, x, y, mmx = -1, mmy = -1;
  5.         ...
  6.         for (;;) {
  7.                 ...
  8.                 /*从此开始*/
  9.                 if ((mdec.btn & 0x01) != 0) {
  10.                                                         /* 按下左键 */
  11.                                                         if (mmx < 0) {
  12.                                                                 /* 如果处于通常模式 */
  13.                                                                 /* 按照从上到下的顺序寻找鼠标所指向的图层 */
  14.                                                                 for (j = shtctl->top - 1; j > 0; j--) {
  15.                                                                         sht = shtctl->sheets[j];
  16.                                                                         x = mx - sht->vx0;
  17.                                                                         y = my - sht->vy0;
  18.                                                                         if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
  19.                                                                                 if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
  20.                                                                                         sheet_updown(sht, shtctl->top - 1);
  21.                                                                                         if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {
  22.                                                                                                 mmx = mx;        /* 进入窗口移动模式 */
  23.                                                                                                 mmy = my;
  24.                                                                                         }
  25.                                                                                         break;
  26.                                                                                 }
  27.                                                                         }
  28.                                                                 }
  29.                                                         } else {
  30.                                                                 /* 如果处于窗口移动模式 */
  31.                                                                 x = mx - mmx;        /* 计算鼠标的移动距离 */
  32.                                                                 y = my - mmy;
  33.                                                                 sheet_slide(sht, sht->vx0 + x, sht->vy0 + y);
  34.                                                                 mmx = mx;        /* 更新为移动后的坐标 */
  35.                                                                 mmy = my;
  36.                                                         }
  37.                                                 } else {
  38.                                                         /* 没有按下左键 */
  39.                                                         mmx = -1;        /* 返回通常模式 */
  40.                                                 }
  41.                                         }
  42. }
复制代码
固然代码有点长,不过静下心来仔细读的话,大家一定能看懂的。 我们来 make run 试试看,能不能成功呢……成功了! 不过下令行窗口的移动速率太慢了!这大概是QEMU的原因,在真机情况下窗口的移动一定可以到达可接受的速率。
3 用鼠标关闭窗口

如今我们已经实现了窗口的移动,这次就来实现关闭窗口的功能吧。我们的窗口上面已经有 了一个“x”按钮,看到它大家都想按一下吧(笑)? 判定是否点击了“x”按钮的方法,和之前窗口移动时判定是否点击到标题栏的方法是一样的,而且点击后的程序竣事处理,也可以参考强制竣事部门的代码。嗯,这样看上去还挺容易的。 我们添加了11行代码,这样应该可以搞定了。
  1. void HariMain(void)
  2. {
  3.         ...
  4.         for (;;) {
  5.         ...
  6.                 if ((mdec.btn & 0x01) != 0) {
  7.                 ...
  8.                         if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {
  9.                                                                                                         /*点击“X”按钮*/
  10.                                                                                                         if (sht->task != 0) {        /*该窗口是否为应用程序窗口*/
  11.                                                                                                                 cons = (struct CONSOLE *) *((int *) 0x0fec);
  12.                                                                                                                 cons_putstr0(cons, "\nBreak(mouse) :\n");
  13.                                                                                                                 io_cli();        /*强制结束处理中禁止切换任务*/
  14.                                                                                                                 task_cons->tss.eax = (int) &(task_cons->tss.esp0);
  15.                                                                                                                 task_cons->tss.eip = (int) asm_end_app;
  16.                                                                                                                 io_sti();
  17.                                                                                                         }
  18.                                                                                                 }
  19.                                                                                                 break;
  20.         ...
  21. }
复制代码
将上面的程序“make run” 结果如下:

4 将输入切换到应用程序窗口

固然我们已经实现了像walk.hrb这样让应用程序接受键盘输入的功能,不过仔细看画面会发现,处于输入状态的其实是下令行窗口,而不是walk的窗口。

这样看上去有点怪,应该先让应用程序窗口处于输入状态,然后再操作“*” 举行移动。 之前我们所使用的Tab键切换很简单,只能在两个窗口之间交替举行,但这次我们已经表现出了3个以上的窗口,情况变得有点复杂,在按下Tab键时,我们需要判定切换到哪个窗口。
我们先这样规定吧:按下Tab键时将键盘输入切换到当前输入窗口下面一层的窗口中,若当前窗口为 最下层,则切换到最上层窗口。 对于键盘输入的控制我们之前用的是key_to这个变量,不过这已经是很久以前写的了(应该但感觉似乎已颠末了很久了呢),这个方法是在17天中写的,如今已经不能用了,于是我们改成使用key_win这个变量存放当前处于输人模式的窗口地址。
另外,另有一个题目,如果当应用程序窗口处于输入模式时被关闭的话要怎样处理呢?这个时候我们可以让系统自动切换到最上层的窗口。 本次修改的内容较多。重要修改的地方是将HariMain中key_to的地方改为key_win,以及添加在输入窗口消散时(被关闭时)举行处理的代码。
有个细节需要讲一下,我们用SHEET布局中的task成员来判定数据发送对象的FIFO,因此在 sht cons->task中也加入了TASK布局的地址,这样的话我们就无法分辨窗口是不是由应用程序生 成的,于是我们需要通过SHEET布局中的flags成员举行判定(以0x10比特位举行区分)。此外, 只有下令行窗口需要控制光标的ON/OFF,应用程序窗口不需要,这一区别也是通过flags来举行 判定的(以0x20比特位举行区分)。
  1. void HariMain(void)
  2. {
  3.         ...
  4.         struct SHEET *sht = 0, *key_win;        /*这里!*/
  5.         ...
  6.         /*从此开始*/
  7.         key_win = sht_win;
  8.         sht_cons->task = task_cons;
  9.         sht_cons->flags |= 0x20;        /*有光标*/
  10. /*到此结束*/
  11.         if (key_win->flags == 0) {        /* 输入窗口被关闭 */
  12.                                 key_win = shtctl->sheets[shtctl->top - 1];
  13.                                 cursor_c = keywin_on(key_win, sht_win, cursor_c);
  14.                         }
  15.         ...
  16.         else {
  17.                         i = fifo32_get(&fifo);
  18.                         io_sti();
  19.                         if (key_win->flags == 0) {        /* 输入窗口被关闭 */ /*这里!*/
  20.                                 key_win = shtctl->sheets[shtctl->top - 1];
  21.                                 cursor_c = keywin_on(key_win, sht_win, cursor_c);
  22.                         }
  23.                         if (256 <= i && i <= 511) { /* 键盘数据 */
  24.                                 if (i < 0x80 + 256) {
  25.                                         if (key_shift == 0) {
  26.                                                 s[0] = keytable0[i - 256];
  27.                                         } else {
  28.                                                 s[0] = keytable1[i - 256];
  29.                                         }
  30.                                 } else {
  31.                                         s[0] = 0;
  32.                                 }
  33.                                 if ('A' <= s[0] && s[0] <= 'Z') {       
  34.                                         if (((key_leds & 4) == 0 && key_shift == 0) ||
  35.                                                         ((key_leds & 4) != 0 && key_shift != 0)) {
  36.                                                 s[0] += 0x20;       
  37.                                         }
  38.                                 }
  39.                                 if (s[0] != 0) { /* 一般字符 */
  40.                                         if (key_win == sht_win) {        /* 发送至任务A */
  41.                                                 if (cursor_x < 128) {
  42.                                                         /* 显示一个字符并将光标后移一位 */
  43.                                                         s[1] = 0;
  44.                                                         putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, s, 1);
  45.                                                         cursor_x += 8;
  46.                                                 }
  47.                                         } else {        /* 发送至命令行窗口 */
  48.                                                 fifo32_put(&key_win->task->fifo, s[0] + 256);
  49.                                         }
  50.                                 }
  51.                                 if (i == 256 + 0x0e) {        /* 退格键 */
  52.                                         if (key_win == sht_win) {        /* 发送至任务A */
  53.                                                 if (cursor_x > 8) {
  54.                                                         /* 用空格擦除光标后将光标前移一位 */
  55.                                                         putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, " ", 1);
  56.                                                         cursor_x -= 8;
  57.                                                 }
  58.                                         } else {        /* 发送至命令行窗口 */
  59.                                                 fifo32_put(&key_win->task->fifo, 8 + 256);
  60.                                         }
  61.                                 }
  62.                                 if (i == 256 + 0x1c) {        /* Enter */
  63.                                         if (key_win != sht_win) {        /* 发送至命令行窗口 */
  64.                                                 fifo32_put(&key_win->task->fifo, 10 + 256);
  65.                                         }
  66.                                 }
  67.                                 if (i == 256 + 0x0f) {        /* Tab */
  68.                                         cursor_c = keywin_off(key_win, sht_win, cursor_c, cursor_x);
  69.                                         j = key_win->height - 1;
  70.                                         if (j == 0) {
  71.                                                 j = shtctl->top - 1;
  72.                                         }
  73.                                         key_win = shtctl->sheets[j];
  74.                                         cursor_c = keywin_on(key_win, sht_win, cursor_c);
  75.                                 }/*到此结束*/
  76.                                 ...
  77.                                 else if (512 <= i && i <= 767) { /* 鼠标数据 */
  78.                                 if (mouse_decode(&mdec, i - 512) != 0) {
  79.                                         mx += mdec.x;
  80.                                         my += mdec.y;
  81.                                         if (mx < 0) {
  82.                                                 mx = 0;
  83.                                         }
  84.                                         if (my < 0) {
  85.                                                 my = 0;
  86.                                         }
  87.                                         if (mx > binfo->scrnx - 1) {
  88.                                                 mx = binfo->scrnx - 1;
  89.                                         }
  90.                                         if (my > binfo->scrny - 1) {
  91.                                                 my = binfo->scrny - 1;
  92.                                         }
  93.                                         sheet_slide(sht_mouse, mx, my);
  94.                                         if ((mdec.btn & 0x01) != 0) {
  95.                                                 /* 按下左键 */
  96.                                                 if (mmx < 0) {
  97.                                                         /* 如果处于通常模式 */
  98.                                                         /* 按照从上到下的顺序寻找鼠标所指向的图层 */
  99.                                                         for (j = shtctl->top - 1; j > 0; j--) {
  100.                                                                 sht = shtctl->sheets[j];
  101.                                                                 x = mx - sht->vx0;
  102.                                                                 y = my - sht->vy0;
  103.                                                                 if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
  104.                                                                         if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
  105.                                                                                 sheet_updown(sht, shtctl->top - 1);
  106.                                                                                 if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {
  107.                                                                                         mmx = mx;       
  108.                                                                                         mmy = my;
  109.                                                                                 }
  110.                                                                                 if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {
  111.                                                                                         /*点击“X”按钮*/
  112.                                                                                         /*这里!*/if ((sht->flags & 0x10) != 0) {                /*是由应用程序生成的窗口*/
  113.                                                                        
  114. }
复制代码
上面的代码中调用了keywin_on和keywin_off两个函数,它们的功能是控制窗口标题栏的颜色 和task_a窗口的光标,我们将它们写在bootpack.c中。
  1. int keywin_off(struct SHEET *key_win, struct SHEET *sht_win, int cur_c, int cur_x)
  2. {
  3.         change_wtitle8(key_win, 0);
  4.         if (key_win == sht_win) {
  5.                 cur_c = -1; /* 删除光标 */
  6.                 boxfill8(sht_win->buf, sht_win->bxsize, COL8_FFFFFF, cur_x, 28, cur_x + 7, 43);
  7.         } else {
  8.                 if ((key_win->flags & 0x20) != 0) {
  9.                         fifo32_put(&key_win->task->fifo, 3); /* 命令行窗口光标OFF */
  10.                 }
  11.         }
  12.         return cur_c;
  13. }
  14. int keywin_on(struct SHEET *key_win, struct SHEET *sht_win, int cur_c)
  15. {
  16.         change_wtitle8(key_win, 1);
  17.         if (key_win == sht_win) {
  18.                 cur_c = COL8_000000; /* 显示光标 */
  19.         } else {
  20.                 if ((key_win->flags & 0x20) != 0) {
  21.                         fifo32_put(&key_win->task->fifo, 2); /* 命令行窗口光标ON */
  22.                 }
  23.         }
  24.         return cur_c;
  25. }
复制代码
上面的代码中我们调用了一个叫做change_wtitle8的函数,这个函数的功能是改变窗口标题栏 的颜色,我们写在window.c中。其实make_wtitle8也可以实现相同的功能,但change_wtitle8的好处是,即便不知道窗口的名称也可以改变标题栏 的颜色。除此之外,代码的别的部门应该不需要讲解了吧。
  1. void change_wtitle8(struct SHEET *sht, char act)
  2. {
  3.         int x, y, xsize = sht->bxsize;
  4.         char c, tc_new, tbc_new, tc_old, tbc_old, *buf = sht->buf;
  5.         if (act != 0) {
  6.                 tc_new  = COL8_FFFFFF;
  7.                 tbc_new = COL8_000084;
  8.                 tc_old  = COL8_C6C6C6;
  9.                 tbc_old = COL8_848484;
  10.         } else {
  11.                 tc_new  = COL8_C6C6C6;
  12.                 tbc_new = COL8_848484;
  13.                 tc_old  = COL8_FFFFFF;
  14.                 tbc_old = COL8_000084;
  15.         }
  16.         for (y = 3; y <= 20; y++) {
  17.                 for (x = 3; x <= xsize - 4; x++) {
  18.                         c = buf[y * xsize + x];
  19.                         if (c == tc_old && x <= xsize - 22) {
  20.                                 c = tc_new;
  21.                         } else if (c == tbc_old) {
  22.                                 c = tbc_new;
  23.                         }
  24.                         buf[y * xsize + x] = c;
  25.                 }
  26.         }
  27.         sheet_refresh(sht, 3, 3, xsize, 21);
  28.         return;
  29. }
复制代码
我们对cmd_app也举行了修改,重要修改了应用程序竣事时自动关闭窗口的部门。因为没有运行应用程序的下令行窗口,其task也不为0,以是需要通过flags的0x10比特位来判定是否自动关闭。
  1. int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
  2. {
  3.         ...
  4.         if (finfo != 0) {
  5.                 ...
  6.                 if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
  7.                 ...
  8.                 for (i = 0; i < MAX_SHEETS; i++) {
  9.                                 sht = &(shtctl->sheets0[i]);
  10.                                 if ((sht->flags & 0x11) == 0x11 && sht->task == task) {
  11.                                         /* 找到应用程序残留的窗口 */
  12.                                         sheet_free(sht);        /* 关闭 */
  13.                                 }
  14.                         }
  15.                         memman_free_4k(memman, (int) q, segsiz);
  16.                 } else {
  17.                         cons_putstr0(cons, ".hrb file format error.\n");
  18.                 }
复制代码
我们还修改了hrb_api,为了在打开窗口的地方启用自动关闭窗口的功能,我们将flags和 0x10 举行OR运算。
  1. int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
  2. {
  3.         ...
  4.         } else if (edx == 5) {
  5.                 sht = sheet_alloc(shtctl);
  6.                 sht->task = task;
  7.                 sht->flags |= 0x10;        /*这里!*/
  8.                 sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);
  9.                 make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);
  10.                 sheet_slide(sht, 100, 50);
  11.                 sheet_updown(sht, 3);        /* 图层高度3位于task_a之上 */
  12.                 reg[7] = (int) sht;
  13.         } else if (edx == 6) {
复制代码
下面轮到例行的 make run ,成功了!
5 用鼠标切换输入窗口

我们已经实现了输入窗口的切换,着实进步不小,只是用Tab键来举行切换的操作和 Windows 还不一样。在Windows中,只要用鼠标在窗口上点击一下,谁人窗口就会被切换到画面的最上方, 而且键盘输入也会自动切换到该窗口。因此,我们的操作系统也可以通过简单的点击就能完成输入切换。
刚才为了实现用Tab键切换,我们修改了大量的代码,而这次我们只需要添加一点点代码就好了,因为只需要通过鼠标操作实现和键盘操作相同的功能而已。
  1. void HariMain(void)
  2. {
  3.         ...
  4.         for (;;) {
  5.                 ...
  6.                         if ((mdec.btn & 0x01) != 0) {
  7.                                                 /* 按下左键 */
  8.                                                 if (mmx < 0) {
  9.                                                         /* 如果处于通常模式 */
  10.                                                         /* 按照从上到下的顺序寻找鼠标所指向的图层 */
  11.                                                         for (j = shtctl->top - 1; j > 0; j--) {
  12.                                                                 sht = shtctl->sheets[j];
  13.                                                                 x = mx - sht->vx0;
  14.                                                                 y = my - sht->vy0;
  15.                                                                 if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
  16.                                                                         if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
  17.                                                                                 sheet_updown(sht, shtctl->top - 1);
  18.                                         /*从此开始*/                                        if (sht != key_win) {
  19.                                                                                         cursor_c = keywin_off(key_win, sht_win, cursor_c, cursor_x);
  20.                                                                                         key_win = sht;
  21.                                                                                         cursor_c = keywin_on(key_win, sht_win, cursor_c);
  22.                                         /*到此结束*/                                        }
  23.                                                                                 if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {
  24.                                                                                         mmx = mx;        /* 进入窗口移动模式 */
  25.                                                                                         mmy = my;
  26.                                                                                 }
  27.                                                                                 if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {
  28.                                                                                         /* X */
  29.                                                                                         ...
  30.                                                                                 break;
  31.                                                                         }
  32.                                                                 }
复制代码
“make run” , 点击一下应用程序窗口试试看吧。

6 定时器

API 到如今为止,筹划本日要讲的关于窗口操作功能的部门已经全部完成了,不过如今时间还早,我们来做点好玩的吧。本日我们还没有写过新的 API,那如今我们就来做个新的API吧,比如说,定时器的AP1。
记得在12天中我们实现了用定时器来计时的功能,当时我们高兴了半天,因为我们的操作系统可以用来泡面了。不过如今我们的操作系统 又回到了派不上什么用场的状态,即便可以表现窗口、画点、画直线,但它却不能帮我们解决现实题目呀。
因此,接下来我们打算让应用程序也可以使用定时器,然后就可以去吃碗泡面了。大家先把 想吃的泡面另有开水准备好,趁这段时间我先来写程序了哦! 这次要编写的API如下:
获取定时器(alloc)
EDX=16
EAX=定时器句柄(由操作系统返回)
设置定时器的发送数据(init)
EDX=17
EBX=定时器句柄
EAX=数据
定时器时间设定(set)
EDX=18
EBX=定时器句柄
EAX=时间
释放定时器(free)
EDX=19
EBX=定时器句柄
  1. int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
  2. {
  3.         ...
  4.         } else if (edx == 15) {
  5.                 for (;;) {
  6.                         ...
  7.                         if (i >= 256) { /*键盘数据(通过任务A)等*/
  8.                                 reg[7] = i - 256;
  9.                                 return 0;
  10.                         }
  11.                 }
  12.                 /*从此开始*/
  13.         } else if (edx == 16) {
  14.                 reg[7] = (int) timer_alloc();
  15.         } else if (edx == 17) {
  16.                 timer_init((struct TIMER *) ebx, &task->fifo, eax + 256);
  17.         } else if (edx == 18) {
  18.                 timer_settime((struct TIMER *) ebx, eax);
  19.         } else if (edx == 19) {
  20.                 timer_free((struct TIMER *) ebx);
  21.         }
  22.         /*到此结束*/
  23.         return 0;
  24. }
复制代码
edx的取值为16~19,这个很简单,应该不用讲了。哦对了,edx=17的情况需要稍微说一下,这里数据的编号加上了256,是因为在向应用程序通报FIFO数据时,需要先减去256。
此外,api_getkey,也就是edx=15的部门也稍微修改了一下。之前我们写的是计(256 <= I && I <= 511),而如今修改成了if(I >= 256,就是说,512以上的值也可以通过api_getkey来获取了。之以是要这样改,是因为如今应用程序不仅需要接收键盘的数据,还需要接收应用程序所设置的 定时器发生超时时所通报的数据。
下面我们来编写应用程序:
  1. _api_alloctimer:        ; int api_alloctimer(void);
  2.                 MOV                EDX,16
  3.                 INT                0x40
  4.                 RET
  5. _api_inittimer:                ; void api_inittimer(int timer, int data);
  6.                 PUSH        EBX
  7.                 MOV                EDX,17
  8.                 MOV                EBX,[ESP+ 8]                ; timer
  9.                 MOV                EAX,[ESP+12]                ; data
  10.                 INT                0x40
  11.                 POP                EBX
  12.                 RET
  13. _api_settimer:                ; void api_settimer(int timer, int time);
  14.                 PUSH        EBX
  15.                 MOV                EDX,18
  16.                 MOV                EBX,[ESP+ 8]                ; timer
  17.                 MOV                EAX,[ESP+12]                ; time
  18.                 INT                0x40
  19.                 POP                EBX
  20.                 RET
  21. _api_freetimer:                ; void api_freetimer(int timer);
  22.                 PUSH        EBX
  23.                 MOV                EDX,19
  24.                 MOV                EBX,[ESP+ 8]                ; timer
  25.                 INT                0x40
  26.                 POP                EBX
  27.                 RET
复制代码
  1. // noodle.c
  2. #include <stdio.h>
  3. int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
  4. void api_putstrwin(int win, int x, int y, int col, int len, char *str);
  5. void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
  6. void api_initmalloc(void);
  7. char *api_malloc(int size);
  8. int api_getkey(int mode);
  9. int api_alloctimer(void);
  10. void api_inittimer(int timer, int data);
  11. void api_settimer(int timer, int time);
  12. void api_end(void);
  13. void HariMain(void)
  14. {
  15.         char *buf, s[12];
  16.         int win, timer, sec = 0, min = 0, hou = 0;
  17.         api_initmalloc();
  18.         buf = api_malloc(150 * 50);
  19.         win = api_openwin(buf, 150, 50, -1, "noodle");
  20.         timer = api_alloctimer();
  21.         api_inittimer(timer, 128);
  22.         for (;;) {
  23.                 sprintf(s, "%5d:%02d:%02d", hou, min, sec);
  24.                 api_boxfilwin(win, 28, 27, 115, 41, 7 /* 白色 */);
  25.                 api_putstrwin(win, 28, 27, 0 /* 黑色 */, 11, s);
  26.                 api_settimer(timer, 100);        /* 1秒 */
  27.                 if (api_getkey(1) != 128) {
  28.                         break;
  29.                 }
  30.                 sec++;
  31.                 if (sec == 60) {
  32.                         sec = 0;
  33.                         min++;
  34.                         if (min == 60) {
  35.                                 min = 0;
  36.                                 hou++;
  37.                         }
  38.                 }
  39.         }
  40.         api_end();
  41. }
复制代码
关于noodle.c这里稍微讲解一下,之前操作系统表现的是秒,而如今我们需要表现时、分、秒,这样一来时间看起来会更直观,我们就不需要用泡面的3分钟再乘以60换算成秒了。
当定时器超时时,会产生128这样个值,这个值不是由键盘的编码所使用的,因此除了定时器,别的变乱不可能产生这个值。如果产生的数据是128以外的值,那一定是用户按了回车键或者其他什么键,这时应用程序竣事退出。
开水准备好了吗? make run 然后运行 noodle.hrb 先往泡面里面倒好开水,3分钟到了,可以吃喽!

7 取消定时器

我们所实现的定时器功能,其实有个题目,接下来我们来解决它。 这个题目是在应用程序竣事之后发生的,请大家想象一下noodle.hrb竣事之后的情况。应用程序设置了一个1秒的定时器,当定时器到达指定时间时会产生超时,并向任务发送事先设置的数据。
题目是,如果这时应用程序已经竣事了,定时器的数据就会被发送到下令行窗口,而下令行窗口肯定是一头雾水。 为了确认这个题目,我们在harib21g中运行noodle.hrb,按回车键或者其他恣意键竣事程序看看。大约1秒钟之后,下令行窗口中会自动出现一个神秘的字符。用鼠标按“x”关闭窗口之后,也会出现同样的现象。 要解决这个题目,我们需要取消待机中的定时器,这样一来,就可以在应用程序竣事的同时取消定时器,题目也就迎刃而解了。 首先我们来编写用于取消指定定时器的函数:
  1. // timer.c
  2. int timer_cancel(struct TIMER *timer)
  3. {
  4.         int e;
  5.         struct TIMER *t;
  6.         e = io_load_eflags();
  7.         io_cli();        /* 在设置过程中禁止改变定时器状态 */
  8.         if (timer->flags == TIMER_FLAGS_USING) {        /* 是否需要取消? */
  9.                 if (timer == timerctl.t0) {
  10.                         /* 第一个定时器的取消处理 */
  11.                         t = timer->next;
  12.                         timerctl.t0 = t;
  13.                         timerctl.next = t->timeout;
  14.                 } else {
  15.                         /* 非第一个定时器的取消处理 */
  16.                         /* 找到timer前一个定时器 */
  17.                         t = timerctl.t0;
  18.                         for (;;) {
  19.                                 if (t->next == timer) {
  20.                                         break;
  21.                                 }
  22.                                 t = t->next;
  23.                         }
  24.                         t->next = timer->next; /* 将之前“timer的下一个”指向“timer的下一个” */
  25.                 }
  26.                 timer->flags = TIMER_FLAGS_ALLOC;
  27.                 io_store_eflags(e);
  28.                 return 1;        /* 取消处理成功 */
  29.         }
  30.         io_store_eflags(e);
  31.         return 0; /* 不需要取消处理 */
  32. }
复制代码
详细的解说已经写在程序的注释中了,大家本身理解一下哦。 接下来,我们来编写在应用程序竣事时取消全部定时器的函数。在此之前,我们需要在定时 器上增加一个标记,用来区分该定时器是否需要在应用程序竣事时自动取消。如果没有这个标记 的话,下令行窗口中用来控制光标闪灼的定时器也会被取消掉了。
  1. struct TIMER {
  2.         struct TIMER *next;
  3.         unsigned int timeout;
  4.         char flags, flags2; /* 这里 */
  5.         struct FIFO32 *fifo;
  6.         int data;
  7. };
复制代码
为了避免忘记置0, 通常情况下, 这里的flags2为0, 我们来修改一下timer.alloc。
  1. struct TIMER *timer_alloc(void)
  2. {
  3.         int i;
  4.         for (i = 0; i < MAX_TIMER; i++) {
  5.                 if (timerctl.timers0[i].flags == 0) {
  6.                         timerctl.timers0[i].flags = TIMER_FLAGS_ALLOC;
  7.                         timerctl.timers0[i].flags2 = 0;        /* 这里 */
  8.                         return &timerctl.timers0[i];
  9.                 }
  10.         }
  11.         return 0; /* 没有找到 */
  12. }
复制代码
接下来,我们将应用程序所申请的定时器的fags2设为1。
  1. int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
  2. {
  3.         ...
  4.         if (finfo != 0) {
  5.                 ...
  6.                 if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
  7.                 ...
  8.                                         start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));
  9.                         shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
  10.                         for (i = 0; i < MAX_SHEETS; i++) {
  11.                                 sht = &(shtctl->sheets0[i]);
  12.                                 if ((sht->flags & 0x11) == 0x11 && sht->task == task) {
  13.                                         /* 找到应用程序残留的窗口 */
  14.                                         sheet_free(sht);        /* 关闭 */
  15.                                 }
  16.                         }
  17.                         timer_cancelall(&task->fifo);        /* 这里 */
  18.                         memman_free_4k(memman, (int) q, segsiz);
  19.                 } else {
  20.                 ...
复制代码
准备完成了,下面我们就编写一个函数,来取消应用程序竣事时所不需要的定时器。
  1. void timer_cancelall(struct FIFO32 *fifo)
  2. {
  3.         int e, i;
  4.         struct TIMER *t;
  5.         e = io_load_eflags();
  6.         io_cli();        /* 在设置过程中禁止改变定时器状态 */
  7.         for (i = 0; i < MAX_TIMER; i++) {
  8.                 t = &timerctl.timers0[i];
  9.                 if (t->flags != 0 && t->flags2 != 0 && t->fifo == fifo) {
  10.                         timer_cancel(t);
  11.                         timer_free(t);
  12.                 }
  13.         }
  14.         io_store_eflags(e);
  15.         return;
  16. }
复制代码
大功告成了! 我们来 make run,运行noodle.brb,然后让程序竣事。 哦哦成功了,神秘字符再也不出现太好了!
总结

好了,本日的内容就到这里吧,来日诰日见哦!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

tsx81429

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表