diff options
Diffstat (limited to 'intern/ghost/intern/GHOST_SystemWin32.cpp')
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWin32.cpp | 232 |
1 files changed, 196 insertions, 36 deletions
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index f784d100db2..8178b9bdf1e 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -234,6 +234,11 @@ GHOST_SystemWin32::~GHOST_SystemWin32() toggleConsole(1); } +GHOST_TUns64 GHOST_SystemWin32::millisSinceStart(__int64 ms) const +{ + return (GHOST_TUns64)(ms - m_start * 1000 / m_freq); +} + GHOST_TUns64 GHOST_SystemWin32::performanceCounterToMillis(__int64 perf_ticks) const { // Calculate the time passed since system initialization. @@ -417,8 +422,8 @@ finished: /** * Dispose of a context. - * \param context Pointer to the context to be disposed. - * \return Indication of success. + * \param context Pointer to the context to be disposed. + * \return Indication of success. */ GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context) { @@ -709,9 +714,12 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, return key; } -//! note: this function can be extended to include other exotic cases as they arise. -// This function was added in response to bug [#25715] -// This is going to be a long list [T42426] +/** + * \note this function can be extended to include other exotic cases as they arise. + * + * This function was added in response to bug T25715. + * This is going to be a long list T42426. + */ GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const { GHOST_TKey key = GHOST_kKeyUnknown; @@ -938,23 +946,137 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, window->updateMouseCapture(MouseReleased); } - if (window->m_tabletInRange) { - if (window->useTabletAPI(GHOST_kTabletNative)) { - // Win32 Pointer processing handles input while in-range and in-contact events. - return NULL; - } + /* Check for active Wintab mouse emulation in addition to a tablet in range because a proximity + * leave event might have fired before the Windows mouse up event, thus there are still tablet + * events to grab. The described behavior was observed in a Wacom Bamboo CTE-450. */ + if (window->useTabletAPI(GHOST_kTabletWintab) && + (window->m_tabletInRange || window->wintabSysButPressed()) && + processWintabEvent(type, window, mask, window->getMousePressed())) { + /* Wintab processing only handles in-contact events. */ + return NULL; } return new GHOST_EventButton( - system->getMilliSeconds(), type, window, mask, window->getTabletData()); + system->getMilliSeconds(), type, window, mask, GHOST_TABLET_DATA_NONE); } -void GHOST_SystemWin32::processPointerEvents( +GHOST_TSuccess GHOST_SystemWin32::processWintabEvent(GHOST_TEventType type, + GHOST_WindowWin32 *window, + GHOST_TButtonMask mask, + bool mousePressed) +{ + GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + + /* Only process Wintab packets if we can correlate them to a Window's mouse button event. When a + * button event associated to a mouse button by Wintab occurs outside of WM_*BUTTON events, + * there's no way to tell if other simultaneously pressed non-mouse mapped buttons are associated + * to a modifier key (shift, alt, ctrl) or a system event (scroll, etc.) and thus it is not + * possible to determine if a mouse click event should occur. */ + if (!mousePressed && !window->wintabSysButPressed()) { + return GHOST_kFailure; + } + + std::vector<GHOST_WintabInfoWin32> wintabInfo; + if (!window->getWintabInfo(wintabInfo)) { + return GHOST_kFailure; + } + + auto wtiIter = wintabInfo.begin(); + + /* We only process events that correlate to a mouse button events, so there may exist Wintab + * button down events that were instead mapped to e.g. scroll still in the queue. We need to + * skip those and find the last button down mapped to mouse buttons. */ + if (!window->wintabSysButPressed()) { + /* Assume there may be no button down event currently in the queue. */ + wtiIter = wintabInfo.end(); + + for (auto it = wintabInfo.begin(); it != wintabInfo.end(); it++) { + if (it->type == GHOST_kEventButtonDown) { + wtiIter = it; + } + } + } + + bool unhandledButton = type != GHOST_kEventCursorMove; + + for (; wtiIter != wintabInfo.end(); wtiIter++) { + auto info = *wtiIter; + + switch (info.type) { + case GHOST_kEventButtonDown: { + /* While changing windows with a tablet, Window's mouse button events normally occur before + * tablet proximity events, so a button up event can't be differentiated as occurring from + * a Wintab tablet or a normal mouse and a Ghost button event will always be generated. + * + * If we were called during a button down event create a ghost button down event, otherwise + * don't duplicate the prior button down as it interrupts drawing immediately after + * changing a window. */ + system->pushEvent(new GHOST_EventCursor( + info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData)); + if (type == GHOST_kEventButtonDown && mask == info.button) { + system->pushEvent( + new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData)); + unhandledButton = false; + } + window->updateWintabSysBut(MousePressed); + break; + } + case GHOST_kEventCursorMove: + system->pushEvent(new GHOST_EventCursor( + info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData)); + break; + case GHOST_kEventButtonUp: + system->pushEvent( + new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData)); + if (type == GHOST_kEventButtonUp && mask == info.button) { + unhandledButton = false; + } + window->updateWintabSysBut(MouseReleased); + break; + default: + break; + } + } + + /* No Wintab button found correlating to the system button event, handle it too. + * + * Wintab button up events may be handled during WM_MOUSEMOVE, before their corresponding + * WM_*BUTTONUP event has fired, which results in two GHOST Button up events for a single Wintab + * associated button event. Alternatively this Windows button up event may have been generated + * from a non-stylus device such as a button on the tablet pad and needs to be handled for some + * workflows. + * + * The ambiguity introduced by Windows and Wintab buttons being asynchronous and having no + * definitive way to associate each, and that the Wintab API does not provide enough information + * to differentiate whether the stylus down is or is not modified by another button to a + * non-mouse mapping, means that we must pessimistically generate mouse up events when we are + * unsure of an association to prevent the mouse locking into a down state. */ + if (unhandledButton) { + if (!window->wintabSysButPressed()) { + GHOST_TInt32 x, y; + system->getCursorPosition(x, y); + system->pushEvent(new GHOST_EventCursor(system->getMilliSeconds(), + GHOST_kEventCursorMove, + window, + x, + y, + GHOST_TABLET_DATA_NONE)); + } + system->pushEvent(new GHOST_EventButton( + system->getMilliSeconds(), type, window, mask, GHOST_TABLET_DATA_NONE)); + } + + return GHOST_kSuccess; +} + +void GHOST_SystemWin32::processPointerEvent( UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventHandled) { std::vector<GHOST_PointerInfoWin32> pointerInfo; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); + /* Pointer events might fire when changing windows for a device which is set to use Wintab, even + * when when Wintab is left enabled but set to the bottom of Wintab overlap order. */ if (!window->useTabletAPI(GHOST_kTabletNative)) { return; } @@ -979,7 +1101,7 @@ void GHOST_SystemWin32::processPointerEvents( pointerInfo[0].tabletData)); break; case WM_POINTERDOWN: - // Move cursor to point of contact because GHOST_EventButton does not include position. + /* Move cursor to point of contact because GHOST_EventButton does not include position. */ system->pushEvent(new GHOST_EventCursor(pointerInfo[0].time, GHOST_kEventCursorMove, window, @@ -994,8 +1116,8 @@ void GHOST_SystemWin32::processPointerEvents( window->updateMouseCapture(MousePressed); break; case WM_POINTERUPDATE: - // Coalesced pointer events are reverse chronological order, reorder chronologically. - // Only contiguous move events are coalesced. + /* Coalesced pointer events are reverse chronological order, reorder chronologically. + * Only contiguous move events are coalesced. */ for (GHOST_TUns32 i = pointerInfo.size(); i-- > 0;) { system->pushEvent(new GHOST_EventCursor(pointerInfo[i].time, GHOST_kEventCursorMove, @@ -1015,11 +1137,6 @@ void GHOST_SystemWin32::processPointerEvents( break; case WM_POINTERLEAVE: window->m_tabletInRange = false; - system->pushEvent(new GHOST_EventButton(pointerInfo[0].time, - GHOST_kEventCursorMove, - window, - pointerInfo[0].buttonMask, - pointerInfo[0].tabletData)); break; default: break; @@ -1034,13 +1151,21 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind GHOST_TInt32 x_screen, y_screen; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); - if (window->m_tabletInRange) { - if (window->useTabletAPI(GHOST_kTabletNative)) { - // Tablet input handled in WM_POINTER* events. WM_MOUSEMOVE events in response to tablet - // input aren't normally generated when using WM_POINTER events, but manually moving the - // system cursor as we do in WM_POINTER handling does. + if (window->m_tabletInRange || window->wintabSysButPressed()) { + if (window->useTabletAPI(GHOST_kTabletWintab) && + processWintabEvent( + GHOST_kEventCursorMove, window, GHOST_kButtonMaskNone, window->getMousePressed())) { return NULL; } + else if (window->useTabletAPI(GHOST_kTabletNative)) { + /* Tablet input handled in WM_POINTER* events. WM_MOUSEMOVE events in response to tablet + * input aren't normally generated when using WM_POINTER events, but manually moving the + * system cursor as we do in WM_POINTER handling does. */ + return NULL; + } + + /* If using Wintab but no button event is currently active, + * fall through to default handling. */ } system->getCursorPosition(x_screen, y_screen); @@ -1073,7 +1198,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind window, x_screen + x_accum, y_screen + y_accum, - window->getTabletData()); + GHOST_TABLET_DATA_NONE); } } else { @@ -1082,7 +1207,7 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind window, x_screen, y_screen, - window->getTabletData()); + GHOST_TABLET_DATA_NONE); } return NULL; } @@ -1199,7 +1324,6 @@ GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, if (type == GHOST_kEventWindowActivate) { system->getWindowManager()->setActiveWindow(window); - window->bringTabletContextToFront(); } return new GHOST_Event(system->getMilliSeconds(), type, window); @@ -1227,6 +1351,20 @@ GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType, system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data)); } +void GHOST_SystemWin32::setTabletAPI(GHOST_TTabletAPI api) +{ + GHOST_System::setTabletAPI(api); + + GHOST_WindowManager *wm = getWindowManager(); + GHOST_WindowWin32 *activeWindow = (GHOST_WindowWin32 *)wm->getActiveWindow(); + + for (GHOST_IWindow *win : wm->getWindows()) { + GHOST_WindowWin32 *windowsWindow = (GHOST_WindowWin32 *)win; + windowsWindow->updateWintab(windowsWindow == activeWindow, + !::IsIconic(windowsWindow->getHWND())); + } +} + void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO *minmax) { minmax->ptMinTrackSize.x = 320; @@ -1464,14 +1602,19 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; //////////////////////////////////////////////////////////////////////// - // Tablet events, processed + // Wintab events, processed //////////////////////////////////////////////////////////////////////// - case WT_PACKET: - window->processWin32TabletEvent(wParam, lParam); + case WT_INFOCHANGE: { + window->processWintabInfoChangeEvent(lParam); break; - case WT_CSRCHANGE: - case WT_PROXIMITY: - window->processWin32TabletInitEvent(); + } + case WT_PROXIMITY: { + bool inRange = LOWORD(lParam); + window->processWintabProximityEvent(inRange); + break; + } + case WT_PACKET: + window->updatePendingWintabEvents(); break; //////////////////////////////////////////////////////////////////////// // Pointer events, processed @@ -1481,7 +1624,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_POINTERUPDATE: case WM_POINTERUP: case WM_POINTERLEAVE: - processPointerEvents(msg, window, wParam, lParam, eventHandled); + processPointerEvent(msg, window, wParam, lParam, eventHandled); break; //////////////////////////////////////////////////////////////////////// // Mouse events, processed @@ -1611,7 +1754,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, * will not be dispatched to OUR active window if we minimize one of OUR windows. */ if (LOWORD(wParam) == WA_INACTIVE) window->lostMouseCapture(); - window->processWin32TabletActivateEvent(GET_WM_ACTIVATE_STATE(wParam, lParam)); + + window->updateWintab(LOWORD(wParam) != WA_INACTIVE, !::IsIconic(window->getHWND())); + lResult = ::DefWindowProc(hwnd, msg, wParam, lParam); break; } @@ -1669,6 +1814,14 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, else { event = processWindowEvent(GHOST_kEventWindowSize, window); } + + /* Window might be minimized while inactive. When a window is inactive but not minimized, + * Wintab is left enabled (to catch the case where a pen is used to activate a window). + * When an inactive window is minimized, we need to disable Wintab. */ + if (msg == WM_SIZE && wParam == SIZE_MINIMIZED) { + window->updateWintab(false, false); + } + break; case WM_CAPTURECHANGED: window->lostMouseCapture(); @@ -1719,6 +1872,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, SWP_NOZORDER | SWP_NOACTIVATE); } break; + case WM_DISPLAYCHANGE: + for (GHOST_IWindow *iter_win : system->getWindowManager()->getWindows()) { + GHOST_WindowWin32 *iter_win32win = (GHOST_WindowWin32 *)iter_win; + iter_win32win->processWintabDisplayChangeEvent(); + } + break; //////////////////////////////////////////////////////////////////////// // Window events, ignored //////////////////////////////////////////////////////////////////////// @@ -1912,6 +2071,7 @@ void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const } } +/* -------------------------------------------------------------------- */ /** \name Message Box * \{ */ GHOST_TSuccess GHOST_SystemWin32::showMessageBox(const char *title, |