Blender中的事件处理器

在Blender中,事件主要指以下内容(参见:https://wiki.blender.org/index.php/Dev:2.5/Source/Architecture/Window_Manager

  • 键盘、鼠标、设备、计时器
  • custom data (tablet, drag n drop)
  • modifier state, mouse coords
  • key modifiers (LMB, H-key, etc)
  • modifier order (Ctrl+Alt or Alt+Ctrl)
  • gestures (border, lasso, lines, etc)
  • multi-event (H, J, K etc)

事件处理是交由GHOST系统来完成的,在主程序中主事件循环代码在WM_Main(wm.c,bf_windowmanager库)函数中:

 1 void WM_main(bContext *C)
 2 {
 3     /* Single refresh before handling events.
 4      * This ensures we don't run operators before the depsgraph has been evaluated. */
 5     wm_event_do_refresh_wm_and_depsgraph(C);
 6 
 7     while (1) {
 8         
 9         /* get events from ghost, handle window events, add to window queues */
10         wm_window_process_events(C); 
11         
12         /* per window, all events to the window, screen, area and region handlers */
13         wm_event_do_handlers(C);
14         
15         /* events have left notes about changes, we handle and cache it */
16         wm_event_do_notifiers(C);
17         
18         /* execute cached changes draw */
19         wm_draw_update(C);
20     }
21 }

主循环中四步分别处理四种数据:event(事件)、operator(操作处理器)、notifier(通知)、draw(绘制)。

我们关心事件处理,也就是第10行代码完成的,它调用wm_window_process_events函数:

 1 void wm_window_process_events(const bContext *C) 
 2 {
 3     int hasevent;
 4 
 5     BLI_assert(BLI_thread_is_main());
 6 
 7     hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
 8 
 9     if (hasevent)
10         GHOST_DispatchEvents(g_system);
11     
12     hasevent |= wm_window_timer(C);
13 
14     /* no event, we sleep 5 milliseconds */
15     if (hasevent == 0)
16         PIL_sleep_ms(5);
17 }

 这个函数很简单,从操作系统中取得消息事件,有事件则进行派发,完成事件的处理过程。我们具体来看看这二个过程。这二个过程位于GHOST_C-api.cpp文件中,属于bf_intern_ghost库

 1 int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent)
 2 {
 3     GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
 4     
 5     return (int) system->processEvents(waitForEvent ? true : false);
 6 }
 7 
 8 
 9 
10 void GHOST_DispatchEvents(GHOST_SystemHandle systemhandle)
11 {
12     GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
13     
14     system->dispatchEvents();
15 }

 

由于ghost库是使用c++编译的,而bf_windowmanager库是c编译的,为了方便c代码中使用c++代码,所有ghost对外接口的代码都在GHOST_C-api.cpp中进行了包装,这二个函数也是这个目的,它们分别于5和14行调用了GHOST系统的processEvents和dispathcEvent函数。processEvents函数与操作系统密切相关的,在window操作系统中对应是GHOST_SystemWin32.cpp中函数:


 1 bool GHOST_SystemWin32::processEvents(bool waitForEvent)
 2 {
 3     MSG msg;
 4     bool anyProcessed = false;
 5 
 6     do {
 7         GHOST_TimerManager *timerMgr = getTimerManager();
 8 
 9         if (waitForEvent && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
10 #if 1
11             ::Sleep(1);
12 #else
13             GHOST_TUns64 next = timerMgr->nextFireTime();
14             GHOST_TInt64 maxSleep = next - getMilliSeconds();
15             
16             if (next == GHOST_kFireTimeNever) {
17                 ::WaitMessage();
18             }
19             else if (maxSleep >= 0.0) {
20                 ::SetTimer(NULL, 0, maxSleep, NULL);
21                 ::WaitMessage();
22                 ::KillTimer(NULL, 0);
23             }
24 #endif
25         }
26 
27         if (timerMgr->fireTimers(getMilliSeconds())) {
28             anyProcessed = true;
29         }
30 
31         // Process all the events waiting for us
32         while (::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE) != 0) {
33             // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
34             // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar.
35             ::TranslateMessage(&msg);
36             ::DispatchMessageW(&msg);
37             anyProcessed = true;
38         }
39     } while (waitForEvent && !anyProcessed);
40 
41     return anyProcessed;
42 }
32行开始的循环调用操作系统的api处理消息事件,windows操作系统将消息派发到窗口过程中。Blender在windows系统中的窗口回调过程也位于GHOST_SystemWin32.cpp中:
  1 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  2 {
  3     GHOST_Event *event = NULL;
  4     bool eventHandled = false;
  5 
  6     LRESULT lResult = 0;
  7     GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
  8     GHOST_EventManager *eventManager = system->getEventManager();
  9     GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized");
 10 
 11     if (hwnd) {
 12 #if 0
 13         // Disabled due to bug in Intel drivers, see T51959
 14         if(msg == WM_NCCREATE) {
 15             // Tell Windows to automatically handle scaling of non-client areas
 16             // such as the caption bar. EnableNonClientDpiScaling was introduced in Windows 10
 17             HMODULE m_user32 = ::LoadLibrary("User32.dll");
 18             if (m_user32) {
 19                 GHOST_WIN32_EnableNonClientDpiScaling fpEnableNonClientDpiScaling =
 20                     (GHOST_WIN32_EnableNonClientDpiScaling) ::GetProcAddress(m_user32, "EnableNonClientDpiScaling");
 21 
 22                 if (fpEnableNonClientDpiScaling) {
 23                     fpEnableNonClientDpiScaling(hwnd);
 24                 }
 25             }
 26         }
 27 #endif
 28 
 29         GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
 30         if (window) {
 31             switch (msg) {
 32                 // we need to check if new key layout has AltGr
 33                 case WM_INPUTLANGCHANGE:
 34                 {
 35                     system->handleKeyboardChange();
 36 #ifdef WITH_INPUT_IME
 37                     window->getImeInput()->SetInputLanguage();
 38 #endif
 39                     break;
 40                 }
 41                 ////////////////////////////////////////////////////////////////////////
 42                 // Keyboard events, processed
 43                 ////////////////////////////////////////////////////////////////////////
 44                 case WM_INPUT:
 45                 {
 46                     // check WM_INPUT from input sink when ghost window is not in the foreground
 47                     if (wParam == RIM_INPUTSINK) {
 48                         if (GetFocus() != hwnd) // WM_INPUT message not for this window
 49                             return 0;
 50                     } //else wParam == RIM_INPUT
 51 
 52                     RAWINPUT raw;
 53                     RAWINPUT *raw_ptr = &raw;
 54                     UINT rawSize = sizeof(RAWINPUT);
 55 
 56                     GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));
 57 
 58                     switch (raw.header.dwType) {
 59                         case RIM_TYPEKEYBOARD:
 60                             event = processKeyEvent(window, raw);
 61                             if (!event) {
 62                                 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ");
 63                                 GHOST_PRINT(msg);
 64                                 GHOST_PRINT(" key ignored\n");
 65                             }
 66                             break;
 67 #ifdef WITH_INPUT_NDOF
 68                         case RIM_TYPEHID:
 69                             if (system->processNDOF(raw))
 70                                 eventHandled = true;
 71                             break;
 72 #endif
 73                     }
 74                     break;
 75                 }
 76 #ifdef WITH_INPUT_IME
 77                 ////////////////////////////////////////////////////////////////////////
 78                 // IME events, processed, read more in GHOST_IME.h
 79                 ////////////////////////////////////////////////////////////////////////
 80                 case WM_IME_SETCONTEXT:
 81                 {
 82                     GHOST_ImeWin32 *ime = window->getImeInput();
 83                     ime->SetInputLanguage();
 84                     ime->CreateImeWindow(hwnd);
 85                     ime->CleanupComposition(hwnd);
 86                     ime->CheckFirst(hwnd);
 87                     break;
 88                 }
 89                 case WM_IME_STARTCOMPOSITION:
 90                 {
 91                     GHOST_ImeWin32 *ime = window->getImeInput();
 92                     eventHandled = true;
 93                     /* remove input event before start comp event, avoid redundant input */
 94                     eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
 95                     ime->CreateImeWindow(hwnd);
 96                     ime->ResetComposition(hwnd);
 97                     event = processImeEvent(
 98                             GHOST_kEventImeCompositionStart,
 99                             window,
100                             &ime->eventImeData);
101                     break;
102                 }
103                 case WM_IME_COMPOSITION:
104                 {
105                     GHOST_ImeWin32 *ime = window->getImeInput();
106                     eventHandled = true;
107                     ime->UpdateImeWindow(hwnd);
108                     ime->UpdateInfo(hwnd);
109                     if (ime->eventImeData.result_len) {
110                         /* remove redundant IME event */
111                         eventManager->removeTypeEvents(GHOST_kEventImeComposition, window);
112                     }
113                     event = processImeEvent(
114                             GHOST_kEventImeComposition,
115                             window,
116                             &ime->eventImeData);
117                     break;
118                 }
119                 case WM_IME_ENDCOMPOSITION:
120                 {
121                     GHOST_ImeWin32 *ime = window->getImeInput();
122                     eventHandled = true;
123                     /* remove input event after end comp event, avoid redundant input */
124                     eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
125                     ime->ResetComposition(hwnd);
126                     ime->DestroyImeWindow(hwnd);
127                     event = processImeEvent(
128                             GHOST_kEventImeCompositionEnd,
129                             window,
130                             &ime->eventImeData);
131                     break;
132                 }
133 #endif /* WITH_INPUT_IME */
134                 ////////////////////////////////////////////////////////////////////////
135                 // Keyboard events, ignored
136                 ////////////////////////////////////////////////////////////////////////
137                 case WM_KEYDOWN:
138                 case WM_SYSKEYDOWN:
139                 case WM_KEYUP:
140                 case WM_SYSKEYUP:
141                 /* These functions were replaced by WM_INPUT*/
142                 case WM_CHAR:
143                 /* The WM_CHAR message is posted to the window with the keyboard focus when
144                  * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
145                  * contains the character code of the key that was pressed.
146                  */
147                 case WM_DEADCHAR:
148                     /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
149                      * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR 
150                      * specifies a character code generated by a dead key. A dead key is a key that 
151                      * generates a character, such as the umlaut (double-dot), that is combined with 
152                      * another character to form a composite character. For example, the umlaut-O 
153                      * character (Ö) is generated by typing the dead key for the umlaut character, and
154                      * then typing the O key.
155                      */
156                     break;
157                 case WM_SYSDEADCHAR:
158                 /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when
159                  * a WM_SYSKEYDOWN message is translated by the TranslateMessage function.
160                  * WM_SYSDEADCHAR specifies the character code of a system dead key - that is,
161                  * a dead key that is pressed while holding down the alt key.
162                  */
163                 case WM_SYSCHAR:
164                     /* The WM_SYSCHAR message is sent to the window with the keyboard focus when 
165                      * a WM_SYSCHAR message is translated by the TranslateMessage function. 
166                      * WM_SYSCHAR specifies the character code of a dead key - that is, 
167                      * a dead key that is pressed while holding down the alt key.
168                      * To prevent the sound, DefWindowProc must be avoided by return
169                      */
170                     break;
171                 case WM_SYSCOMMAND:
172                     /* The WM_SYSCHAR message is sent to the window when system commands such as 
173                      * maximize, minimize  or close the window are triggered. Also it is sent when ALT 
174                      * button is press for menu. To prevent this we must return preventing DefWindowProc.
175                      */
176                     if (wParam == SC_KEYMENU) {
177                         eventHandled = true;
178                     }
179                     break;
180                 ////////////////////////////////////////////////////////////////////////
181                 // Tablet events, processed
182                 ////////////////////////////////////////////////////////////////////////
183                 case WT_PACKET:
184                     window->processWin32TabletEvent(wParam, lParam);
185                     break;
186                 case WT_CSRCHANGE:
187                 case WT_PROXIMITY:
188                     window->processWin32TabletInitEvent();
189                     break;
190                 ////////////////////////////////////////////////////////////////////////
191                 // Mouse events, processed
192                 ////////////////////////////////////////////////////////////////////////
193                 case WM_LBUTTONDOWN:
194                     window->registerMouseClickEvent(0);
195                     event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
196                     break;
197                 case WM_MBUTTONDOWN:
198                     window->registerMouseClickEvent(0);
199                     event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
200                     break;
201                 case WM_RBUTTONDOWN:
202                     window->registerMouseClickEvent(0);
203                     event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
204                     break;
205                 case WM_XBUTTONDOWN:
206                     window->registerMouseClickEvent(0);
207                     if ((short) HIWORD(wParam) == XBUTTON1) {
208                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
209                     }
210                     else if ((short) HIWORD(wParam) == XBUTTON2) {
211                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
212                     }
213                     break;
214                 case WM_LBUTTONUP:
215                     window->registerMouseClickEvent(1);
216                     event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
217                     break;
218                 case WM_MBUTTONUP:
219                     window->registerMouseClickEvent(1);
220                     event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
221                     break;
222                 case WM_RBUTTONUP:
223                     window->registerMouseClickEvent(1);
224                     event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
225                     break;
226                 case WM_XBUTTONUP:
227                     window->registerMouseClickEvent(1);
228                     if ((short) HIWORD(wParam) == XBUTTON1) {
229                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
230                     }
231                     else if ((short) HIWORD(wParam) == XBUTTON2) {
232                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
233                     }
234                     break;
235                 case WM_MOUSEMOVE:
236                     event = processCursorEvent(GHOST_kEventCursorMove, window);
237                     break;
238                 case WM_MOUSEWHEEL:
239                 {
240                     /* The WM_MOUSEWHEEL message is sent to the focus window 
241                      * when the mouse wheel is rotated. The DefWindowProc 
242                      * function propagates the message to the window's parent.
243                      * There should be no internal forwarding of the message, 
244                      * since DefWindowProc propagates it up the parent chain 
245                      * until it finds a window that processes it.
246                      */
247 
248                     /* Get the window under the mouse and send event to its queue. */
249                     POINT mouse_pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
250                     HWND mouse_hwnd = ChildWindowFromPoint(HWND_DESKTOP, mouse_pos);
251                     GHOST_WindowWin32 *mouse_window = (GHOST_WindowWin32 *)::GetWindowLongPtr(mouse_hwnd, GWLP_USERDATA);
252                     
253                     processWheelEvent(mouse_window ? mouse_window : window , wParam, lParam);
254                     eventHandled = true;
255 #ifdef BROKEN_PEEK_TOUCHPAD
256                     PostMessage(hwnd, WM_USER, 0, 0);
257 #endif
258                     break;
259                 }
260                 case WM_SETCURSOR:
261                     /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
262                      * to move within a window and mouse input is not captured.
263                      * This means we have to set the cursor shape every time the mouse moves!
264                      * The DefWindowProc function uses this message to set the cursor to an 
265                      * arrow if it is not in the client area.
266                      */
267                     if (LOWORD(lParam) == HTCLIENT) {
268                         // Load the current cursor
269                         window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
270                         // Bypass call to DefWindowProc
271                         return 0;
272                     } 
273                     else {
274                         // Outside of client area show standard cursor
275                         window->loadCursor(true, GHOST_kStandardCursorDefault);
276                     }
277                     break;
278 
279                 ////////////////////////////////////////////////////////////////////////
280                 // Mouse events, ignored
281                 ////////////////////////////////////////////////////////////////////////
282                 case WM_NCMOUSEMOVE:
283                 /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved
284                  * within the nonclient area of the window. This message is posted to the window
285                  * that contains the cursor. If a window has captured the mouse, this message is not posted.
286                  */
287                 case WM_NCHITTEST:
288                     /* The WM_NCHITTEST message is sent to a window when the cursor moves, or 
289                      * when a mouse button is pressed or released. If the mouse is not captured, 
290                      * the message is sent to the window beneath the cursor. Otherwise, the message 
291                      * is sent to the window that has captured the mouse. 
292                      */
293                     break;
294 
295                 ////////////////////////////////////////////////////////////////////////
296                 // Window events, processed
297                 ////////////////////////////////////////////////////////////////////////
298                 case WM_CLOSE:
299                     /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
300                     event = processWindowEvent(GHOST_kEventWindowClose, window);
301                     break;
302                 case WM_ACTIVATE:
303                     /* The WM_ACTIVATE message is sent to both the window being activated and the window being 
304                      * deactivated. If the windows use the same input queue, the message is sent synchronously, 
305                      * first to the window procedure of the top-level window being deactivated, then to the window
306                      * procedure of the top-level window being activated. If the windows use different input queues,
307                      * the message is sent asynchronously, so the window is activated immediately. 
308                      */
309                 {
310                     GHOST_ModifierKeys modifiers;
311                     modifiers.clear();
312                     system->storeModifierKeys(modifiers);
313                     system->m_wheelDeltaAccum = 0;
314                     event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
315                     /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
316                      * will not be dispatched to OUR active window if we minimize one of OUR windows. */
317                     if (LOWORD(wParam) == WA_INACTIVE)
318                         window->lostMouseCapture();
319 
320                     lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
321                     break;
322                 }
323                 case WM_ENTERSIZEMOVE:
324                     /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving 
325                      * or sizing modal loop. The window enters the moving or sizing modal loop when the user 
326                      * clicks the window's title bar or sizing border, or when the window passes the 
327                      * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the 
328                      * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when 
329                      * DefWindowProc returns. 
330                      */
331                     window->m_inLiveResize = 1;
332                     break;
333                 case WM_EXITSIZEMOVE:
334                     window->m_inLiveResize = 0;
335                     break;
336                 case WM_PAINT:
337                     /* An application sends the WM_PAINT message when the system or another application 
338                      * makes a request to paint a portion of an application's window. The message is sent
339                      * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage 
340                      * function when the application obtains a WM_PAINT message by using the GetMessage or 
341                      * PeekMessage function. 
342                      */
343                     if (!window->m_inLiveResize) {
344                         event = processWindowEvent(GHOST_kEventWindowUpdate, window);
345                         ::ValidateRect(hwnd, NULL);
346                     }
347                     else {
348                         eventHandled = true;
349                     }
350                     break;
351                 case WM_GETMINMAXINFO:
352                     /* The WM_GETMINMAXINFO message is sent to a window when the size or 
353                      * position of the window is about to change. An application can use 
354                      * this message to override the window's default maximized size and 
355                      * position, or its default minimum or maximum tracking size. 
356                      */
357                     processMinMaxInfo((MINMAXINFO *) lParam);
358                     /* Let DefWindowProc handle it. */
359                     break;
360                 case WM_SIZING:
361                 case WM_SIZE:
362                     /* The WM_SIZE message is sent to a window after its size has changed.
363                      * The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
364                      * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
365                      * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
366                      * message without calling DefWindowProc.
367                      */
368                     /* we get first WM_SIZE before we fully init. So, do not dispatch before we continiously resizng */
369                     if (window->m_inLiveResize) {
370                         system->pushEvent(processWindowEvent(GHOST_kEventWindowSize, window));
371                         system->dispatchEvents();
372                     }
373                     else {
374                         event = processWindowEvent(GHOST_kEventWindowSize, window);
375                     }
376                     break;
377                 case WM_CAPTURECHANGED:
378                     window->lostMouseCapture();
379                     break;
380                 case WM_MOVING:
381                     /* The WM_MOVING message is sent to a window that the user is moving. By processing
382                      * this message, an application can monitor the size and position of the drag rectangle
383                      * and, if needed, change its size or position.
384                      */
385                 case WM_MOVE:
386                     /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
387                      * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
388                      * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
389                      * message without calling DefWindowProc. 
390                      */
391                     /* see WM_SIZE comment*/
392                     if (window->m_inLiveResize) {
393                         system->pushEvent(processWindowEvent(GHOST_kEventWindowMove, window));
394                         system->dispatchEvents();
395                     }
396                     else {
397                         event = processWindowEvent(GHOST_kEventWindowMove, window);
398                     }
399 
400                     break;
401                 case WM_DPICHANGED:
402                     /* The WM_DPICHANGED message is sent when the effective dots per inch (dpi) for a window has changed.
403                     * The DPI is the scale factor for a window. There are multiple events that can cause the DPI to
404                     * change such as when the window is moved to a monitor with a different DPI.
405                     */
406                     {
407                         WORD newYAxisDPI = HIWORD(wParam);
408                         WORD newXAxisDPI = LOWORD(wParam);
409                         // The suggested new size and position of the window.
410                         RECT* const suggestedWindowRect = (RECT*)lParam;
411 
412                         // Push DPI change event first
413                         system->pushEvent(processWindowEvent(GHOST_kEventWindowDPIHintChanged, window));
414                         system->dispatchEvents();
415                         eventHandled = true;
416 
417                         // Then move and resize window
418                         SetWindowPos(hwnd,
419                             NULL,
420                             suggestedWindowRect->left,
421                             suggestedWindowRect->top,
422                             suggestedWindowRect->right - suggestedWindowRect->left,
423                             suggestedWindowRect->bottom - suggestedWindowRect->top,
424                             SWP_NOZORDER | SWP_NOACTIVATE);
425                     }
426                     break;
427                 ////////////////////////////////////////////////////////////////////////
428                 // Window events, ignored
429                 ////////////////////////////////////////////////////////////////////////
430                 case WM_WINDOWPOSCHANGED:
431                 /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
432                  * in the Z order has changed as a result of a call to the SetWindowPos function or
433                  * another window-management function.
434                  * The WM_SIZE and WM_MOVE messages are not sent if an application handles the
435                  * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
436                  * to perform any move or size change processing during the WM_WINDOWPOSCHANGED
437                  * message without calling DefWindowProc.
438                  */
439                 case WM_ERASEBKGND:
440                 /* An application sends the WM_ERASEBKGND message when the window background must be
441                  * erased (for example, when a window is resized). The message is sent to prepare an
442                  * invalidated portion of a window for painting.
443                  */
444                 case WM_NCPAINT:
445                 /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
446                 case WM_NCACTIVATE:
447                 /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed
448                  * to indicate an active or inactive state.
449                  */
450                 case WM_DESTROY:
451                 /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window
452                  * procedure of the window being destroyed after the window is removed from the screen.
453                  * This message is sent first to the window being destroyed and then to the child windows
454                  * (if any) as they are destroyed. During the processing of the message, it can be assumed
455                  * that all child windows still exist.
456                  */
457                 case WM_NCDESTROY:
458                     /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The 
459                      * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
460                      * message. WM_DESTROY is used to free the allocated memory object associated with the window. 
461                      */
462                     break;
463                 case WM_KILLFOCUS:
464                     /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. 
465                      * We want to prevent this if a window is still active and it loses focus to nowhere*/
466                     if (!wParam && hwnd == ::GetActiveWindow())
467                         ::SetFocus(hwnd);
468                 case WM_SHOWWINDOW:
469                 /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
470                 case WM_WINDOWPOSCHANGING:
471                 /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in
472                  * the Z order is about to change as a result of a call to the SetWindowPos function or
473                  * another window-management function.
474                  */
475                 case WM_SETFOCUS:
476                 /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
477                     break;
478                 ////////////////////////////////////////////////////////////////////////
479                 // Other events
480                 ////////////////////////////////////////////////////////////////////////
481                 case WM_GETTEXT:
482                 /* An application sends a WM_GETTEXT message to copy the text that
483                  * corresponds to a window into a buffer provided by the caller.
484                  */
485                 case WM_ACTIVATEAPP:
486                 /* The WM_ACTIVATEAPP message is sent when a window belonging to a
487                  * different application than the active window is about to be activated.
488                  * The message is sent to the application whose window is being activated
489                  * and to the application whose window is being deactivated.
490                  */
491                 case WM_TIMER:
492                     /* The WIN32 docs say:
493                      * The WM_TIMER message is posted to the installing thread's message queue
494                      * when a timer expires. You can process the message by providing a WM_TIMER
495                      * case in the window procedure. Otherwise, the default window procedure will
496                      * call the TimerProc callback function specified in the call to the SetTimer
497                      * function used to install the timer. 
498                      *
499                      * In GHOST, we let DefWindowProc call the timer callback.
500                      */
501                     break;
502 
503             }
504         }
505         else {
506             // Event found for a window before the pointer to the class has been set.
507             GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n");
508             /* These are events we typically miss at this point:
509              * WM_GETMINMAXINFO    0x24
510              * WM_NCCREATE            0x81
511              * WM_NCCALCSIZE        0x83
512              * WM_CREATE            0x01
513              * We let DefWindowProc do the work.
514              */
515         }
516     }
517     else {
518         // Events without valid hwnd
519         GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n");
520     }
521 
522     if (event) {
523         system->pushEvent(event);
524         eventHandled = true;
525     }
526 
527     if (!eventHandled)
528         lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam);
529 
530     return lResult;
531 }
GHOST_Windows操作系统下Blender窗口回调SystemWin32::s_wndProc

 在522行中,通过前面的处理,将包装成GHOST_Event *类型的event放入事件管理器中:

 1 GHOST_TSuccess GHOST_System::pushEvent(GHOST_IEvent *event)
 2 {
 3     GHOST_TSuccess success;
 4     if (m_eventManager) {
 5         success = m_eventManager->pushEvent(event);
 6     }
 7     else {
 8         success = GHOST_kFailure;
 9     }
10     return success;
11 }

第5行将event推入GHOST_EventManager*类型的m_eventManager中。

/**
 * Manages an event stack and a list of event consumers.
 * The stack works on a FIFO (First In First Out) basis.
 * Events are pushed on the front of the stack and retrieved from the back.
 * Ownership of the event is transferred to the event manager as soon as an event is pushed.
 * Ownership of the event is transferred from the event manager as soon as an event is popped.
 * Events can be dispatched to the event consumers.
 */
class GHOST_EventManager

 至此,完成了从windwos系统中获取应用程序消息事件、进一步加工成Blender中的GHOST_Event对象,最后将对象推入GHOST_EventManager对象。当然并不是所有的消息事件都加工成GHOST_Event,只是有需要的才加工的。

这仅完成了主程序事件循环中的wm_window_process_events过程的第一步,下一步就是对事件管理器的GHOST事件进行派发处理过程,它由dispatchEvents完成,位于GHOST_System.cpp中:

 1 void GHOST_System::dispatchEvents()
 2 {
 3 #ifdef WITH_INPUT_NDOF
 4     // NDOF Motion event is sent only once per dispatch, so do it now:
 5     if (m_ndofManager) {
 6         m_ndofManager->sendMotionEvent();
 7     }
 8 #endif
 9 
10     if (m_eventManager) {
11         m_eventManager->dispatchEvents();
12     }
13 
14     m_timerManager->fireTimers(getMilliSeconds());
15 }
16 
17 void GHOST_EventManager::dispatchEvents()
18 {
19     while (!m_events.empty()) {
20         dispatchEvent();
21     }
22 
23     disposeEvents();
24 }
25 
26 void GHOST_EventManager::dispatchEvent()
27 {
28     GHOST_IEvent *event = m_events.back();
29     m_events.pop_back();
30     m_handled_events.push_back(event);
31 
32     dispatchEvent(event);
33 }
34 
35 void GHOST_EventManager::dispatchEvent(GHOST_IEvent *event)
36 {
37     TConsumerVector::iterator iter;
38 
39     for (iter = m_consumers.begin(); iter != m_consumers.end(); ++iter) {
40         (*iter)->processEvent(event);
41     }
42 }

 11行,就是调用事件管理器m_eventManager来处理GHOST事件,35行的函数对事件进行处理,它是通过调用GHOST_CallbackEventConsumer类对象中的函数来实现,具体实现可参考GHOST_CallbackEventConsumer类代码。

至此,Blender中的event处理过程就结束了。

 

各种操作如果需要用户交互时,将使用事件处理器,

第13行,就是处理事件事件处理器中所有操作wm_event_do_handlers函数如下:

  1 /* called in main loop */
  2 /* goes over entire hierarchy:  events -> window -> screen -> area -> region */
  3 void wm_event_do_handlers(bContext *C)
  4 {
  5     wmWindowManager *wm = CTX_wm_manager(C);
  6     wmWindow *win;
  7 
  8     /* update key configuration before handling events */
  9     WM_keyconfig_update(wm);
 10     WM_manipulatorconfig_update(CTX_data_main(C));
 11 
 12     for (win = wm->windows.first; win; win = win->next) {
 13         bScreen *screen = WM_window_get_active_screen(win);
 14         wmEvent *event;
 15 
 16         /* some safty checks - these should always be set! */
 17         BLI_assert(WM_window_get_active_scene(win));
 18         BLI_assert(WM_window_get_active_screen(win));
 19         BLI_assert(WM_window_get_active_workspace(win));
 20 
 21         if (screen == NULL)
 22             wm_event_free_all(win);
 23         else {
 24             Scene *scene = WM_window_get_active_scene(win);
 25 
 26             if (scene) {
 27                 int is_playing_sound = BKE_sound_scene_playing(scene);
 28                 
 29                 if (is_playing_sound != -1) {
 30                     bool is_playing_screen;
 31                     CTX_wm_window_set(C, win);
 32                     CTX_data_scene_set(C, scene);
 33                     
 34                     is_playing_screen = (ED_screen_animation_playing(wm) != NULL);
 35 
 36                     if (((is_playing_sound == 1) && (is_playing_screen == 0)) ||
 37                         ((is_playing_sound == 0) && (is_playing_screen == 1)))
 38                     {
 39                         ED_screen_animation_play(C, -1, 1);
 40                     }
 41                     
 42                     if (is_playing_sound == 0) {
 43                         const float time = BKE_sound_sync_scene(scene);
 44                         if (isfinite(time)) {
 45                             int ncfra = time * (float)FPS + 0.5f;
 46                             if (ncfra != scene->r.cfra) {
 47                                 scene->r.cfra = ncfra;
 48                                 ViewLayer *view_layer = CTX_data_view_layer(C);
 49                                 Depsgraph *depsgraph = CTX_data_depsgraph(C);
 50                                 ED_update_for_newframe(CTX_data_main(C), scene, view_layer, depsgraph);
 51                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
 52                             }
 53                         }
 54                     }
 55                     
 56                     CTX_data_scene_set(C, NULL);
 57                     CTX_wm_screen_set(C, NULL);
 58                     CTX_wm_window_set(C, NULL);
 59                 }
 60             }
 61         }
 62         
 63         while ( (event = win->queue.first) ) {
 64             int action = WM_HANDLER_CONTINUE;
 65 
 66             /* active screen might change during handlers, update pointer */
 67             screen = WM_window_get_active_screen(win);
 68 
 69             if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
 70                 printf("\n%s: Handling event\n", __func__);
 71                 WM_event_print(event);
 72             }
 73 
 74             /* take care of pie event filter */
 75             if (wm_event_pie_filter(win, event)) {
 76                 if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
 77                     printf("\n%s: event filtered due to pie button pressed\n", __func__);
 78                 }
 79                 BLI_remlink(&win->queue, event);
 80                 wm_event_free(event);
 81                 continue;
 82             }
 83 
 84             CTX_wm_window_set(C, win);
 85 
 86             /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
 87             CTX_wm_area_set(C, area_event_inside(C, &event->x));
 88             CTX_wm_region_set(C, region_event_inside(C, &event->x));
 89             
 90             /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
 91             wm_window_make_drawable(wm, win);
 92             
 93             wm_region_mouse_co(C, event);
 94 
 95 
 96             /* first we do priority handlers, modal + some limited keymaps */
 97             action |= wm_handlers_do(C, event, &win->modalhandlers);
 98             
 99             /* fileread case */
100             if (CTX_wm_window(C) == NULL)
101                 return;
102             
103             /* check dragging, creates new event or frees, adds draw tag */
104             wm_event_drag_test(wm, win, event);
105             
106             /* builtin tweak, if action is break it removes tweak */
107             wm_tweakevent_test(C, event, action);
108 
109             if ((action & WM_HANDLER_BREAK) == 0) {
110                 ScrArea *sa;
111                 ARegion *ar;
112     
113                 /* Note: setting subwin active should be done here, after modal handlers have been done */
114                 if (event->type == MOUSEMOVE) {
115                     /* state variables in screen, cursors. Also used in wm_draw.c, fails for modal handlers though */
116                     ED_screen_set_subwinactive(C, event);
117                     /* for regions having custom cursors */
118                     wm_paintcursor_test(C, event);
119                 }
120 #ifdef WITH_INPUT_NDOF
121                 else if (event->type == NDOF_MOTION) {
122                     win->addmousemove = true;
123                 }
124 #endif
125 
126                 for (sa = screen->areabase.first; sa; sa = sa->next) {
127                     /* after restoring a screen from SCREENMAXIMIZED we have to wait
128                      * with the screen handling till the region coordinates are updated */
129                     if (screen->skip_handling == true) {
130                         /* restore for the next iteration of wm_event_do_handlers */
131                         screen->skip_handling = false;
132                         break;
133                     }
134 
135                     /* update azones if needed - done here because it needs to be independent from redraws */
136                     if (sa->flag & AREA_FLAG_ACTIONZONES_UPDATE) {
137                         ED_area_azones_update(sa, &event->x);
138                     }
139 
140                     if (wm_event_inside_i(event, &sa->totrct)) {
141                         CTX_wm_area_set(C, sa);
142 
143                         if ((action & WM_HANDLER_BREAK) == 0) {
144                             for (ar = sa->regionbase.first; ar; ar = ar->next) {
145                                 if (wm_event_inside_i(event, &ar->winrct)) {
146                                     CTX_wm_region_set(C, ar);
147                                     
148                                     /* call even on non mouse events, since the */
149                                     wm_region_mouse_co(C, event);
150 
151                                     if (!BLI_listbase_is_empty(&wm->drags)) {
152                                         /* does polls for drop regions and checks uibuts */
153                                         /* need to be here to make sure region context is true */
154                                         if (ELEM(event->type, MOUSEMOVE, EVT_DROP) || ISKEYMODIFIER(event->type)) {
155                                             wm_drags_check_ops(C, event);
156                                         }
157                                     }
158 
159 #ifdef USE_WORKSPACE_TOOL
160                                     /* How to solve properly?
161                                      *
162                                      * Handlers are stored in each region,
163                                      * however the tool-system swaps keymaps often and isn't stored
164                                      * per region.
165                                      *
166                                      * Need to investigate how this could be done better.
167                                      * We might need to add a more dynamic handler type that uses a callback
168                                      * to fetch its current keymap.
169                                      */
170                                     wmEventHandler sneaky_handler = {NULL};
171                                     if (ar->regiontype == RGN_TYPE_WINDOW) {
172                                         WorkSpace *workspace = WM_window_get_active_workspace(win);
173                                         if (workspace->tool.keymap[0] &&
174                                             workspace->tool.spacetype == sa->spacetype)
175                                         {
176                                             wmKeyMap *km = WM_keymap_find_all(
177                                                     C, workspace->tool.keymap, sa->spacetype, RGN_TYPE_WINDOW);
178                                             if (km != NULL) {
179                                                 sneaky_handler.keymap = km;
180                                                 /* Handle widgets first. */
181                                                 wmEventHandler *handler_last = ar->handlers.last;
182                                                 while (handler_last && handler_last->manipulator_map == NULL) {
183                                                     handler_last = handler_last->prev;
184                                                 }
185                                                 /* Head of list or after last manipulator. */
186                                                 BLI_insertlinkafter(&ar->handlers, handler_last, &sneaky_handler);
187                                             }
188                                         }
189                                     }
190 #endif /* USE_WORKSPACE_TOOL */
191 
192                                     action |= wm_handlers_do(C, event, &ar->handlers);
193 
194 #ifdef USE_WORKSPACE_TOOL
195                                     if (sneaky_handler.keymap) {
196                                         BLI_remlink(&ar->handlers, &sneaky_handler);
197                                     }
198 #endif /* USE_WORKSPACE_TOOL */
199 
200                                     /* fileread case (python), [#29489] */
201                                     if (CTX_wm_window(C) == NULL)
202                                         return;
203 
204                                     if (action & WM_HANDLER_BREAK)
205                                         break;
206                                 }
207                             }
208                         }
209 
210                         CTX_wm_region_set(C, NULL);
211 
212                         if ((action & WM_HANDLER_BREAK) == 0) {
213                             wm_region_mouse_co(C, event); /* only invalidates event->mval in this case */
214                             action |= wm_handlers_do(C, event, &sa->handlers);
215                         }
216                         CTX_wm_area_set(C, NULL);
217 
218                         /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
219                     }
220                 }
221                 
222                 if ((action & WM_HANDLER_BREAK) == 0) {
223                     /* also some non-modal handlers need active area/region */
224                     CTX_wm_area_set(C, area_event_inside(C, &event->x));
225                     CTX_wm_region_set(C, region_event_inside(C, &event->x));
226 
227                     wm_region_mouse_co(C, event);
228 
229                     action |= wm_handlers_do(C, event, &win->handlers);
230 
231                     /* fileread case */
232                     if (CTX_wm_window(C) == NULL)
233                         return;
234                 }
235 
236             }
237 
238             /* update previous mouse position for following events to use */
239             win->eventstate->prevx = event->x;
240             win->eventstate->prevy = event->y;
241 
242             /* unlink and free here, blender-quit then frees all */
243             BLI_remlink(&win->queue, event);
244             wm_event_free(event);
245             
246         }
247         
248         /* only add mousemove when queue was read entirely */
249         if (win->addmousemove && win->eventstate) {
250             wmEvent tevent = *(win->eventstate);
251             // printf("adding MOUSEMOVE %d %d\n", tevent.x, tevent.y);
252             tevent.type = MOUSEMOVE;
253             tevent.prevx = tevent.x;
254             tevent.prevy = tevent.y;
255             wm_event_add(win, &tevent);
256             win->addmousemove = 0;
257         }
258         
259         CTX_wm_window_set(C, NULL);
260     }
261 
262     /* update key configuration after handling events */
263     WM_keyconfig_update(wm);
264     WM_manipulatorconfig_update(CTX_data_main(C));
265 }

 接着调用wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)来处理

 1 /* this calls handlers twice - to solve (double-)click events二次调用,解决鼠标二击事件 */
 2 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
 3 {
 4     int action = wm_handlers_do_intern(C, event, handlers);
 5         
 6     /* fileread case */
 7     if (CTX_wm_window(C) == NULL)
 8         return action;
 9 
10     if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE, EVENT_NONE) && !ISTIMER(event->type)) {
11 
12         /* test for CLICK events */
13         if (wm_action_not_handled(action)) {
14             wmWindow *win = CTX_wm_window(C);
15             
16             /* eventstate stores if previous event was a KM_PRESS, in case that 
17              * wasn't handled, the KM_RELEASE will become a KM_CLICK */
18             
19             if (win && event->val == KM_PRESS) {
20                 win->eventstate->check_click = true;
21             }
22             
23             if (win && win->eventstate->prevtype == event->type) {
24                 
25                 if ((event->val == KM_RELEASE) &&
26                     (win->eventstate->prevval == KM_PRESS) &&
27                     (win->eventstate->check_click == true))
28                 {
29                     if ((abs(event->x - win->eventstate->prevclickx)) <= WM_EVENT_CLICK_WIGGLE_ROOM &&
30                         (abs(event->y - win->eventstate->prevclicky)) <= WM_EVENT_CLICK_WIGGLE_ROOM)
31                     {
32                         event->val = KM_CLICK;
33 
34                         if (G.debug & (G_DEBUG_HANDLERS)) {
35                             printf("%s: handling CLICK\n", __func__);
36                         }
37 
38                         action |= wm_handlers_do_intern(C, event, handlers);
39 
40                         event->val = KM_RELEASE;
41                     }
42                     else {
43                         win->eventstate->check_click = 0;
44                     }
45                 }
46                 else if (event->val == KM_DBL_CLICK) {
47                     event->val = KM_PRESS;
48                     action |= wm_handlers_do_intern(C, event, handlers);
49                     
50                     /* revert value if not handled */
51                     if (wm_action_not_handled(action)) {
52                         event->val = KM_DBL_CLICK;
53                     }
54                 }
55             }
56         }
57         else {
58             wmWindow *win = CTX_wm_window(C);
59 
60             if (win)
61                 win->eventstate->check_click = 0;
62         }
63     }
64     
65     return action;
66 }

 

posted @ 2018-05-20 17:45  平凡人  阅读(1526)  评论(1编辑  收藏  举报