Changeset View
Standalone View
intern/ghost/intern/GHOST_SystemWin32.cpp
| /* SPDX-License-Identifier: GPL-2.0-or-later | /* SPDX-License-Identifier: GPL-2.0-or-later | ||||
| * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ | * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ | ||||
| /** \file | /** \file | ||||
| * \ingroup GHOST | * \ingroup GHOST | ||||
| */ | */ | ||||
| #include "GHOST_SystemWin32.h" | #include "GHOST_SystemWin32.h" | ||||
| #include "GHOST_ContextD3D.h" | #include "GHOST_ContextD3D.h" | ||||
| #include "GHOST_EventDragnDrop.h" | #include "GHOST_EventDragnDrop.h" | ||||
| #include "GHOST_EventTrackpad.h" | |||||
| #ifndef _WIN32_IE | #ifndef _WIN32_IE | ||||
| # define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */ | # define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */ | ||||
| #endif | #endif | ||||
| #include <commctrl.h> | #include <commctrl.h> | ||||
| #include <dwmapi.h> | #include <dwmapi.h> | ||||
| #include <psapi.h> | #include <psapi.h> | ||||
| ▲ Show 20 Lines • Show All 391 Lines • ▼ Show 20 Lines | #else | ||||
| } | } | ||||
| #endif | #endif | ||||
| } | } | ||||
| if (timerMgr->fireTimers(getMilliSeconds())) { | if (timerMgr->fireTimers(getMilliSeconds())) { | ||||
| hasEventHandled = true; | hasEventHandled = true; | ||||
| } | } | ||||
| driveTrackpad(); | |||||
| // Process all the events waiting for us | // Process all the events waiting for us | ||||
| while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) { | while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) { | ||||
| // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data. | // TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data. | ||||
| // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar. | // Needed for MapVirtualKey or if we ever need to get chars from wm_ime_char or similar. | ||||
| ::TranslateMessage(&msg); | ::TranslateMessage(&msg); | ||||
| ::DispatchMessageW(&msg); | ::DispatchMessageW(&msg); | ||||
| hasEventHandled = true; | hasEventHandled = true; | ||||
| } | } | ||||
| processTrackpad(); | |||||
nicholas_rishel: The active window can change while events are handled (and iirc can be destructed to) so this… | |||||
| /* PeekMessage above is allowed to dispatch messages to the wndproc without us | /* PeekMessage above is allowed to dispatch messages to the wndproc without us | ||||
Done Inline ActionsCheck for null. nicholas_rishel: Check for null. | |||||
Done Inline ActionsI found that the direct manipulation handlers are called during DispatchMessageW above, so probably best to move this block before the PeekMessageW loop. GetTouchpadInfo discussed in another comment should occur after PeekMessageW. Here is where you'd call system->pushEvent(new GHOST_EventTrackpad(...). nicholas_rishel: I found that the direct manipulation handlers are called during `DispatchMessageW` above, so… | |||||
| * noticing, so we need to check the event manager here to see if there are | * noticing, so we need to check the event manager here to see if there are | ||||
| * events waiting in the queue. | * events waiting in the queue. | ||||
| */ | */ | ||||
| hasEventHandled |= this->m_eventManager->getNumEvents() > 0; | hasEventHandled |= this->m_eventManager->getNumEvents() > 0; | ||||
| } while (waitForEvent && !hasEventHandled); | } while (waitForEvent && !hasEventHandled); | ||||
| return hasEventHandled; | return hasEventHandled; | ||||
| ▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons &buttons) const | ||||
| down = HIBYTE(::GetAsyncKeyState(VK_RBUTTON)) != 0; | down = HIBYTE(::GetAsyncKeyState(VK_RBUTTON)) != 0; | ||||
| buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down); | buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down); | ||||
| return GHOST_kSuccess; | return GHOST_kSuccess; | ||||
| } | } | ||||
| GHOST_TSuccess GHOST_SystemWin32::init() | GHOST_TSuccess GHOST_SystemWin32::init() | ||||
| { | { | ||||
| GHOST_TSuccess success = GHOST_System::init(); | GHOST_TSuccess success = GHOST_System::init(); | ||||
| InitCommonControls(); | InitCommonControls(); | ||||
Done Inline ActionsOnly the active window should be updated. nicholas_rishel: Only the active window should be updated. | |||||
| /* Disable scaling on high DPI displays on Vista */ | /* Disable scaling on high DPI displays on Vista */ | ||||
| SetProcessDPIAware(); | SetProcessDPIAware(); | ||||
Not Done Inline ActionsWhat is this updating exactly? It's not clear to me that it's correct to update things after multiple events have been handled, rather than after every event or specific types of events.. brecht: What is this updating exactly?
It's not clear to me that it's correct to update things after… | |||||
Not Done Inline ActionsThis updates viewport through IDirectManipulationUpdateManager because it's configured in DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE mode. After that OnContentUpdated is called and trackpad events are added to the queue if needed. I'm not sure where this code should be. I also tried other update modes. It works without manual updating, but there is no inertia for some reason. pembem22: This updates viewport through `IDirectManipulationUpdateManager` because it's configured in… | |||||
Not Done Inline ActionsThe update manager's intended use was to be passed to DirectComposition which drives the updates. This is as good a place as any to emulate it's behavior, afaik there's no way to get better granularity because the only relevant event from Precision Touchpad devices is DM_POINTERHITTEST which only triggers when the event starts. nicholas_rishel: The update manager's intended use was to be passed to DirectComposition which drives the… | |||||
| initRawInput(); | initRawInput(); | ||||
| m_lfstart = ::GetTickCount(); | m_lfstart = ::GetTickCount(); | ||||
| // Determine whether this system has a high frequency performance counter. */ | // Determine whether this system has a high frequency performance counter. */ | ||||
| m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE; | m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE; | ||||
| if (m_hasPerformanceCounter) { | if (m_hasPerformanceCounter) { | ||||
| GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n"); | GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n"); | ||||
| ::QueryPerformanceCounter((LARGE_INTEGER *)&m_start); | ::QueryPerformanceCounter((LARGE_INTEGER *)&m_start); | ||||
| ▲ Show 20 Lines • Show All 897 Lines • ▼ Show 20 Lines | case 3: // buttons | ||||
| m_ndofManager->updateButtons(button_bits, now); | m_ndofManager->updateButtons(button_bits, now); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return eventSent; | return eventSent; | ||||
| } | } | ||||
| #endif // WITH_INPUT_NDOF | #endif // WITH_INPUT_NDOF | ||||
| void GHOST_SystemWin32::driveTrackpad() | |||||
| { | |||||
| GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>( | |||||
| getWindowManager()->getActiveWindow()); | |||||
| if (active_window) { | |||||
| active_window->updateDirectManipulation(); | |||||
| } | |||||
| } | |||||
| void GHOST_SystemWin32::processTrackpad() | |||||
| { | |||||
| GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>( | |||||
| getWindowManager()->getActiveWindow()); | |||||
| if (!active_window) { | |||||
| return; | |||||
| } | |||||
| GHOST_TTrackpadInfo trackpad_info = active_window->getTrackpadInfo(); | |||||
| GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); | |||||
| int32_t cursor_x, cursor_y; | |||||
| system->getCursorPosition(cursor_x, cursor_y); | |||||
| if (trackpad_info.x != 0 || trackpad_info.y != 0) { | |||||
| system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(), | |||||
| active_window, | |||||
| GHOST_kTrackpadEventScroll, | |||||
| cursor_x, | |||||
| cursor_y, | |||||
| trackpad_info.x, | |||||
| trackpad_info.y, | |||||
| trackpad_info.isScrollDirectionInverted)); | |||||
| } | |||||
| if (trackpad_info.scale != 0) { | |||||
| system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(), | |||||
| active_window, | |||||
| GHOST_kTrackpadEventMagnify, | |||||
| cursor_x, | |||||
| cursor_y, | |||||
| trackpad_info.scale, | |||||
| 0, | |||||
| false)); | |||||
| } | |||||
| } | |||||
| LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | ||||
| { | { | ||||
| GHOST_Event *event = NULL; | GHOST_Event *event = NULL; | ||||
| bool eventHandled = false; | bool eventHandled = false; | ||||
| LRESULT lResult = 0; | LRESULT lResult = 0; | ||||
| GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); | GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); | ||||
| #ifdef WITH_INPUT_IME | #ifdef WITH_INPUT_IME | ||||
| ▲ Show 20 Lines • Show All 313 Lines • ▼ Show 20 Lines | #endif /* WITH_INPUT_IME */ | ||||
| } | } | ||||
| else if ((short)HIWORD(wParam) == XBUTTON2) { | else if ((short)HIWORD(wParam) == XBUTTON2) { | ||||
| event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5); | event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5); | ||||
| } | } | ||||
| break; | break; | ||||
| case WM_LBUTTONUP: | case WM_LBUTTONUP: | ||||
| event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft); | event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft); | ||||
| break; | break; | ||||
| case WM_MBUTTONUP: | case WM_MBUTTONUP: | ||||
Done Inline ActionsMissing break;. brecht: Missing `break;`. | |||||
| event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle); | event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle); | ||||
| break; | break; | ||||
| case WM_RBUTTONUP: | case WM_RBUTTONUP: | ||||
| event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight); | event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight); | ||||
| break; | break; | ||||
| case WM_XBUTTONUP: | case WM_XBUTTONUP: | ||||
| if ((short)HIWORD(wParam) == XBUTTON1) { | if ((short)HIWORD(wParam) == XBUTTON1) { | ||||
| event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4); | event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4); | ||||
| ▲ Show 20 Lines • Show All 206 Lines • ▼ Show 20 Lines | #endif | ||||
| // Then move and resize window | // Then move and resize window | ||||
| SetWindowPos(hwnd, | SetWindowPos(hwnd, | ||||
| NULL, | NULL, | ||||
| suggestedWindowRect->left, | suggestedWindowRect->left, | ||||
| suggestedWindowRect->top, | suggestedWindowRect->top, | ||||
| suggestedWindowRect->right - suggestedWindowRect->left, | suggestedWindowRect->right - suggestedWindowRect->left, | ||||
| suggestedWindowRect->bottom - suggestedWindowRect->top, | suggestedWindowRect->bottom - suggestedWindowRect->top, | ||||
| SWP_NOZORDER | SWP_NOACTIVATE); | SWP_NOZORDER | SWP_NOACTIVATE); | ||||
| window->updateDPI(); | |||||
| } | } | ||||
| break; | break; | ||||
| case WM_DISPLAYCHANGE: { | case WM_DISPLAYCHANGE: { | ||||
| GHOST_Wintab *wt = window->getWintab(); | GHOST_Wintab *wt = window->getWintab(); | ||||
| if (wt) { | if (wt) { | ||||
| wt->remapCoordinates(); | wt->remapCoordinates(); | ||||
| } | } | ||||
| break; | break; | ||||
| ▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | #endif | ||||
| * when a timer expires. You can process the message by providing a WM_TIMER | * when a timer expires. You can process the message by providing a WM_TIMER | ||||
| * case in the window procedure. Otherwise, the default window procedure will | * case in the window procedure. Otherwise, the default window procedure will | ||||
| * call the TimerProc callback function specified in the call to the SetTimer | * call the TimerProc callback function specified in the call to the SetTimer | ||||
| * function used to install the timer. | * function used to install the timer. | ||||
| * | * | ||||
| * In GHOST, we let DefWindowProc call the timer callback. | * In GHOST, we let DefWindowProc call the timer callback. | ||||
| */ | */ | ||||
| break; | break; | ||||
| case DM_POINTERHITTEST: | |||||
| /* The DM_POINTERHITTEST message is sent to a window, when pointer input is first | |||||
| * detected, in order to determine the most probable input target for Direct | |||||
| * Manipulation. */ | |||||
| window->onPointerHitTest(wParam); | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| // Event found for a window before the pointer to the class has been set. | // Event found for a window before the pointer to the class has been set. | ||||
| GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n"); | GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n"); | ||||
| /* These are events we typically miss at this point: | /* These are events we typically miss at this point: | ||||
| * WM_GETMINMAXINFO 0x24 | * WM_GETMINMAXINFO 0x24 | ||||
| * WM_NCCREATE 0x81 | * WM_NCCREATE 0x81 | ||||
| ▲ Show 20 Lines • Show All 257 Lines • Show Last 20 Lines | |||||
The active window can change while events are handled (and iirc can be destructed to) so this pointer may be incorrect or invalid by this point. We need to get it again. My preferred fix is to pull out the following logic into a system->processTrackpad function and get the active window in the function.