Unix/Linux下的Curses库开发指南——第四章 鼠标支持
来源:互联网 发布:无聊了怎么办 知乎 编辑:程序博客网 时间:2024/04/27 22:02
第4章 鼠标支持
4.1鼠标支持简介
终端支持鼠标操作目前是一个比较高级的话题,很少有资料涉及。另一方面支持鼠标操作的终端实际上目前非常的少,除了xterm类型的终端外,其余的终端都不支持。因此这部分大多数人可能不需要,所以本书开始不准备涉及这方面的讨论,但考虑到书籍的完整性,还是把这部分加入。(Sco Unix、Solaris等操作系统终端环境下都不支持鼠标)。本章的示例程序可以在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 x,y,z;
mmask_t bstate;
}MEVENT;
其中bstate是我们最感兴趣的成员,它描述了事件发生的时候鼠标按键的状态;
其余的id用来区分消息来源的不同的设备,比如鼠标,图形板等等;x,y给出了事件发生时候的鼠标位置,至于z暂时没有使用。
另一方面,为了能够保存鼠标事件,curses中引入了鼠标事件队列。所有的鼠标事件触发后,它们将被压入鼠标事件队列中等候处理。实际上所有的鼠标函数都是通过鼠标事件队列获取相应的鼠标事件。
4.3开始使用鼠标
4.3.1鼠标操作函数
curses中的鼠标支持函数包括下面的七个:
■ mmask_t mousemask(mmask_t newmask,mmask_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结构中的x,y坐标。wenclose()用来判断参数中的(x;y)坐标是否在给定的窗口win中。对于前面的函数getmouse(),它返回的MEVENT结构中的(x,y)坐标是相对于屏幕的左上角而言。而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;
}
}
- Unix/Linux下的Curses库开发指南——第四章 鼠标支持
- Unix/Linux下的Curses库开发指南——第三章curses库窗口
- Unix/Linux下的Curses库开发指南——第一章 Curses库开发简介
- Unix/Linux下的Curses库开发指南——第二章 curses库I/O处理
- Unix/Linux下的Curses库开发指南——第二章 curses库I/O处理
- Unix/Linux下的Curses库开发指南——第五章 面板库(panel)开发及应用
- Linux/Unix下的Curses库开发指南——第七章 表单开发及应用(1)
- 公开 《Unix/Linux下的Curses库开发指南》全书内容
- Linux Unix C 中的curses库——curses.h
- Unix/Linux下的Curse库开发指南——第六章 菜单开发及应用(1)
- 以下是UNIX linux 下c语言的图形编程 curses库
- linux下的c 编程------curses 库
- linux下的图形编程库curses
- Linux下curses库的基本操作
- Linux/Unix终端图形库curses简介
- curses库(LINUX/UNIX图形函数库)
- linux下curses的安装
- linux下curses库介绍
- (转)无法将自定义控件安装到工具箱的解决办法
- 《UCD火花集》书评:从卖产品到卖用户体验
- asp.net 验证码
- postfix+devecot+openwebmail+apache
- "控件必须放在具有 runat=server 的窗体标记内"错误的解决方法
- Unix/Linux下的Curses库开发指南——第四章 鼠标支持
- 一份比较详细的DOS命令说明!
- 判断是否为数字C(包括小数)
- Js 操作文件及文件夹
- 了解webservice和简单实例
- http://www.cnblogs.com/wangkewei/archive/2009/05/20/1472629.html
- 如何在你的软件中加入脚本功能
- oracle.jdbc.OracleConnection.physicalConnectionWithin问题
- 扩展Windows Mobile模拟器存储空间的方法