SWT中事件的触发借助于底层操作系统,因此首先贴出windows下的系统函数:
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
当用户单击某个控件时首先由操作系统调用Display的windowProc方法
/*
*handle为系统事件的句柄140051169515536
*userata为事件类型(注意:这里事件指的是系统事件)
*/
long /*int*/ windowProc (long /*int*/ handle, long /*int*/ user_data) {
Widget widget = getWidget (handle);//根据句柄获得相应的控件,本例子中为Button
if (widget == null) return 0;
return widget.windowProc (handle, user_data);//调用widget的windowProc函数,
//该函数并没有被子类Button重写,因此Button继承了父类的函数
}
Widget的windowProc函数
//根据系统事件类型(user_data),调用相应的处理函数,这里为Button重写的gtk_clicked函数。
long /*int*/ windowProc (long /*int*/ handle, long /*int*/ user_data) {
switch ((int)/*64*/user_data) {
case ACTIVATE: return gtk_activate (handle);
case CHANGED: return gtk_changed (handle);
case CLICKED: return gtk_clicked (handle);
case CREATE_MENU_PROXY: return gtk_create_menu_proxy (handle);
case DAY_SELECTED: return gtk_day_selected (handle);
case DAY_SELECTED_DOUBLE_CLICK: return gtk_day_selected_double_click (handle);
case HIDE: return gtk_hide (handle);
case GRAB_FOCUS: return gtk_grab_focus (handle);
case MAP: return gtk_map (handle);
case MONTH_CHANGED: return gtk_month_changed (handle);
case OUTPUT: return gtk_output (handle);
case POPUP_MENU: return gtk_popup_menu (handle);
case PREEDIT_CHANGED: return gtk_preedit_changed (handle);
case REALIZE: return gtk_realize (handle);
case START_INTERACTIVE_SEARCH: return gtk_start_interactive_search (handle);
case SELECT: return gtk_select (handle);
case SELECTION_DONE: return gtk_selection_done (handle);
case SHOW: return gtk_show (handle);
case VALUE_CHANGED: return gtk_value_changed (handle);
case UNMAP: return gtk_unmap (handle);
case UNREALIZE: return gtk_unrealize (handle);
default: return 0;
}
}
以下是Button类的gtk_clicked函数
long /*int*/ gtk_clicked (long /*int*/ widget) {
if ((style & SWT.RADIO) != 0) {//当Button类型为单选按钮时
if ((parent.getStyle () & SWT.NO_RADIO_GROUP) != 0) {
setSelection (!selected);
} else {
selectRadio ();
}
} else {
if ((style & SWT.CHECK) != 0) {//当Button类型为多选按钮时
if (grayed) {
if (OS.gtk_toggle_button_get_active (handle)) {
OS.gtk_toggle_button_set_inconsistent (handle, true);
} else {
OS.gtk_toggle_button_set_inconsistent (handle, false);
}
}
}
}
sendSelectionEvent (SWT.Selection);
return 0;
}
void sendSelectionEvent (int eventType) {
sendSelectionEvent (eventType, null, false);
}
void sendSelectionEvent (int eventType, Event event, boolean send) {
if (eventTable == null && !display.filters (eventType)) {
return;
}
if (event == null) event = new Event ();
//返回当前GTK+处理事件的副本,详细请参考http://www.gtk.org/api/2.6/gtk/gtk-General.html
//TODO:关于底层GTK+的东西,还需要扩展
long /*int*/ ptr = OS.gtk_get_current_event ();
if (ptr != 0) {
GdkEvent gdkEvent = new GdkEvent ();
OS.memmove (gdkEvent, ptr, GdkEvent.sizeof);
switch (gdkEvent.type) {
case OS.GDK_KEY_PRESS:
case OS.GDK_KEY_RELEASE:
case OS.GDK_BUTTON_PRESS:
case OS.GDK_2BUTTON_PRESS:
case OS.GDK_BUTTON_RELEASE: {
int [] state = new int [1];
OS.gdk_event_get_state (ptr, state);
setInputState (event, state [0]);
break;
}
}
OS.gdk_event_free (ptr);
}
sendEvent (eventType, event, send);
}
//根据事件类型封装java中的事件
void sendEvent (int eventType, Event event, boolean send) {
if (eventTable == null && !display.filters (eventType)) {
return;
}
if (event == null) event = new Event ();
event.type = eventType;
event.display = display;
event.widget = this;
if (event.time == 0) {
event.time = display.getLastEventTime ();
}
if (send) {
sendEvent (event);//将消息直接send到窗口过程
} else {
display.postEvent (event);//将消息post到一个先进先出的队列中(消息队列)
}
}
////将消息post到一个先进先出的队列中(消息队列)
void postEvent (Event event) {
/*
* Place the event at the end of the event queue.
* This code is always called in the Display's
* thread so it must be re-enterant but does not
* need to be synchronized.
*/
if (eventQueue == null) eventQueue = new Event [4];
int index = 0;
int length = eventQueue.length;
//寻找消息队列中的一个空位置放入消息
while (index < length) {
if (eventQueue [index] == null) break;
index++;
}
//如果当前消息队列已满,新建一个长度+4的消息队列
if (index == length) {
Event [] newQueue = new Event [length + 4];
System.arraycopy (eventQueue, 0, newQueue, 0, length);
eventQueue = newQueue;
}
eventQueue [index] = event;
}
//Display通过调用readAndDispatch方法,后者调用runDeferredEvents方法取出消息队列eventQueue中第一个事件
,并调用事件对应widget的sendEvent方法。
boolean runDeferredEvents () {
boolean run = false;
/*
* Run deferred events. This code is always
* called in the Display's thread so it must
* be re-enterant but need not be synchronized.
*/
while (eventQueue != null) {
/* Take an event off the queue */
Event event = eventQueue [0];
if (event == null) break;
int length = eventQueue.length;
System.arraycopy (eventQueue, 1, eventQueue, 0, --length);
eventQueue [length] = null;
/* Run the event */
Widget widget = event.widget;
if (widget != null && !widget.isDisposed ()) {
Widget item = event.item;
if (item == null || !item.isDisposed ()) {
run = true;
widget.sendEvent (event);
}
}
/*
* At this point, the event queue could
* be null due to a recursive invokation
* when running the event.
*/
}
/* Clear the queue */
eventQueue = null;
return run;
}
//widget的sendEvent方法
void sendEvent (Event event) {
Display display = event.display;
if (!display.filterEvent (event)) {
if (eventTable != null) eventTable.sendEvent (event);
}
}
eventTable是widget中保存事件类型和对应监听器的数据结构。eventTable根据event的type找到对应的listener,
然后执行Listener子类TypedListener的handleEvent方法
handleEvent主要的操作就是
SelectionEvent event = new SelectionEvent (e);//对event进一步封装
((SelectionListener) eventListener).widgetSelected (event);//调用对应listener的回调方法。
到此,一个响应用户事件的过程结束。这里只是记录一个过程,以后会就每一点详细讨论。
分享一个源代码学习的网站
http://grepcode.com/file/repository.grepcode.com/java/eclipse.org/4.2/org.eclipse.swt.gtk.linux/x86_64/3.100.0/org/eclipse/swt/widgets/Shell.java#Shell