• 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
    ");
     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
    ");
    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
    ");
    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("
    %s: Handling event
    ", __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("
    %s: event filtered due to pie button pressed
    ", __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
    ", 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
    ", __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 }
  • 相关阅读:
    JavaScript 对象
    Java条件语句
    函数的使用注意事项:
    函数的特点
    函数的格式
    for循环
    break和continue的区别和作用
    注意事项
    CSS浮动清除的方法
    转:Oracle 中union的用法
  • 原文地址:https://www.cnblogs.com/jiaping/p/8232822.html
Copyright © 2020-2023  润新知