Unix/Linux下的Curses库开发指南——第四章 鼠标支持

来源:互联网 发布:无聊了怎么办 知乎 编辑:程序博客网 时间:2024/04/27 22:02
 

4章 鼠标支持

4.1鼠标支持简介

终端支持鼠标操作目前是一个比较高级的话题,很少有资料涉及。另一方面支持鼠标操作的终端实际上目前非常的少,除了xterm类型的终端外,其余的终端都不支持。因此这部分大多数人可能不需要,所以本书开始不准备涉及这方面的讨论,但考虑到书籍的完整性,还是把这部分加入。(Sco UnixSolaris等操作系统终端环境下都不支持鼠标)。本章的示例程序可以在Linux下的X Window中通过。

 

鼠标能够让用户的操作更加方便和容易,但存在着一个非常严重的问题,即就是程序的移植性问题。curses库的引入正是为了解决终端的移植问题,如果过多的使用鼠标支持,将使得程序的移植性非常的差,在那些不支持鼠标的终端上操作将无法进行,这将违背于curses库当初的设计。因此我们应该避免使用鼠标,对于那些使用鼠标的程序最好同时能够提供键盘解决方案,除非你能保证你使用的所有终端都能够支持鼠标。

4.2鼠标支持概念和数据结构

鼠标操作将引入一个新的概念:事件。每一个鼠标操作都对应于一个鼠标事件,同时会触发该鼠标事件。一旦鼠标事件触发,我们就可以执行特定的程序。目前,curses库中支持的所有的鼠标事件如表4.1

表4.1curses库中的所有鼠标事件

鼠标事件

鼠标操作

BUTTON1_PRESSED

按下鼠标键1

BUTTON1_RELEASED

松开鼠标键1

BUTTON1_CLICKED

单击鼠标键1

BUTTON1_DOUBLE_CLICKED

双击鼠标键1

BUTTON1_TRIPLE_CLICKED

连续三次点击鼠标键1

BUTTON2_PRESSED

按下鼠标键2

BUTTON2_RELEASED

松开鼠标键2

BUTTON2_CLICKED

单击鼠标键2

BUTTON2_DOUBLE_CLICKED

双击鼠标键2

BUTTON2_TRIPLE_CLICKED

连续三次点击鼠标键2

BUTTON3_PRESSED

按下鼠标键3

BUTTON3_RELEASED

松开鼠标键3

BUTTON3_CLICKED

单击鼠标键3

BUTTON3_DOUBLE_CLICKED

双击鼠标键3

BUTTON3_TRIPLE_CLICKED

连续三次点击鼠标键3

BUTTON4_PRESSED

按下鼠标键4

BUTTON4_RELEASED

松开鼠标键4

BUTTON4_CLICKED

单击鼠标键4

BUTTON4_DOUBLE_CLICKED

双击鼠标键4

BUTTON4_TRIPLE_CLICKED

连续三次点击鼠标键4

BUTTON_SHIFT

在鼠标状态改变期间按下SHIFT

BUTTON_CTRL

在鼠标状态改变期间按下CTRL

BUTTON_ALT

在鼠标状态改变期间按下ALT

ALL_MOUSE_ENENT

报告所有的鼠标的状态改变

REPORT_MOUSE_POSITION

鼠标移动

 

为了能够描述每个鼠标事件,curses中使用结构MEVENT,它的结构如下:

typedef struct {

short id

int xyz

mmask_t bstate

}MEVENT

其中bstate是我们最感兴趣的成员,它描述了事件发生的时候鼠标按键的状态;

其余的id用来区分消息来源的不同的设备,比如鼠标,图形板等等;xy给出了事件发生时候的鼠标位置,至于z暂时没有使用。  

另一方面,为了能够保存鼠标事件,curses中引入了鼠标事件队列。所有的鼠标事件触发后,它们将被压入鼠标事件队列中等候处理。实际上所有的鼠标函数都是通过鼠标事件队列获取相应的鼠标事件。

4.3开始使用鼠标

4.3.1鼠标操作函数

curses中的鼠标支持函数包括下面的七个:

 mmask_t mousemask(mmask_t newmaskmmask_t *oldmask)

这个函数是所有的鼠标函数中第一个调用的,它的作用相当于initscr(),它初始化鼠标系统,通知系统必须截获并处理参数newmask指定的鼠标事件,指定之外的事件则可以忽略。如果设置之前系统指定的处理事件oldmask不为NULL,则原有的事件保存在oldmask中,这样一旦处理结束后可以恢复为原来的设置。默认情况下,函数不对任何事件进行处理。因此如果需要使用鼠标进行处理,我们必须自己进行设置。如果我们对所有的鼠标事件都需要处理的话,那可以使用ALL_MOUSE_EVENT事件;如果需要关闭所有的鼠标事件,newmask可以设置为0

如果函数执行错误,将返回0;否则返回设置的当前位置。如果我们需要处理鼠标的所有双击事件,那么函数用法可以如下:

mousemask(BUTTON1_DOUBLE_CLICKED|BUTTON2_DOUBLE_CLICKED|

BUTTON3_DOUBLE_CLICKED|BUTTON4_DOUBLE_CLICKED,

Old_mask);

 

■ int mouseinterval(int erval);

该函数用来设置鼠标一次点击的时间间隔。一次鼠标点击定义为在一定的时间间隔内鼠标被按下又被释放的过程。默认情况下这个时间间隔为1/5秒。通过mouseinterval()函数我们可以修改这个时间间隔。erval就是需要设定的时间间隔。它的单位是毫秒。如果将鼠标点击的时间间隔从默认值更改为1秒,则函数用法如下:

mouseinterval(1000)

如果函数执行成功,将返回OK,否则返回ERR

 

■ int getmouse(MEVENT *envnt);

■ int ungetmouse(MEVENT *event);

与使用getch()wgetch()从键盘接受输入一样,我们也同样使用getch()wgetch()从鼠标接受输入。但是为了区别键盘输入和鼠标输入,我们定义了KEY_MOUSE常量。如果是鼠标输入,getch()wgetch()将返回KEY_MOUSE。因此我们可以通过判断getch()wgetch()的返回值是否为KEY_MOUSE判断是否是鼠标输入。

如果输入为鼠标,则 getmouse()函数用来从鼠标事件队列中获取下一个鼠标事件。如果执行成功,它将根据鼠标事件填充结构mevent。因此通过获取该event结构中的bstate成员可以知道具体的鼠标事件。

因此整个鼠标处理的代码可以如下:

MEVENT event;

ch = getch();

if(ch == KEY_MOUSE)

    if(getmouse(&event) == OK)

        .    /* 事件处理代码 */

        .

.

 

■ bool wenclose(WINDOW *win,int y,int x);

ungetmouse()的作用与ungetch()类似,它将KEY_MOUSE事件返回给getch()wgetch()函数的输入队列,同时将鼠标事件返回给鼠标事件队列。

■ bool wmouse_trafo(const WINDOW *win,int *pY,int *pX,bool to_screen);

这两个函数用来处理MEVENT结构中的xy坐标。wenclose()用来判断参数中的(xy)坐标是否在给定的窗口win中。对于前面的函数getmouse(),它返回的MEVENT结构中的(xy)坐标是相对于屏幕的左上角而言。而wenclose()是相对于指定窗口的。getmouse()用来获取整个屏幕上的指定的鼠标事件,而wenclose()只能处理指定窗口内的鼠标事件。发生在窗口之外的其余的鼠标事件,wenclose()无法获取。一旦在窗口内触发鼠标事件,wmouse_trafo()函数将把屏幕坐标转换为窗口坐标,这时候最后一个参数to_screen必需设置为FALSE;如果to_screen设置为TRUE,则函数将把窗口坐标转换为屏幕相对坐标。

   

4.3.2 鼠标程序开发步骤

鼠标程序的开发一般遵循下面的一些步骤:

(1) 使用mousemask()函数初始化需要获取的鼠标事件。

(2) 循环使用getch()或者wgetch()函数获取键盘或者鼠标输入,如果getch()wgetch()返回KEY_MOUSE,则表明是鼠标输入。

(3) 通过getmouse()获取触发的鼠标事件,根据具体的事件进行处理。

下面我们根据这三个开发步骤看下面的一个示例程序。

4.3.3示例程序

下面的程序演示了如何使用鼠标进行菜单选择,

#include <curses.h>

#define WIDTH 30

#define HEIGHT 10 

int startx = 0;

int starty = 0;

char *choices[] = {  

"Choice 1",

            "Choice 2",

            "Choice 3",

            "Choice 4",

            "Exit",

          };

 

int n_choices = sizeof(choices) / sizeof(char *);

void print_menu(WINDOW *menu_win, int highlight);

void report_choice(int mouse_x, int mouse_y, int *p_choice);

int main()

{    

int c, choice = 0;

    WINDOW *menu_win;

    MEVENT event;

 

    initscr();

    clear();

    noecho();

    cbreak();    

 

    /* 在屏幕上输出窗口 */

    startx = (80 - WIDTH) / 2;

    starty = (24 - HEIGHT) / 2;

    

    attron(A_REVERSE);

    mvprintw(23, 1, "Click on Exit to quit");

    refresh();

    attroff(A_REVERSE);

 

    /* Print the menu for the first time */

    menu_win = newwin(HEIGHT, WIDTH, starty, startx);

    print_menu(menu_win, 1);

    /* Get all the mouse events */

    mousemask(ALL_MOUSE_EVENTS, NULL);

    

    while(1)

    {    c = wgetch(menu_win);

        switch(c)

        {    case KEY_MOUSE:

            if(getmouse(&event) == OK)

            {    /* 一旦用户按下左键 */

                if(event.bstate & BUTTON1_PRESSED)

                {    report_choice(event.x + 1, event.y + 1, &choice);

                    if(choice == -1) //Exit chosen

                        goto end;

                    mvprintw(22, 1, "Choice made is : %d String Chosen is /"%10s/"", choice, choices[choice - 1]);

                    refresh(); 

                }

            }

            print_menu(menu_win, choice);

            break;

        }

    }        

end:

    endwin();

    return 0;

}

 

void print_menu(WINDOW *menu_win, int highlight)

{

    int x, y, i;    

 

    x = 2;

    y = 2;

    box(menu_win, 0, 0);

    for(i = 0; i < n_choices; ++i)

    {    if(highlight == i + 1)

        {    wattron(menu_win, A_REVERSE); 

            mvwprintw(menu_win, y, x, "%s", choices[i]);

            wattroff(menu_win, A_REVERSE);

        }

        else

            mvwprintw(menu_win, y, x, "%s", choices[i]);

        ++y;

    }

    wrefresh(menu_win);

}

 

void report_choice(int mouse_x, int mouse_y, int *p_choice)

{    int i,j, choice;

 

    i = startx + 2;

    j = starty + 3;

    

    for(choice = 0; choice < n_choices; ++choice)

        if(mouse_y == j + choice && mouse_x >= i && mouse_x <= i + strlen(choices[choice]))

        {    if(choice == n_choices - 1)

                *p_choice = -1;        

            else

                *p_choice = choice + 1;    

            break;

        }

}

原创粉丝点击