要做图形学作业,!就采用了这中古老的方式,顺便翻译了篇文章. 
=========================================================================================================
Dos环境下的鼠标编程

输入设备只有键盘(没有鼠标)的时候已经过去了,以图形界面为基础的操作系统以及应用程序的到来导至了对鼠标的迫切需求与广泛应用。鼠标是一种指向设备用来控制显示在屏幕上的指针。当我们移动鼠标的时候,指针也以同样的方向在屏幕上移动。通过使用鼠标,我们可以进行单击图标或者拖动对象等操作。软件会对这些行为做特定的响应。比如,如果你用鼠标点击一个文件,程序就会把文件的内容显示给你。对于图形界面的操作系统(比如windows)而言,鼠标已经象键盘一样成为了系统的一部分。也就是说,每个应用程序在这种系统下创建,运行,系统给与了关于鼠标的充分的支持。在这种情况下开发应用程序,会非常容易,因为每一个鼠表事件,操作系统都会感知并为你产生一个关于此事件的消息。当用户点击按钮或者用鼠标做些其他事情的时候,系统会产生一个消息发送给相应的应用程序,这个消息描述了鼠标的行为.在这种情况下,软件开发人员所需要做的只是处理这些消息。
在dos下写支持鼠标的应用程序会需要有一些技巧,稍微有点复杂。这是因为dos提供的是文字模式(字符模式),dos的开发者从来没有想过要将鼠标做为操作系统的一部分功能。所以,这里没有想windows系统那样的消息概念(在windows下,单击鼠标,会产生一个消息,发送给相关的应用程序)。但是,dos是兼容鼠标的。只要你确实有个鼠标,并且有相应的驱动程序。
在这篇教程中,我写一些关于用c语言处理一些鼠标的功能。我已经假设你已经使用过了c语言,如果你还没有,你最好先去阅读关于c基础的书,在dos下进行鼠标编程包括了调用中断。让我们开始学习之前,先熟悉一下关于中断方面的知识。


中断小知识:
  当你电脑的处理器正在忙着工作,而你恰好按下了键盘上的按键,会发生什么呢?无论当时处理器在做什么,我们会看见,根据我们的按键,会有一个相应的字母显示在屏幕上。因为按键中断了处理器的执行,我们就说,键盘中断产生了。简明的说,中断是一个终止处理器正在执行的操作,而转去执行别的操作的信号。仅仅在新的工作完成之后,处理器才回去执行开始中断前尚未完成的任务。这种恢复之所以可能是因为中断一但发生,处理器就会通过将所有的内部寄存器压入堆栈来保存处理器状态,并在恢复的时候将堆栈中的数据弹回到原来的寄存中。现在我们来了解一些技术细节,每一个中断都有它的中断处理程序。中断处理程序是在内存中的一断程序,当中断发生时,处理器就转去执行相应的中断处理程序,中断处理程序的地址全部保存在一个叫中断向量表的地方。中断发生时,处理器从中断向量表中获取中断程序地址,并转去执行。每一个中断都有一个号码标识。9h是键盘的中断号,8h是时钟的,等等。当调用一个中断的时候,我门需要将一些简单的数字传进去,被子过程使用。为什么我们需要这些子函数?拿中断号-10h的处理屏幕的中断来说,它允许我们利用使用子过程(41h和42h)来进行文字模式和图形模式的切换。允许我们只用子中断ah
来写一个字符到屏幕上,允许我们调用子中断42h清理屏幕,等等。我们将子中断号作为参数传递进去,以告诉系统我们想要具体使用的中断功能。


让我们进入“唯c”的世界吧

我们的第一程序是检查判断鼠标驱动程序是否已经被载入。
这里再说一下。驱动是一个检查鼠标的存在,并解释从鼠标端口传来的信号的程序。
1)  探测鼠标是否被载入.
--------------------------------------------------------------------------------

      
   #include <dos.h>
  main()
  {
    union REGS i,o;

    clrscr();

    i.x.ax=0;
      int86(0x33,&i,&o);

   if(o.x.ax==0)
      printf("No Mouse Available.....");
     else
          printf("Mouse Available......");  
       }
上面这个程序声明了两个类型为REGS的联合体。关于REGS的具体声明,已经包含在dos.h头文件中,有两个结构体(struct WORDREGS x, struct BYTEREGS h)这两个结构题包含一些1字节单位长度和二字节单位长度的变量,用来间接的表示寄存器。通过传递0给AX寄存器,并调用鼠标中断,我们能够检测出鼠标驱动程序是否已经被载入。我们使用int86这个函数来调用中断。Int86()需要参个参数,分别是中断号,和两个REGS类型的变量。如果不存在鼠标驱动,0会被返回到AX寄存器。在程序中,所有的返回值都能在REGS类型变量o中获得。

2.一个显示鼠标指针的程序。
我们的第一个程序仅仅报告了鼠标驱动是否存在。就算驱动存在,我们也不会发现有鼠标指针的标记。如果要看到鼠标指针,我们需要使用第一个子过程。看下面的这个程序。
#include <dos.h>
       main()
       {
         union REGS i,o;

   clrscr();

   i.x.ax=0;
     int86(0x33,&i,&o);

     if(o.x.ax==0)
         {
      printf("No Mouse Available.....");
          exit();
         }

     i.x.ax=1;
   int86(0x33,&i,&o);
      }
上面这个程序除了最后几行,基本是一致的。我们已经把1付给了AX寄存器,然后调用了中断,以看见指针。因为我们处于文字模式,我们的鼠标指针是一个小方块。如果我们切换到图形模式,我们将看到一个小箭头。


3) 实现隐藏鼠标指针。
甚至当我们的程序结束后,鼠标指针仍然在。看下面的程序:

#include <dos.h>
  main()
  {
       union REGS i,o;

         clrscr();

       i.x.ax=0;
       int86(0x33,&i,&o);

            if(o.x.ax==0)
     {
            printf("No Mouse Available.....");
            exit();
      }

     i.x.ax=1;
      int86(0x33,&i,&o);

     gotoxy(24,23);
       printf("Press any key to hide mouse cursor...");
      getch();

     i.x.ax=2;
      int86(0x33,&i,&o);

       gotoxy(10,23);
       printf("Mouse cursor is hidden !! Press any key to terminate the program ...");
           getch();
         }
       上面这个程序使用了子过程2,并调用中断实现隐藏鼠标指针。当当我们移动黍标画直线和方筐的时候,这个功能非常有用。当写这类程序的时候,我们不想让指针擦去了我们所画的,所以我们把指针隐藏起来。

4.编写程序实现获取鼠标位置。

为了写一个有用的支持鼠标的程序,我们通常需要知道指针所在坐标(x,y);下面这个程序打印出鼠标的位置,伴随着鼠标移动。
#include <dos.h>
  main()
  {
    union REGS i,o;

    clrscr();

    i.x.ax=0;
    int86(0x33,&i,&o);

    if(o.x.ax==0)
    {
      printf("No Mouse Available...");
      exit();
    }

    i.x.ax=1;
    int86(0x33,&i,&o);

    gotoxy(25,23);
    printf("Press any key to exit...");

    while(!kbhit())
    {
     i.x.ax=3;
     int86(0x33,&i,&o);
     gotoxy(2,2);
     printf("x->co-ordinate=%d     \n y->co-ordinate=%d      ",o.x.cx,o.x.dx);
    }

    i.x.ax=2;
    int86(0x33,&i,&o);
  }
  上面这个程序有个循环,直到我们按某个键,循环才退出。在这个循环里,我们使用子过程3。子过程三返回x坐标到cx寄存器,y坐标到dx寄存器。然后一直打印x,y的坐标直到循环退出。鼠标的范围在文字模式下是640x200,在图形模式下是640x480。
5》打印出鼠标键点击时候的情况。
知道鼠标哪个键被按下是非常重要的,下面这个程序显示了我们点击鼠标的时候是哪个键被按下。
#include <dos.h>
  main()
  {
    union REGS i,o;
    int button;

    clrscr();

    i.x.ax=0;
    int86(0x33,&i,&o);

    if(o.x.ax==0)
    {
      printf("No mouse available....");
      exit();
    }

    i.x.ax=1;
    int86(0x33,&i,&o);

    gotoxy(24,23);
    printf("Press any key to exit....");

    while(!kbhit())
    {
      i.x.ax=3;
      int86(0x33,&i,&o);
  
      button=o.x.bx&7;

      gotoxy(23,11);
      switch(button)
      {
        case 1:
    printf("Left button pressed                                  ");
        break;

        case 2:
    printf("Right button pressed                                  ");
        break;

        case 4:
    printf("Middle button pressed                                  ");
        break;

        case 3:
    printf("Left and Right buttons pressed                          ");
        break;

        case 5:
    printf("Left and Middle buttons pressed                          ");
        break;

        case 6:
    printf("Right and Middle buttons pressed                          ");
        break;

        case 7:
    printf("All the three buttons pressed                          ");
        break;

        default:
    printf("No button pressed....");
      }
    }

     i.x.ax=2;
     int86(0x33,&i,&o);
   }
上面着个程序除了在循环外有一些例外外,其他跟前面那个程序是一致的,我们仍然调用子中断3,鼠标键被按下的信息其实已经存放在了bx寄存器里面。全部的按键信息被存放在bx的前三个bit里。所以我们将bx与7做与操作以分离前三个bit,并把他们存放在button这个变量里面。第1位是1表示左键按下了,如果是0,表示没有被按下。第2位是1表示右键按下了,如果是0,表示没有被按下。如果最后通1位是1表示中间的键按下了,如果是0,表示没有被按下。


6.编写程序实现置鼠标指针到屏幕上所需要的位置。
一些时候我们需要实现置鼠标指针到屏幕上所需要的位置。就项我们使用gotoxy()将键盘光标置到指定的位置那样。下面这个程序将鼠标指针放到了屏幕上(x=150,y=100)这个位置。
#include <dos.h>
    main()
    {
      union REGS i,o;

       clrscr();

       i.x.ax=0;
        int86(0x33,&i,&o);

      if(o.x.ax==0)
      {
         printf("No mouse available");
         exit();
      }
      i.x.ax=1;
      int86(0x33,&i,&o);

      i.x.ax=3;
      int86(0x33,&i,&o);

         gotoxy(1,1);
      printf("Current Position:x=%d    y=%d    ",o.x.cx,o.x.dx);

      gotoxy(15,23);
      printf("Press any key to set the mouse pointer to (150,100)...");
      getch();

      i.x.ax=4;
      i.x.cx=150;
      i.x.dx=100;
      int86(0x33,&i,&o);

      gotoxy(15,23);
     printf("Cursor is set ... press a key to exit                    ");
     getch();
   }
上面那个程序中,我们使用子过程4设置了鼠标位置。我们通过将值传递给CX寄存器设置x坐标,通过将值传递给DX寄存器设置Y坐标。

7切换到图形模式
下面这个程序将实现从文字模式切换到图形模式。执行程序后,你会发现鼠标已经变成了箭头。这个程序试图在图形模式下执行程序4,并且观察鼠标的边界已经增加到了640x480。
#include <graphics.h>
  #include <dos.h>

  main()
  {
    int gd=DETECT,gm;
    union REGS i,o;
  
    initgraph(&gd,&gm,"");

    i.x.ax=0;
    int86(0x33,&i,&o);

    if(o.x.ax==0)
    {
      printf("No Mouse Avaialable..");
      restorecrtmode();
      exit();
    }

    i.x.ax=1;
    int86(0x33,&i,&o);

    outtextxy(100,400,"Mouse Pointer in graphics mode!!Press any key to exit");
    getch();

    i.x.ax=2;
    int86(0x33,&i,&o);

    restorecrtmode();
  }
在上面这个程序中,我们使用了标准的库函数initgraph()初始画图形系统。这个函数接受三个参数:图形驱动,图形模式,驱动路经。传递参数DETCET,我们告诉函数让它自己选择适当的驱动。当DETECT被使用的时候,就没有需要规定图形模式,驱动路径为空,因为驱动文件已经被定位在当前目录。最后,我们通过restorecrtmode()这个函数来恢复文字模式。

8》实现限制鼠标活动范围。
有些时候,我们需要限制鼠标指针活动范围。为了达到那个目的,我们需要定义上下,左右四条边界。子过程7,8用来实现限制指针在一定边界内。请看下面例子:


#include <graphics.h>
  #include <dos.h>
  main()
  {
    union REGS i,o;
    int gd=DETECT,gm;

    initgraph(&gd,&gm,"");

    i.x.ax=0;
    int86(0x33,&i,&o);

    if(o.x.ax==0)
    {
      restorecrtmode();
      printf("No Mouse Available.....");
      exit();
    }
    rectangle(99,49,501,151);

    i.x.ax=1;
    int86(0x33,&i,&o);

    i.x.ax=7;
    i.x.cx=100;
    i.x.dx=500;
    int86(0x33,&i,&o);

    i.x.ax=8;
    i.x.cx=50;
    i.x.dx=150;
    int86(0x33,&i,&o);

    while(!kbhit())
    ;

    i.x.ax=2;
    int86(0x33,&i,&o);

    restorecrtmode();
  }
  在上面这个程序中,子过程7用来实现限制左右,也就是x方向的边界,子过程8用来实现上下,也就是y方向的边界。

9》自由的画画!!
你也许会想,画图工具是怎样实现的?下面就一个用c语言写的这样的程序。下面这个程序使用了一些我们上面已经讨论过的子过程,展示了如何使用这些过程写出有用的程序。往下面看吧。
#include <graphics.h>
  #include <dos.h>

  union REGS i,o;

  main()
  {

    int gd=DETECT,gm,button,x1,y1,x2,y2;

    initgraph(&gd,&gm,"");

    i.x.ax=0;
    int86(0x33,&i,&o);

    if(o.x.ax==0)
    {
      printf("No Mouse is available..");
      exit();
      restorecrtmode();
    }

    outtextxy(230,400,"Press any key to exit....");

    while(!kbhit())
     {
      show_mouse();
      get_mouse_pos(&x1,&y1,&button);
  
      x2=x1;
      y2=y1;
  
      while(button==1)
      {
        hide_mouse();
        line(x1,y1,x2,y2);
  
        x1=x2;
        y1=y2;

        get_mouse_pos(&x2,&y2,&button);
      }
     }
     restorecrtmode();
   }

    show_mouse()
    {
      i.x.ax=1;
      int86(0x33,&i,&o);
    }
  
  hide_mouse()
  {
     i.x.ax=2;
     int86(0x33,&i,&o);
        }
  
  get_mouse_pos(int *x,int *y,int *button)
  {
     i.x.ax=3;
     int86(0x33,&i,&o);

           *x=o.x.cx;
         *y=o.x.dx;
           *button=o.x.bx&1;
        }

对于这个程序,没有什么所说的,看完了程序,你一定已经懂得了吧。

10》画直线
下面这个程序展示了如何交互式的使用鼠标画直线。如果你懂得了画直线,那么画方形也一定不成问题了。所以务必仔细的阅读下面的程序。


#include <graphics.h>
     #include <alloc.h>
     #include <dos.h>

     union REGS i,o;
     char far *p;

     main()
     {

       int gd=DETECT,gm,button,x1,y1,x2,y2,prevx2,prevy2,x,y;

       initgraph(&gd,&gm,"");

       i.x.ax=0;
       int86(0x33,&i,&o);

       if(o.x.ax==0)
         {
         printf("No Mouse is available..");
         exit();
         restorecrtmode();
       }

      while(!kbhit())
      {
        show_mouse();
        get_mouse_pos(&x1,&y1,&button);

        if(button==1)
        {
          hide_mouse();

          x2=x1;
          y2=y1;

         save(x1,y1,x2,y2);
         line(x1,y1,x2,y2);

         prevx2=x2;
         prevy2=y2;

        get_mouse_pos(&x2,&y2,&button);

        while(button==1)
             {
          if(x2!=prevx2 || y2!=prevy2)
          {
      setcolor(BLACK);
            line(x1,y1,prevx2,prevy2);
        x=x1<prevx2?x1:prevx2;
              y=y1<prevy2?y1:prevy2;
              restore(x,y);
          setcolor(WHITE);
        save(x1,y1,x2,y2);
        line(x1,y1,x2,y2);
        prevx2=x2;
        prevy2=y2;
         }
         get_mouse_pos(&x2,&y2,&button);
             }
       farfree(p);
     }
     }
      restorecrtmode();
       }

    show_mouse()
     {
     i.x.ax=1;
     int86(0x33,&i,&o);
     }

    hide_mouse()
    {
      i.x.ax=2;
         int86(0x33,&i,&o);
    }

    get_mouse_pos(int *x,int *y,int *button)
    {
        i.x.ax=3;
        int86(0x33,&i,&o);

        *x=o.x.cx;
        *y=o.x.dx;
        *button=o.x.bx&1;
        }

    save(int x1,int y1,int x2,int y2)
    {
        unsigned area;

        area=imagesize(x1,y1,x2,y2);
        p=farmalloc(area);

        if(p==NULL)
        {
           restorecrtmode();
           printf("No Memory...");
            exit();
          }

         getimage(x1,y1,x2,y2,p);
       }

       restore(int x1,int y1)
       {
    putimage(x1,y1,p,OR_PUT);
    farfree(p);
       }
当我们交互的画直线的时候,我们必须确保当直线相交的时候,当前画的直线不能覆盖已经画了的直线。为了达到这个要求,上面那个程序使用了save和restore这两个函数。这两个函数获取并保存当前屏幕内容。


译者结束语:图形学作业可以以这种最古老的方式完成了,哈哈哈!
Translate by rubbish