当鼠标在屏幕上移动时,可以使用鼠标移动事件跟踪它的移动。移动事件是当鼠标指针在窗口内移动时发生的,穿越事件是在鼠标指针进入或离开GdkWindow窗口时发生的。移动事件中的典型成员是GDK_MOTION_NOTIFY。有两种类型的穿越事件:
GDK_ENTER_NOTIFY和GDK_LEAVE_NOTIFY。
有两种方法跟踪鼠标移动事件。如果在窗口的事件屏蔽中指定了GDK_POINTER_MOTION_MASK,可以接收到X服务器能产生的尽可能多的事件。如果用户快速移动指针,程序会被移动事件淹没,必须快速处理它们,否则应用程序在处理大量事件时会反应迟钝。
如果还指定了GDK_POINTER_MOTION_HINT_MASK,那么每次只发送一个移动事件。也要调用gdk_window_get_pointer()函数、鼠标指针离开并重新进入窗口,或者鼠标按键或键盘事件发生时,事件才会发送出去。也就是,每次接收到一个移动事件必须调用gdk_window_get_pointer()函数取得鼠标指针的当前位置,并通知X服务器已经可以接收另一个事件了。
选择何种模式依赖于应用程序。如果需要精确跟踪指针的轨迹,就得捕获所有的移动事件。如果仅仅关心最近的鼠标指针位置,为了将网络流量最小化,使应用程序的响应能力最大化,则应在应用程序的事件屏蔽中包含GDK_POINTER_MOTION_HINT_MASK。
gdk_window_get_pointer()函数要求在服务器和客户之间传输数据以获得鼠标指针的位置,所以它对应用程序的响应能力有很大的限制。如果能尽可能快地处理鼠标运动事件而不被大量的事件淹没,应用程序看起来会比没有设置事件屏蔽GDK_POINTER_MOTION_HINT_MASK要快些。鼠标移动事件不可能在一秒钟内发生200次,所以如果能在5毫秒内处理好鼠标移动事件,情况就会不错。
如果想在一个或多个鼠标按键按下时才接收鼠标事件,可以用GDK_BUTTON_MOTION_MASK代替GDK_POINTER_MOTION_MASK。还可以用GDK_POINTER_MOTION_HINT_MASK和GDK_BUTTON_MOTION_MASK一起使用来限制要接收的事件的数量,就像和GDK_POINTER_MOTION_MASK一起使用一样。如果仅仅对某个特定的鼠标按键按下时的鼠标移动事件感兴趣,可以使用与特定鼠标键相关的事件屏蔽GDK_BUTTON1_MOTION_MASK、GDK_BUTTON2_MOTION_MASK以及GDK_BUTTON3_MOTION_MASK。这三个事件屏蔽的任何组合都是允许的。它们也可以与GDK_POINTER_MOTION_HINT_MASK联合使用以限制事件发生的数量。
总而言之,可以用下面五个事件屏蔽值选择在什么按键状态下接收什么鼠标移动事件:
GDK_POINTER_MOTION_MASK:不管按键状态,接收所有事件。
GDK_BUTTON_MOTION_MASK:有按键按下时接收所有事件。
GDK_BUTTON1_MOTION_MASK:按键1按下时接收所有事件。
GDK_BUTTON2_MOTION_MASK:按键2按下时接收所有事件。
GDK_BUTTON3_MOTION_MASK:按键3按下时接收所有事件。
缺省状态下,应用程序会被X服务器能生成的事件所淹没,最好在事件屏蔽中添加一个GDK_POINTER_MOTION_HINT_MASK,让事件“一段时间内发生一次”。
移动事件用GdkEventMotion结构描述:
typedefstruct_GdkEventMotionGdkEventMotion;
struct_GdkEventMotion
{
GdkEventTypetype;
GdkWindow*window;
gint8send_event;
guint32time;
gdoublex;
gdoubley;
gdoublepressure;
gdoublextilt;
gdoubleytilt;
guintstate;
gint16is_hint;
GdkInputSourcesource;
guint32deviceid;
gdoublex_root,y_root;
};
大多数成员与GdkEventButton中的成员是类似的。事实上,唯一与GdkEventMotion不同的成员是is_hint标志。如果它是TRUE,GDK_POINTER_MOTION_HINT_MASK标志会选中。
如果要写一个供其他人使用的构件,并且想让他们选择怎样接收移动事件,需要设置这个成员的值。在移动事件处理程序中,可以这么做:
doublex,y;
x=event->motion.x;
y=event->motion.y;
if(event->motion.is_hint)
gdk_window_get_pointer(event->window,&x,&y,NULL);
也就是说,只在必要时调用gdk_window_get_pointer()函数。
如果正在使用GDK_POINTER_MOTION_HINT_MASK,给定事件的坐标应该使用从gdk_window_get_pointer()函数取得的值,因为它们是更新过的。如果正在接收每个事件,调用gdk_window_get_pointer()就没有什么意义,因为这样做太慢了,并且会引起严重事件积压—这样,最终会得到所有的事件,但是应用程序的性能会很糟糕。
穿越事件是在鼠标指针进入或离开一个窗口时发生的。如果在应用程序的GdkWindow之间快速移动,Gdk会为每一个被穿越的窗口产生穿越事件。不过,Gtk+会试图删除在多个“穿越”过程中的事件,只将第一个离开窗口事件和最后一个进入窗口事件传给构件。这种优化能够提高程序的响应速度。如果感觉到应该发生了进入/离开事件,可实际上并没有发生,可能是上面的优化造成的。
GdkEventCrossing结构的定义如下:
typedefstruct_GdkEventCrossingGdkEventCrossing;
struct_GdkEventCrossing
{
GdkEventTypetype;
GdkWindow*window;
gint8send_event;
GdkWindow*subwindow;
guint32time;
gdoublex;
gdoubley;
gdoublex_root;
gdoubley_root;
GdkCrossingModemode;
GdkNotifyTypedetail;
gbooleanfocus;
guintstate;
};
上面所示结构中的很多成员都应该是很熟悉的,其中x和y坐标是相对于发生穿越事件的窗口的坐标;x_root和y_root坐标是相对于根窗口的;time成员指明事件的时间;state成员指明发生事件时按下的鼠标按键和组合键。结构中的前三个成员是GdkEventAny中的三个标准成员。不过,这里还有几个新成员。
GdkEventCrossing结构中的window成员是一个指向鼠标指针要进入或离开的窗口指针,x和y坐标就是相对于这个窗口的。但是,在“离开”事件发生之前,鼠标指针也许已经在接收事件的窗口的子窗口中存在了;当“进入”事件发生时,鼠标指针也许在一个子窗口中消失了。在这些情况下,subwindow成员应该设置为这个子窗口。否则,可以将subwindow设置为NULL。注意,如果在子窗口的事件屏蔽值中设置有GDK_ENTER_NOTIFY_MASK或GDK_LEAVE_NOTIFY_MASK,子窗口也会接收到它自己的“进入”或者“离开”事件。
GdkEventCrossing中的mode成员指出事件是正常引发的还是作为指针独占的一部分。
当指针被独占或解除独占时,指针也许会移动。由一个独占引起的鼠标移动事件的mode成员的值为GDK_CROSSING_GRAB,由解除独占引起的的移动事件的mode成员为GDK_CROSSING_UNGRAB,所有其他情况mode都为GDK_CROSSING_NORMAL。
GdkEventCrossing中的detail成员很少用到。它给出了关于要离开和进入的窗口在X系统窗口树中的相对位置的一些信息。它有两个很简单、很有用的值:
GDK_NOTIFY_INFERIOR标志一个当指针移进或移出一个子窗口时,由父窗口接收到的穿越事件。
GDK_NOTIFY_ANCESTOR标志一个当指针移进或者移出一个父窗口时,由子窗口接收到的穿越事件。其他几种值也是可能的:GDK_NOTIFY_VIRTUAL,GDK_NOTIFY_INFERIOR,GDK_NOTIFY_NONLINEAR,GDK_NOTIFY_NONLINEAR_VIRTUAL以及GDK_NOTIFY_UNKNOWN。不过,它们绝不会用到,因为它们太复杂了。
键盘焦点
GdkEventCrossing的focus成员指出是事件窗口还是它的子窗口得到键盘输入焦点。键盘焦点是一个X概念,用于决定哪一个窗口应该接收按键事件。窗口管理器决定哪一个顶级窗口应该获得焦点。通常,获得焦点的窗口是高亮显示的,并在最前面显示。大多数窗口管理器会让你在“跟随鼠标获得焦点”和“点击获得焦点”间作出选择。当应用程序具有焦点时,可以将焦点在它的子窗口间自由移动(例如,在不同的GtkText构件间移动)。不过,Gtk+并不对子窗口使用X的焦点机制。
顶级GtkWindow构件是唯一接收X焦点的窗口。因而,它们从X服务器(通过Gdk)接收所有的原始按键事件。Gtk+实现了它自己的构件焦点概念,它和X的窗口焦点概念是类似的,但实际上是截然不同的。当一个顶级窗口接收到按键事件,它会将事件传给具有Gtk+焦点的构件。
简而言之,如果包含事件窗口的顶级GtkWindow窗口目前具有X焦点,focus成员的值就会是TRUE。Focus成员与Gtk+的构件焦点概念没有直接的关系。
阅读(1931) | 评论(0) | 转发(0) |