Compare commits

...

11 Commits

Author SHA1 Message Date
578f82d2ee Add tablet data to mouse input. 2021-09-05 20:17:28 -07:00
1a419c9f4e [WIP] Continuous grab for tablets
This revision modifies mouse and tablet handling to allow both to interchangably accumulate during continuous grab. For tablet input in pen mode, grab accumulates over the change in position while the tablet is in range. For a tablet in mouse mode, grab accumulates as a normal mouse accumulates.

Also adds ability for operators to force continuous grab. This is necessary for the walk operator which has been rewritten to take advantage of tablet continuous grab.

Differential Revision: https://developer.blender.org/D12408
2021-09-05 20:12:00 -07:00
3af3e77e67 Cleanup 2021-09-05 16:41:33 -07:00
1a00134ab0 Converted walk operator to use continuous grab. Mouse and tablet are now opaque to the operator handling.
Continous grab has to be forced even if user has disabled it.
2021-09-05 11:51:43 -07:00
a8f7bfa213 Continuous grab mostly working for Wintab and Windows Ink. Some minor race conditions for Windows Ink when a pen quickly enters then leaves range shortly after a mouse event.
# Conflicts:
#	intern/ghost/intern/GHOST_SystemWin32.cpp
2021-09-05 11:51:43 -07:00
ccd5264344 Consolidate repeat actions. 2021-09-04 18:18:56 -07:00
d06fb5a92b Improve cursor wrap reliability. 2021-09-04 18:18:56 -07:00
aea5b4fbfa Cleanup: remove unmodified return variable. 2021-09-04 18:18:56 -07:00
fa80af4455 Fix Windows cursor wrap jumps. 2021-09-04 18:18:56 -07:00
bcab006892 Supply tablet data with 2021-09-04 18:18:56 -07:00
fa0e0bcf32 Cleanup: replace defines with static const in walk/fly operators.
No functional changes.
2021-09-04 18:18:56 -07:00
12 changed files with 493 additions and 218 deletions

View File

@@ -108,29 +108,35 @@
static void initRawInput() static void initRawInput()
{ {
#ifdef WITH_INPUT_NDOF #ifdef WITH_INPUT_NDOF
# define DEVICE_COUNT 2 # define DEVICE_COUNT 3
#else #else
# define DEVICE_COUNT 1 # define DEVICE_COUNT 2
#endif #endif
RAWINPUTDEVICE devices[DEVICE_COUNT]; RAWINPUTDEVICE devices[DEVICE_COUNT];
memset(devices, 0, DEVICE_COUNT * sizeof(RAWINPUTDEVICE)); memset(devices, 0, DEVICE_COUNT * sizeof(RAWINPUTDEVICE));
// Initiates WM_INPUT messages from keyboard /* Initiates WM_INPUT messages from keyboard.
// That way GHOST can retrieve true keys * That way GHOST can retrieve true keys.
*/
devices[0].usUsagePage = 0x01; devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */ devices[0].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
#ifdef WITH_INPUT_NDOF /* Initiates WM_INPUT messages for mouse.
// multi-axis mouse (SpaceNavigator, etc.) * Used to differentiate absolute and relative cursor movement.
* */
devices[1].usUsagePage = 0x01; devices[1].usUsagePage = 0x01;
devices[1].usUsage = 0x08; devices[1].usUsage = 0x02;
#ifdef WITH_INPUT_NDOF
/* multi - axis mouse(SpaceNavigator, etc.) */
devices[2].usUsagePage = 0x01;
devices[2].usUsage = 0x08;
#endif #endif
if (RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE))) if (!RegisterRawInputDevices(devices, DEVICE_COUNT, sizeof(RAWINPUTDEVICE))) {
; // yay!
else
GHOST_PRINTF("could not register for RawInput: %d\n", (int)GetLastError()); GHOST_PRINTF("could not register for RawInput: %d\n", (int)GetLastError());
}
#undef DEVICE_COUNT #undef DEVICE_COUNT
} }
@@ -150,6 +156,8 @@ GHOST_SystemWin32::GHOST_SystemWin32()
// blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI. // blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI.
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
EnableMouseInPointer(true);
// Check if current keyboard layout uses AltGr and save keylayout ID for // Check if current keyboard layout uses AltGr and save keylayout ID for
// specialized handling if keys like VK_OEM_*. I.e. french keylayout // specialized handling if keys like VK_OEM_*. I.e. french keylayout
// generates VK_OEM_8 for their exclamation key (key left of right shift) // generates VK_OEM_8 for their exclamation key (key left of right shift)
@@ -160,6 +168,11 @@ GHOST_SystemWin32::GHOST_SystemWin32()
#ifdef WITH_INPUT_NDOF #ifdef WITH_INPUT_NDOF
m_ndofManager = new GHOST_NDOFManagerWin32(*this); m_ndofManager = new GHOST_NDOFManagerWin32(*this);
#endif #endif
POINT pt;
::GetCursorPos(&pt);
m_lastX = pt.x;
m_lastY = pt.y;
} }
GHOST_SystemWin32::~GHOST_SystemWin32() GHOST_SystemWin32::~GHOST_SystemWin32()
@@ -461,6 +474,8 @@ GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(int32_t &x, int32_t &y) cons
GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(int32_t x, int32_t y) GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(int32_t x, int32_t y)
{ {
/* Don't use SendInput or mouse_event functions. Unlike SetCursorPos, they are received by
* WM_INPUT processing and interrupt differentiating absolute vs relative mouse input. */
if (!::GetActiveWindow()) if (!::GetActiveWindow())
return GHOST_kFailure; return GHOST_kFailure;
return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure; return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
@@ -878,7 +893,7 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask, td); return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask, td);
} }
void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window) void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window, UINT genSerial)
{ {
GHOST_Wintab *wt = window->getWintab(); GHOST_Wintab *wt = window->getWintab();
if (!wt) { if (!wt) {
@@ -888,7 +903,7 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
std::vector<GHOST_WintabInfoWin32> wintabInfo; std::vector<GHOST_WintabInfoWin32> wintabInfo;
wt->getInput(wintabInfo); wt->getInput(wintabInfo, genSerial);
/* Wintab provided coordinates are untrusted until a Wintab and Win32 button down event match. /* Wintab provided coordinates are untrusted until a Wintab and Win32 button down event match.
* This is checked on every button down event, and revoked if there is a mismatch. This can * This is checked on every button down event, and revoked if there is a mismatch. This can
@@ -896,9 +911,7 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
* *
* If Wintab was never trusted while processing this Win32 event, a fallback Ghost cursor move * If Wintab was never trusted while processing this Win32 event, a fallback Ghost cursor move
* event is created at the position of the Win32 WT_PACKET event. */ * event is created at the position of the Win32 WT_PACKET event. */
bool mouseMoveHandled; bool useWintabPos = wt->trustCoordinates();
bool useWintabPos;
mouseMoveHandled = useWintabPos = wt->trustCoordinates();
for (GHOST_WintabInfoWin32 &info : wintabInfo) { for (GHOST_WintabInfoWin32 &info : wintabInfo) {
switch (info.type) { switch (info.type) {
@@ -908,8 +921,30 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
} }
wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y); wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y);
if (system->m_firstProximity) {
system->m_firstProximity = false;
if (window->getCursorGrabModeIsWarp()) {
system->warpGrabAccum(window, info.x, info.y);
}
}
int x = info.x;
int y = info.y;
if (window->getCursorGrabModeIsWarp()) {
int32_t x_accum, y_accum;
window->getCursorGrabAccum(x_accum, y_accum);
x += x_accum;
y += y_accum;
}
system->pushEvent(new GHOST_EventCursor( system->pushEvent(new GHOST_EventCursor(
info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData)); info.time, GHOST_kEventCursorMove, window, x, y, info.tabletData));
system->m_lastX = info.x;
system->m_lastY = info.y;
break; break;
} }
@@ -948,14 +983,23 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
/* Move cursor to button location, to prevent incorrect cursor position when /* Move cursor to button location, to prevent incorrect cursor position when
* transitioning from unsynchronized Win32 to Wintab cursor control. */ * transitioning from unsynchronized Win32 to Wintab cursor control. */
wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y); wt->mapWintabToSysCoordinates(info.x, info.y, info.x, info.y);
int x = info.x;
int y = info.y;
if (window->getCursorGrabModeIsWarp()) {
int32_t x_accum, y_accum;
window->getCursorGrabAccum(x_accum, y_accum);
x += x_accum;
y += y_accum;
}
system->pushEvent(new GHOST_EventCursor( system->pushEvent(new GHOST_EventCursor(
info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData)); info.time, GHOST_kEventCursorMove, window, x, y, info.tabletData));
window->updateMouseCapture(MousePressed); window->updateMouseCapture(MousePressed);
system->pushEvent( system->pushEvent(
new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData)); new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
mouseMoveHandled = true;
break; break;
} }
} }
@@ -996,25 +1040,18 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
break; break;
} }
} }
/* Fallback cursor movement if Wintab position were never trusted while processing this event. */
if (!mouseMoveHandled) {
DWORD pos = GetMessagePos();
int x = GET_X_LPARAM(pos);
int y = GET_Y_LPARAM(pos);
/* TODO supply tablet data */
system->pushEvent(new GHOST_EventCursor(
system->getMilliSeconds(), GHOST_kEventCursorMove, window, x, y, GHOST_TABLET_DATA_NONE));
}
} }
void GHOST_SystemWin32::processPointerEvent( void GHOST_SystemWin32::processPointerEvent(
UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventHandled) UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventHandled)
{ {
int32_t pointerId = GET_POINTERID_WPARAM(wParam);
POINTER_INPUT_TYPE inputType;
GetPointerType(pointerId, &inputType);
/* Pointer events might fire when changing windows for a device which is set to use Wintab, /* Pointer events might fire when changing windows for a device which is set to use Wintab,
* even when Wintab is left enabled but set to the bottom of Wintab overlap order. */ * even when Wintab is left enabled but set to the bottom of Wintab overlap order. */
if (!window->usingTabletAPI(GHOST_kTabletWinPointer)) { if (!window->usingTabletAPI(GHOST_kTabletWinPointer) && inputType != PT_MOUSE) {
return; return;
} }
@@ -1022,6 +1059,13 @@ void GHOST_SystemWin32::processPointerEvent(
std::vector<GHOST_PointerInfoWin32> pointerInfo; std::vector<GHOST_PointerInfoWin32> pointerInfo;
if (window->getPointerInfo(pointerInfo, wParam, lParam) != GHOST_kSuccess) { if (window->getPointerInfo(pointerInfo, wParam, lParam) != GHOST_kSuccess) {
if (inputType == PT_MOUSE && type == WM_POINTERUPDATE) {
GHOST_EventCursor *event = processCursorEvent(window);
if (event) {
system->pushEvent(event);
eventHandled = true;
}
}
return; return;
} }
@@ -1030,25 +1074,43 @@ void GHOST_SystemWin32::processPointerEvent(
/* Coalesced pointer events are reverse chronological order, reorder chronologically. /* Coalesced pointer events are reverse chronological order, reorder chronologically.
* Only contiguous move events are coalesced. */ * Only contiguous move events are coalesced. */
for (uint32_t i = pointerInfo.size(); i-- > 0;) { for (uint32_t i = pointerInfo.size(); i-- > 0;) {
system->pushEvent(new GHOST_EventCursor(pointerInfo[i].time, int x = pointerInfo[i].pixelLocation.x;
GHOST_kEventCursorMove, int y = pointerInfo[i].pixelLocation.y;
window,
pointerInfo[i].pixelLocation.x, if (window->getCursorGrabModeIsWarp()) {
pointerInfo[i].pixelLocation.y, int32_t x_accum, y_accum;
pointerInfo[i].tabletData)); window->getCursorGrabAccum(x_accum, y_accum);
x += x_accum;
y += y_accum;
}
system->pushEvent(new GHOST_EventCursor(
pointerInfo[i].time, GHOST_kEventCursorMove, window, x, y, pointerInfo[i].tabletData));
}
if (pointerInfo.size() > 0) {
GHOST_PointerInfoWin32 front = pointerInfo.front();
system->m_lastX = front.pixelLocation.x;
system->m_lastY = front.pixelLocation.y;
} }
/* Leave event unhandled so that system cursor is moved. */ /* Leave event unhandled so that system cursor is moved. */
break; break;
case WM_POINTERDOWN: case WM_POINTERDOWN: {
int x = pointerInfo[0].pixelLocation.x;
int y = pointerInfo[0].pixelLocation.y;
if (window->getCursorGrabModeIsWarp()) {
int32_t x_accum, y_accum;
window->getCursorGrabAccum(x_accum, y_accum);
x += x_accum;
y += y_accum;
}
/* 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, system->pushEvent(new GHOST_EventCursor(
GHOST_kEventCursorMove, pointerInfo[0].time, GHOST_kEventCursorMove, window, x, y, pointerInfo[0].tabletData));
window,
pointerInfo[0].pixelLocation.x,
pointerInfo[0].pixelLocation.y,
pointerInfo[0].tabletData));
system->pushEvent(new GHOST_EventButton(pointerInfo[0].time, system->pushEvent(new GHOST_EventButton(pointerInfo[0].time,
GHOST_kEventButtonDown, GHOST_kEventButtonDown,
window, window,
@@ -1060,6 +1122,7 @@ void GHOST_SystemWin32::processPointerEvent(
eventHandled = true; eventHandled = true;
break; break;
}
case WM_POINTERUP: case WM_POINTERUP:
system->pushEvent(new GHOST_EventButton(pointerInfo[0].time, system->pushEvent(new GHOST_EventButton(pointerInfo[0].time,
GHOST_kEventButtonUp, GHOST_kEventButtonUp,
@@ -1079,20 +1142,49 @@ void GHOST_SystemWin32::processPointerEvent(
GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window) GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
{ {
int32_t x_screen, y_screen;
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
if (window->getTabletData().Active != GHOST_kTabletModeNone) { /* Don't use GetPointerInfo to obtain mouse position. It is identical to GetMessagePos for
/* While pen devices are in range, cursor movement is handled by tablet input processing. */ * WM_POINTERUPDATE messages, but returns outdated position for WM_POINTERLEAVE messages thus
return NULL; * doesn't warp when it should.
* This difference only seems to occur while RAWINPUT mouse processing is enabled. */
DWORD pos = ::GetMessagePos();
int32_t x_screen = GET_X_LPARAM(pos);
int32_t y_screen = GET_Y_LPARAM(pos);
/* TODO supply tablet data */
GHOST_TabletData td = window->getTabletData();
td.Pressure = 1.0f;
if (td.Active != GHOST_kTabletModeNone) {
GHOST_Wintab *wt = window->getWintab();
if (window->usingTabletAPI(GHOST_kTabletWintab) && wt && !wt->trustCoordinates()) {
/* Fallback cursor movement if Wintab position were never trusted while processing this
* event. This may happen if the tablet coordinate scaling is off, hasn't yet been
verified,
* or if the tablet is in mouse mode. */
if (system->m_firstProximity) {
system->m_firstProximity = false;
if (window->getCursorGrabModeIsWarp()) {
system->warpGrabAccum(window, x_screen, y_screen);
}
}
}
else {
/* While pen devices are in range, cursor movement is handled by tablet input processing.
*/
return NULL;
}
} }
system->getCursorPosition(x_screen, y_screen); int32_t x_accum = 0;
int32_t y_accum = 0;
if (window->getCursorGrabModeIsWarp()) { if (window->getCursorGrabModeIsWarp()) {
int32_t x_new = x_screen; int32_t x_new = x_screen;
int32_t y_new = y_screen; int32_t y_new = y_screen;
int32_t x_accum, y_accum;
GHOST_Rect bounds; GHOST_Rect bounds;
/* Fallback to window bounds. */ /* Fallback to window bounds. */
@@ -1105,30 +1197,45 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis()); bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis());
window->getCursorGrabAccum(x_accum, y_accum); window->getCursorGrabAccum(x_accum, y_accum);
if (x_new != x_screen || y_new != y_screen) { int32_t warpX = x_new - x_screen;
/* When wrapping we don't need to add an event because the setCursorPosition call will cause int32_t warpY = y_new - y_screen;
* a new event after. */ if ((warpX || warpY) && !system->m_absoluteCursor) {
system->setCursorPosition(x_new, y_new); /* wrap */ system->setCursorPosition(x_new, y_new); /* wrap */
window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
/* We may be in an event before cursor wrap has taken effect */
if (window->m_activeWarpX >= 0 && warpX < 0 || window->m_activeWarpX <= 0 && warpX > 0) {
x_accum -= warpX;
}
if (window->m_activeWarpY >= 0 && warpY < 0 || window->m_activeWarpY <= 0 && warpY > 0) {
y_accum -= warpY;
}
window->setCursorGrabAccum(x_accum, y_accum);
window->m_activeWarpX = warpX;
window->m_activeWarpY = warpY;
/* Accum not relative to new cursor position, update screen position so the event will be
* placed correctly. */
x_screen = x_new;
y_screen = y_new;
} }
else { else {
return new GHOST_EventCursor(system->getMilliSeconds(), window->m_activeWarpX = 0;
GHOST_kEventCursorMove, window->m_activeWarpY = 0;
window,
x_screen + x_accum,
y_screen + y_accum,
GHOST_TABLET_DATA_NONE);
} }
} }
else {
return new GHOST_EventCursor(system->getMilliSeconds(), system->m_lastX = x_screen;
GHOST_kEventCursorMove, system->m_lastY = y_screen;
window,
x_screen, return new GHOST_EventCursor(system->getMilliSeconds(),
y_screen, GHOST_kEventCursorMove,
GHOST_TABLET_DATA_NONE); window,
} x_screen + x_accum,
return NULL; y_screen + y_accum,
td);
} }
void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam) void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
@@ -1454,6 +1561,18 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
eventHandled = true; eventHandled = true;
} }
break; break;
case RIM_TYPEMOUSE: {
/* We would like to correlate absolute/relative input to specific devices in
* WM_POINTER, but all PT_MOUSE pointers' device handle contains the same virtual
* mouse thus loose device information. */
bool now = raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE;
if (system->m_absoluteCursor ^ now) {
printf("%d\n", now);
}
system->m_absoluteCursor = raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE;
eventHandled = true;
break;
}
#endif #endif
} }
break; break;
@@ -1606,9 +1725,39 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
if (inRange) { if (inRange) {
/* Some devices don't emit WT_CSRCHANGE events, so update cursor info here. */ /* Some devices don't emit WT_CSRCHANGE events, so update cursor info here. */
wt->updateCursorInfo(); wt->updateCursorInfo();
wt->enterRange();
window->m_mousePresent = true;
if (window->getCursorGrabModeIsWarp()) {
system->m_firstProximity = true;
/* Mouse events are still generated asynchronously and may have been processed
* while Wintab was out of range, thus we may be in the middle of a cursor wrap
* event. */
window->m_activeWarpX = 0;
window->m_activeWarpY = 0;
}
} }
else { else {
wt->leaveRange(); wt->leaveRange();
/* Check if pointer device left range while outside of the window. This is necessary
* because WM_POINTERENTER and WM_POINTERLEAVE events don't fire when a pen enters
* and leaves without hoving over the window, but the cursor is moved resulting in a
* WM_POINTERLEAVE event for the mouse. */
int32_t msgPosX;
int32_t msgPosY;
system->getCursorPosition(msgPosX, msgPosY);
GHOST_Rect bounds;
window->getClientBounds(bounds);
if (!bounds.isInside(msgPosX, msgPosY)) {
window->m_mousePresent = false;
}
break;
} }
} }
eventHandled = true; eventHandled = true;
@@ -1627,29 +1776,145 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
break; break;
} }
case WT_PACKET: case WT_PACKET:
processWintabEvent(window); processWintabEvent(window, wParam);
eventHandled = true; eventHandled = true;
break; break;
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Pointer events, processed // Pointer events, processed
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
case WM_POINTERUPDATE: case WM_POINTERUPDATE:
if (!window->m_mousePresent) {
/* Wintab asynchronously generates mouse events, if a pen briefly enters then leaves
* outside of the window the associated mouse movements may arrive late, thus we will
* wrap the cursor, leading to an unintuitive jump. */
if (window->usingTabletAPI(GHOST_kTabletWintab) && window->getCursorGrabModeIsWarp() &&
!window->m_activeWarpX && !window->m_activeWarpY) {
int32_t x, y;
system->getCursorPosition(x, y);
GHOST_Rect bounds;
window->getClientBounds(bounds);
if (!bounds.isInside(x, y)) {
break;
}
}
window->m_mousePresent = true;
GHOST_Wintab *wt = window->getWintab();
if (wt) {
wt->gainFocus();
}
/* Only adjust cursor/grab accum offset if not in the middle of a warp event. */
if (window->getCursorGrabModeIsWarp() && !window->m_activeWarpX &&
!window->m_activeWarpY) {
uint32_t pointerId = GET_POINTERID_WPARAM(wParam);
POINTER_INFO pointerInfo;
GetPointerInfo(pointerId, &pointerInfo);
int x = pointerInfo.ptPixelLocation.x;
int y = pointerInfo.ptPixelLocation.y;
system->warpGrabAccum(window, x, y);
}
}
processPointerEvent(msg, window, wParam, lParam, eventHandled);
break;
case WM_POINTERDOWN: case WM_POINTERDOWN:
case WM_POINTERUP: case WM_POINTERUP:
processPointerEvent(msg, window, wParam, lParam, eventHandled); processPointerEvent(msg, window, wParam, lParam, eventHandled);
break; break;
case WM_POINTERDEVICEINRANGE:
break;
case WM_POINTERDEVICEOUTOFRANGE: {
/* Check if pointer device left range while outside of the window. This is necessary
* because WM_POINTERENTER and WM_POINTERLEAVE events don't fire when a pen enters and
* leaves without hoving over the window, but the cursor is moved resulting in a
* WM_POINTERLEAVE event for the mouse. */
DWORD msgPos = ::GetMessagePos();
int msgPosX = GET_X_LPARAM(msgPos);
int msgPosY = GET_Y_LPARAM(msgPos);
GHOST_Rect bounds;
window->getClientBounds(bounds);
if (!bounds.isInside(msgPosX, msgPosY)) {
window->m_mousePresent = false;
}
break;
}
case WM_POINTERENTER: {
/* XXX no pointerenter for PT_MOUSE. */
uint32_t pointerId = GET_POINTERID_WPARAM(wParam);
POINTER_INPUT_TYPE type;
if (!GetPointerType(pointerId, &type)) {
break;
}
/* if mouse is not present, don't apply warp as it will be handled in WM_POINTERUPDATE */
/* XXX this is likely causing issues on leaving/entering across same process windows. */
if (window->getCursorGrabModeIsWarp() && window->m_mousePresent) {
if (type == PT_PEN && window->usingTabletAPI(GHOST_kTabletWintab)) {
break;
}
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
// XXX clienttoscreen?
system->warpGrabAccum(window, x, y);
window->m_activeWarpX = 0;
window->m_activeWarpY = 0;
}
break;
}
case WM_POINTERLEAVE: { case WM_POINTERLEAVE: {
uint32_t pointerId = GET_POINTERID_WPARAM(wParam); uint32_t pointerId = GET_POINTERID_WPARAM(wParam);
POINTER_INFO pointerInfo; POINTER_INPUT_TYPE type;
if (!GetPointerInfo(pointerId, &pointerInfo)) { if (!GetPointerType(pointerId, &type)) {
break; break;
} }
/* Reset pointer pen info if pen device has left tracking range. */ /* Reset pointer pen info if pen device has left tracking range. */
if (pointerInfo.pointerType == PT_PEN) { /* XXX Note, because touch is considered a left button down, it is never in range when it
* leaves */
if (type == PT_PEN) {
if (window->usingTabletAPI(GHOST_kTabletWintab)) {
break;
}
window->resetPointerPenInfo(); window->resetPointerPenInfo();
/* If pen is still in range, the cursor left the window via a hovering pen. */
if (IS_POINTER_INRANGE_WPARAM(wParam)) {
window->m_mousePresent = false;
}
eventHandled = true; eventHandled = true;
} }
else if (type == PT_MOUSE && window->m_mousePresent) {
/* XXX this fires when a mouse is moved after having left from a pen device entering
* while outside of the window. */
window->m_mousePresent = false;
/* Check for tablet data in case mouse movement is actually a Wintab device. */
if (window->getCursorGrabModeIsWarp()) {
event = processCursorEvent(window);
}
else {
GHOST_Wintab *wt = window->getWintab();
if (wt) {
wt->loseFocus();
}
}
eventHandled = true;
}
break; break;
} }
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
@@ -1690,20 +1955,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
} }
break; break;
case WM_MOUSEMOVE: case WM_MOUSEMOVE:
if (!window->m_mousePresent) {
TRACKMOUSEEVENT tme = {sizeof(tme)};
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
TrackMouseEvent(&tme);
window->m_mousePresent = true;
GHOST_Wintab *wt = window->getWintab();
if (wt) {
wt->gainFocus();
}
}
event = processCursorEvent(window);
break; break;
case WM_MOUSEWHEEL: { case WM_MOUSEWHEEL: {
/* The WM_MOUSEWHEEL message is sent to the focus window /* The WM_MOUSEWHEEL message is sent to the focus window
@@ -1739,17 +1990,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
} }
break; break;
case WM_MOUSELEAVE: { case WM_MOUSELEAVE: {
window->m_mousePresent = false;
if (window->getTabletData().Active == GHOST_kTabletModeNone) {
event = processCursorEvent(window);
}
GHOST_Wintab *wt = window->getWintab();
if (wt) {
wt->loseFocus();
}
break; break;
} }
////////////////////////////////////////////////////////////////////////
// Mouse events, ignored // Mouse events, ignored
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
case WM_NCMOUSEMOVE: case WM_NCMOUSEMOVE:
@@ -1794,9 +2036,15 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
window); window);
/* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
* will not be dispatched to OUR active window if we minimize one of OUR windows. */ * will not be dispatched to OUR active window if we minimize one of OUR windows. */
if (LOWORD(wParam) == WA_INACTIVE) if (LOWORD(wParam) == WA_INACTIVE) {
window->lostMouseCapture(); window->lostMouseCapture();
GHOST_Wintab *wt = window->getWintab();
if (wt) {
wt->loseFocus();
}
}
lResult = ::DefWindowProc(hwnd, msg, wParam, lParam); lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
break; break;
} }
@@ -2250,3 +2498,12 @@ int GHOST_SystemWin32::toggleConsole(int action)
return m_consoleStatus; return m_consoleStatus;
} }
void GHOST_SystemWin32::warpGrabAccum(GHOST_WindowWin32 *win, int32_t x, int32_t y)
{
int32_t x_accum, y_accum;
win->getCursorGrabAccum(x_accum, y_accum);
x_accum -= x - this->m_lastX;
y_accum -= y - this->m_lastY;
win->setCursorGrabAccum(x_accum, y_accum);
}

View File

@@ -321,8 +321,9 @@ class GHOST_SystemWin32 : public GHOST_System {
/** /**
* Creates tablet events from Wintab events. * Creates tablet events from Wintab events.
* \param window: The window receiving the event (the active window). * \param window: The window receiving the event (the active window).
* \param TODO
*/ */
static void processWintabEvent(GHOST_WindowWin32 *window); static void processWintabEvent(GHOST_WindowWin32 *window, UINT genSerial);
/** /**
* Creates tablet events from pointer events. * Creates tablet events from pointer events.
@@ -471,6 +472,13 @@ class GHOST_SystemWin32 : public GHOST_System {
/** Wheel delta accumulator. */ /** Wheel delta accumulator. */
int m_wheelDeltaAccum; int m_wheelDeltaAccum;
int32_t m_lastX;
int32_t m_lastY;
bool m_firstProximity = false;
bool m_absoluteCursor = false;
void warpGrabAccum(GHOST_WindowWin32 *win, int32_t x, int32_t y);
}; };
inline void GHOST_SystemWin32::retrieveModifierKeys(GHOST_ModifierKeys &keys) const inline void GHOST_SystemWin32::retrieveModifierKeys(GHOST_ModifierKeys &keys) const

View File

@@ -1072,8 +1072,6 @@ GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode) GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
{ {
GHOST_TSuccess err = GHOST_kSuccess;
if (mode != GHOST_kGrabDisable) { if (mode != GHOST_kGrabDisable) {
// No need to perform grab without warp as it is always on in OS X // No need to perform grab without warp as it is always on in OS X
if (mode != GHOST_kGrabNormal) { if (mode != GHOST_kGrabNormal) {
@@ -1103,7 +1101,8 @@ GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode
setCursorGrabAccum(0, 0); setCursorGrabAccum(0, 0);
m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */ m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
} }
return err;
return GHOST_kSuccess;
} }
GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape) GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)

View File

@@ -75,6 +75,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_isDialog(dialog), m_isDialog(dialog),
m_hasMouseCaptured(false), m_hasMouseCaptured(false),
m_hasGrabMouse(false), m_hasGrabMouse(false),
m_activeWarpX(0),
m_activeWarpY(0),
m_nPressedButtons(0), m_nPressedButtons(0),
m_customCursor(0), m_customCursor(0),
m_wantAlphaBackground(alphaBackground), m_wantAlphaBackground(alphaBackground),
@@ -192,6 +194,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
loadWintab(GHOST_kWindowStateMinimized != state); loadWintab(GHOST_kWindowStateMinimized != state);
} }
RegisterPointerDeviceNotifications(m_hWnd, TRUE);
/* Allow the showing of a progress bar on the taskbar. */ /* Allow the showing of a progress bar on the taskbar. */
CoCreateInstance( CoCreateInstance(
CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar); CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
@@ -625,6 +629,7 @@ void GHOST_WindowWin32::lostMouseCapture()
m_hasGrabMouse = false; m_hasGrabMouse = false;
m_nPressedButtons = 0; m_nPressedButtons = 0;
m_hasMouseCaptured = false; m_hasMouseCaptured = false;
m_mousePresent = false; // XXX necessary?
} }
} }
@@ -820,6 +825,8 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode
if (mode != GHOST_kGrabNormal) { if (mode != GHOST_kGrabNormal) {
m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]); m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
setCursorGrabAccum(0, 0); setCursorGrabAccum(0, 0);
m_activeWarpX = 0;
m_activeWarpY = 0;
if (mode == GHOST_kGrabHide) if (mode == GHOST_kGrabHide)
setWindowCursorVisibility(false); setWindowCursorVisibility(false);
@@ -843,6 +850,8 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode
/* Almost works without but important otherwise the mouse GHOST location /* Almost works without but important otherwise the mouse GHOST location
* can be incorrect on exit. */ * can be incorrect on exit. */
setCursorGrabAccum(0, 0); setCursorGrabAccum(0, 0);
m_activeWarpX = 0;
m_activeWarpY = 0;
m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */ m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
updateMouseCapture(OperatorUngrab); updateMouseCapture(OperatorUngrab);
} }
@@ -872,6 +881,11 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
uint32_t outCount = 0; uint32_t outCount = 0;
POINTER_INPUT_TYPE inputType;
if (GetPointerType(pointerId, &inputType) && inputType == PT_MOUSE) {
return GHOST_kFailure;
}
if (!(GetPointerPenInfoHistory(pointerId, &outCount, NULL))) { if (!(GetPointerPenInfoHistory(pointerId, &outCount, NULL))) {
return GHOST_kFailure; return GHOST_kFailure;
} }
@@ -950,6 +964,11 @@ void GHOST_WindowWin32::resetPointerPenInfo()
m_lastPointerTabletData = GHOST_TABLET_DATA_NONE; m_lastPointerTabletData = GHOST_TABLET_DATA_NONE;
} }
void GHOST_WindowWin32::setPointerPenInfo()
{
m_lastPointerTabletData.Active = GHOST_kTabletModeStylus;
}
GHOST_Wintab *GHOST_WindowWin32::getWintab() const GHOST_Wintab *GHOST_WindowWin32::getWintab() const
{ {
return m_wintab; return m_wintab;

View File

@@ -269,6 +269,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
*/ */
void resetPointerPenInfo(); void resetPointerPenInfo();
void setPointerPenInfo();
/** /**
* Retrieves pointer to Wintab if Wintab is the set Tablet API. * Retrieves pointer to Wintab if Wintab is the set Tablet API.
* \return Pointer to Wintab member. * \return Pointer to Wintab member.
@@ -307,6 +309,10 @@ class GHOST_WindowWin32 : public GHOST_Window {
/** True if the mouse is either over or captured by the window. */ /** True if the mouse is either over or captured by the window. */
bool m_mousePresent; bool m_mousePresent;
/** Active cursor warp in x axis. */
int32_t m_activeWarpX;
/** Active cursor warp in y axis. */
int32_t m_activeWarpY;
/** True if the window currently resizing. */ /** True if the window currently resizing. */
bool m_inLiveResize; bool m_inLiveResize;

View File

@@ -63,6 +63,17 @@ GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
return nullptr; return nullptr;
} }
auto dataGet = (GHOST_WIN32_WTDataGet)::GetProcAddress(handle.get(), "WTDataGet");
if (!dataGet) {
return nullptr;
}
auto queuePacketsEx = (GHOST_WIN32_WTQueuePacketsEx)::GetProcAddress(handle.get(),
"WTQueuePacketsEx");
if (!queuePacketsEx) {
return nullptr;
}
auto queueSizeGet = (GHOST_WIN32_WTQueueSizeGet)::GetProcAddress(handle.get(), "WTQueueSizeGet"); auto queueSizeGet = (GHOST_WIN32_WTQueueSizeGet)::GetProcAddress(handle.get(), "WTQueueSizeGet");
if (!queueSizeGet) { if (!queueSizeGet) {
return nullptr; return nullptr;
@@ -136,6 +147,8 @@ GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
get, get,
set, set,
packetsGet, packetsGet,
dataGet,
queuePacketsEx,
enable, enable,
overlap, overlap,
std::move(hctx), std::move(hctx),
@@ -180,6 +193,8 @@ GHOST_Wintab::GHOST_Wintab(HWND hwnd,
GHOST_WIN32_WTGet get, GHOST_WIN32_WTGet get,
GHOST_WIN32_WTSet set, GHOST_WIN32_WTSet set,
GHOST_WIN32_WTPacketsGet packetsGet, GHOST_WIN32_WTPacketsGet packetsGet,
GHOST_WIN32_WTDataGet dataGet,
GHOST_WIN32_WTQueuePacketsEx queuePacketsEx,
GHOST_WIN32_WTEnable enable, GHOST_WIN32_WTEnable enable,
GHOST_WIN32_WTOverlap overlap, GHOST_WIN32_WTOverlap overlap,
unique_hctx hctx, unique_hctx hctx,
@@ -191,6 +206,8 @@ GHOST_Wintab::GHOST_Wintab(HWND hwnd,
m_fpGet{get}, m_fpGet{get},
m_fpSet{set}, m_fpSet{set},
m_fpPacketsGet{packetsGet}, m_fpPacketsGet{packetsGet},
m_fpDataGet{dataGet},
m_fpQueuePacketsEx{queuePacketsEx},
m_fpEnable{enable}, m_fpEnable{enable},
m_fpOverlap{overlap}, m_fpOverlap{overlap},
m_context{std::move(hctx)}, m_context{std::move(hctx)},
@@ -244,7 +261,16 @@ void GHOST_Wintab::leaveRange()
/* Set to none to indicate tablet is inactive. */ /* Set to none to indicate tablet is inactive. */
m_lastTabletData = GHOST_TABLET_DATA_NONE; m_lastTabletData = GHOST_TABLET_DATA_NONE;
/* Clear the packet queue. */ /* Clear the packet queue. */
m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data()); // XXX proximity may return before proximity leave is processed, this would thus clear valid
// events.
// m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data());
}
void GHOST_Wintab::enterRange()
{
/* Dummy until input arrives. */
m_lastTabletData.Active = GHOST_kTabletModeStylus;
m_firstProximity = true;
} }
void GHOST_Wintab::remapCoordinates() void GHOST_Wintab::remapCoordinates()
@@ -295,10 +321,29 @@ GHOST_TabletData GHOST_Wintab::getLastTabletData()
return m_lastTabletData; return m_lastTabletData;
} }
void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo) void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo, UINT genSerial)
{ {
const int numPackets = m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data()); // const int numPackets = m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data());
// outWintabInfo.resize(numPackets);
UINT beginSerial, unusedEndSerial;
if (!m_fpQueuePacketsEx(m_context.get(), &beginSerial, &unusedEndSerial)) {
return;
}
int numPackets;
m_fpDataGet(m_context.get(), beginSerial, genSerial, m_pkts.size(), m_pkts.data(), &numPackets);
/* If this is the first event since a proximity event, we want to skip all events prior to the
* one which triggered the WT_PACKET event so that we don't use events from the last time the
* stylus was in range. */
if (m_firstProximity) {
m_firstProximity = false;
m_pkts[0] = m_pkts[numPackets - 1];
numPackets = 1;
}
outWintabInfo.resize(numPackets); outWintabInfo.resize(numPackets);
size_t outExtent = 0; size_t outExtent = 0;
for (int i = 0; i < numPackets; i++) { for (int i = 0; i < numPackets; i++) {

View File

@@ -46,6 +46,8 @@ typedef BOOL(API *GHOST_WIN32_WTSet)(HCTX, LPLOGCONTEXTA);
typedef HCTX(API *GHOST_WIN32_WTOpen)(HWND, LPLOGCONTEXTA, BOOL); typedef HCTX(API *GHOST_WIN32_WTOpen)(HWND, LPLOGCONTEXTA, BOOL);
typedef BOOL(API *GHOST_WIN32_WTClose)(HCTX); typedef BOOL(API *GHOST_WIN32_WTClose)(HCTX);
typedef int(API *GHOST_WIN32_WTPacketsGet)(HCTX, int, LPVOID); typedef int(API *GHOST_WIN32_WTPacketsGet)(HCTX, int, LPVOID);
typedef BOOL(API *GHOST_WIN32_WTQueuePacketsEx)(HCTX, UINT FAR *, UINT FAR *);
typedef int(API *GHOST_WIN32_WTDataGet)(HCTX, UINT, UINT, int, LPVOID, LPINT);
typedef int(API *GHOST_WIN32_WTQueueSizeGet)(HCTX); typedef int(API *GHOST_WIN32_WTQueueSizeGet)(HCTX);
typedef BOOL(API *GHOST_WIN32_WTQueueSizeSet)(HCTX, int); typedef BOOL(API *GHOST_WIN32_WTQueueSizeSet)(HCTX, int);
typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL); typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL);
@@ -91,6 +93,8 @@ class GHOST_Wintab {
*/ */
void loseFocus(); void loseFocus();
void enterRange();
/** /**
* Clean up when Wintab leaves tracking range. * Clean up when Wintab leaves tracking range.
*/ */
@@ -130,8 +134,9 @@ class GHOST_Wintab {
/** /**
* Translate Wintab packets into GHOST_WintabInfoWin32 structs. * Translate Wintab packets into GHOST_WintabInfoWin32 structs.
* \param outWintabInfo: Storage to return resulting GHOST_WintabInfoWin32 data. * \param outWintabInfo: Storage to return resulting GHOST_WintabInfoWin32 data.
* \param TODO
*/ */
void getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo); void getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo, UINT genSerial);
/** /**
* Whether Wintab coordinates should be trusted. * Whether Wintab coordinates should be trusted.
@@ -167,6 +172,8 @@ class GHOST_Wintab {
GHOST_WIN32_WTGet m_fpGet = nullptr; GHOST_WIN32_WTGet m_fpGet = nullptr;
GHOST_WIN32_WTSet m_fpSet = nullptr; GHOST_WIN32_WTSet m_fpSet = nullptr;
GHOST_WIN32_WTPacketsGet m_fpPacketsGet = nullptr; GHOST_WIN32_WTPacketsGet m_fpPacketsGet = nullptr;
GHOST_WIN32_WTDataGet m_fpDataGet = nullptr;
GHOST_WIN32_WTQueuePacketsEx m_fpQueuePacketsEx = nullptr;
GHOST_WIN32_WTEnable m_fpEnable = nullptr; GHOST_WIN32_WTEnable m_fpEnable = nullptr;
GHOST_WIN32_WTOverlap m_fpOverlap = nullptr; GHOST_WIN32_WTOverlap m_fpOverlap = nullptr;
@@ -176,6 +183,7 @@ class GHOST_Wintab {
bool m_enabled = false; bool m_enabled = false;
/** Whether the context has focus and is at the top of overlap order. */ /** Whether the context has focus and is at the top of overlap order. */
bool m_focused = false; bool m_focused = false;
bool m_firstProximity = false;
/** Pressed button map. */ /** Pressed button map. */
uint8_t m_buttons = 0; uint8_t m_buttons = 0;
@@ -219,6 +227,8 @@ class GHOST_Wintab {
GHOST_WIN32_WTGet get, GHOST_WIN32_WTGet get,
GHOST_WIN32_WTSet set, GHOST_WIN32_WTSet set,
GHOST_WIN32_WTPacketsGet packetsGet, GHOST_WIN32_WTPacketsGet packetsGet,
GHOST_WIN32_WTDataGet dataGet,
GHOST_WIN32_WTQueuePacketsEx queuePacketsEx,
GHOST_WIN32_WTEnable enable, GHOST_WIN32_WTEnable enable,
GHOST_WIN32_WTOverlap overlap, GHOST_WIN32_WTOverlap overlap,
unique_hctx hctx, unique_hctx hctx,

View File

@@ -752,10 +752,14 @@ static void flyMoveCamera(bContext *C,
static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm) static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm)
{ {
#define FLY_ROTATE_FAC 10.0f /* more is faster */ /* Higher is faster. */
#define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */ static const float fly_rotate_factor = 10.0f;
#define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */ /* Amount to correct per step. */
#define FLY_SMOOTH_FAC 20.0f /* higher value less lag */ static const float fly_z_up_correct_factor = 0.1f;
/* Increase upright momentum each step. */
static const float fly_z_up_correct_accel = 0.05f;
/* Higher is less lag. */
static const float fly_smooth_factor = 20.0f;
RegionView3D *rv3d = fly->rv3d; RegionView3D *rv3d = fly->rv3d;
@@ -875,7 +879,7 @@ static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm)
copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f); copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
mul_m3_v3(mat, upvec); mul_m3_v3(mat, upvec);
/* Rotate about the relative up vec */ /* Rotate about the relative up vec */
axis_angle_to_quat(tmp_quat, upvec, moffset[1] * time_redraw * -FLY_ROTATE_FAC); axis_angle_to_quat(tmp_quat, upvec, moffset[1] * time_redraw * -fly_rotate_factor);
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
if (fly->xlock != FLY_AXISLOCK_STATE_OFF) { if (fly->xlock != FLY_AXISLOCK_STATE_OFF) {
@@ -908,7 +912,7 @@ static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm)
} }
/* Rotate about the relative up vec */ /* Rotate about the relative up vec */
axis_angle_to_quat(tmp_quat, upvec, moffset[0] * time_redraw * FLY_ROTATE_FAC); axis_angle_to_quat(tmp_quat, upvec, moffset[0] * time_redraw * fly_rotate_factor);
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
if (fly->xlock != FLY_AXISLOCK_STATE_OFF) { if (fly->xlock != FLY_AXISLOCK_STATE_OFF) {
@@ -934,10 +938,10 @@ static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm)
axis_angle_to_quat(tmp_quat, axis_angle_to_quat(tmp_quat,
upvec, upvec,
roll * time_redraw_clamped * fly->zlock_momentum * roll * time_redraw_clamped * fly->zlock_momentum *
FLY_ZUP_CORRECT_FAC); fly_z_up_correct_factor);
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL; fly->zlock_momentum += fly_z_up_correct_accel;
} }
else { else {
/* don't check until the view rotates again */ /* don't check until the view rotates again */
@@ -996,7 +1000,7 @@ static int flyApply(bContext *C, FlyInfo *fly, bool is_confirm)
/* impose a directional lag */ /* impose a directional lag */
interp_v3_v3v3( interp_v3_v3v3(
dvec, dvec_tmp, fly->dvec_prev, (1.0f / (1.0f + (time_redraw * FLY_SMOOTH_FAC)))); dvec, dvec_tmp, fly->dvec_prev, (1.0f / (1.0f + (time_redraw * fly_smooth_factor))));
add_v3_v3(rv3d->ofs, dvec); add_v3_v3(rv3d->ofs, dvec);

View File

@@ -284,9 +284,6 @@ typedef struct WalkInfo {
bool is_reversed; bool is_reversed;
#ifdef USE_TABLET_SUPPORT #ifdef USE_TABLET_SUPPORT
/** Check if we had a cursor event before. */
bool is_cursor_first;
/** Tablet devices (we can't relocate the cursor). */ /** Tablet devices (we can't relocate the cursor). */
bool is_cursor_absolute; bool is_cursor_absolute;
#endif #endif
@@ -495,7 +492,7 @@ enum {
static float base_speed = -1.0f; static float base_speed = -1.0f;
static float userdef_speed = -1.0f; static float userdef_speed = -1.0f;
static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op, wmEvent *event)
{ {
wmWindowManager *wm = CTX_wm_manager(C); wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C); wmWindow *win = CTX_wm_window(C);
@@ -537,6 +534,8 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op)
userdef_speed = U.walk_navigation.walk_speed; userdef_speed = U.walk_navigation.walk_speed;
} }
walk->moffset[0] = 0;
walk->moffset[1] = 0;
walk->speed = 0.0f; walk->speed = 0.0f;
walk->is_fast = false; walk->is_fast = false;
walk->is_slow = false; walk->is_slow = false;
@@ -573,8 +572,6 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op)
walk->is_reversed = ((U.walk_navigation.flag & USER_WALK_MOUSE_REVERSE) != 0); walk->is_reversed = ((U.walk_navigation.flag & USER_WALK_MOUSE_REVERSE) != 0);
#ifdef USE_TABLET_SUPPORT #ifdef USE_TABLET_SUPPORT
walk->is_cursor_first = true;
walk->is_cursor_absolute = false; walk->is_cursor_absolute = false;
#endif #endif
@@ -622,11 +619,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op)
walk->center_mval[1] -= walk->region->winrct.ymin; walk->center_mval[1] -= walk->region->winrct.ymin;
#endif #endif
copy_v2_v2_int(walk->prev_mval, walk->center_mval); copy_v2_v2_int(walk->prev_mval, event->mval);
WM_cursor_warp(win,
walk->region->winrct.xmin + walk->center_mval[0],
walk->region->winrct.ymin + walk->center_mval[1]);
/* remove the mouse cursor temporarily */ /* remove the mouse cursor temporarily */
WM_cursor_modal_set(win, WM_CURSOR_NONE); WM_cursor_modal_set(win, WM_CURSOR_NONE);
@@ -706,59 +699,13 @@ static void walkEvent(bContext *C, WalkInfo *walk, const wmEvent *event)
walk->redraw = true; walk->redraw = true;
} }
else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
#ifdef USE_TABLET_SUPPORT
if (walk->is_cursor_first) {
/* wait until we get the 'warp' event */
if ((walk->center_mval[0] == event->mval[0]) && (walk->center_mval[1] == event->mval[1])) {
walk->is_cursor_first = false;
}
else {
/* NOTE: its possible the system isn't giving us the warp event
* ideally we shouldn't have to worry about this, see: T45361 */
wmWindow *win = CTX_wm_window(C);
WM_cursor_warp(win,
walk->region->winrct.xmin + walk->center_mval[0],
walk->region->winrct.ymin + walk->center_mval[1]);
}
return;
}
if ((walk->is_cursor_absolute == false) && event->tablet.is_motion_absolute) {
walk->is_cursor_absolute = true;
copy_v2_v2_int(walk->prev_mval, event->mval);
copy_v2_v2_int(walk->center_mval, event->mval);
}
#endif /* USE_TABLET_SUPPORT */
walk->moffset[0] += event->mval[0] - walk->prev_mval[0]; walk->moffset[0] += event->mval[0] - walk->prev_mval[0];
walk->moffset[1] += event->mval[1] - walk->prev_mval[1]; walk->moffset[1] += event->mval[1] - walk->prev_mval[1];
copy_v2_v2_int(walk->prev_mval, event->mval); copy_v2_v2_int(walk->prev_mval, event->mval);
if ((walk->center_mval[0] != event->mval[0]) || (walk->center_mval[1] != event->mval[1])) { if (walk->moffset[0] != 0 || walk->moffset[1] != 0) {
walk->redraw = true; walk->redraw = true;
#ifdef USE_TABLET_SUPPORT
if (walk->is_cursor_absolute) {
/* pass */
}
else
#endif
if (WM_event_is_last_mousemove(event)) {
wmWindow *win = CTX_wm_window(C);
#ifdef __APPLE__
if ((abs(walk->prev_mval[0] - walk->center_mval[0]) > walk->center_mval[0] / 2) ||
(abs(walk->prev_mval[1] - walk->center_mval[1]) > walk->center_mval[1] / 2))
#endif
{
WM_cursor_warp(win,
walk->region->winrct.xmin + walk->center_mval[0],
walk->region->winrct.ymin + walk->center_mval[1]);
copy_v2_v2_int(walk->prev_mval, walk->center_mval);
}
}
} }
} }
#ifdef WITH_INPUT_NDOF #ifdef WITH_INPUT_NDOF
@@ -1007,14 +954,16 @@ static float getVelocityZeroTime(const float gravity, const float velocity)
static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm) static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
{ {
#define WALK_ROTATE_TABLET_FAC 8.8f /* Higher is faster, relative to region size. */ /* Higher is faster, relative to region size. */
#define WALK_ROTATE_CONSTANT_FAC DEG2RAD(0.15f) /* Higher is faster, radians per-pixel. */ static const float walk_rotate_tablet_factor = 8.8f;
#define WALK_TOP_LIMIT DEG2RADF(85.0f) /* Higher is faster, radians per-pixel. */
#define WALK_BOTTOM_LIMIT DEG2RADF(-80.0f) static const float walk_rotate_constant_factor = DEG2RAD(0.15f);
#define WALK_MOVE_SPEED base_speed static const float walk_top_limit = DEG2RADF(85.0f);
#define WALK_BOOST_FACTOR ((void)0, walk->speed_factor) static const float walk_bottom_limit = DEG2RADF(-80.0f);
#define WALK_ZUP_CORRECT_FAC 0.1f /* Amount to correct per step. */ /* Amount to correct per step. */
#define WALK_ZUP_CORRECT_ACCEL 0.05f /* Increase upright momentum each step. */ static const float walk_zup_correct_fac = 0.1f;
/* Increase upright momentum each step. */
static const float walk_zup_correct_accel = 0.05f;
RegionView3D *rv3d = walk->rv3d; RegionView3D *rv3d = walk->rv3d;
ARegion *region = walk->region; ARegion *region = walk->region;
@@ -1071,13 +1020,13 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
walk->time_lastdraw = time_current; walk->time_lastdraw = time_current;
/* base speed in m/s */ /* base speed in m/s */
walk->speed = WALK_MOVE_SPEED; walk->speed = base_speed;
if (walk->is_fast) { if (walk->is_fast) {
walk->speed *= WALK_BOOST_FACTOR; walk->speed *= walk->speed_factor;
} }
else if (walk->is_slow) { else if (walk->is_slow) {
walk->speed *= 1.0f / WALK_BOOST_FACTOR; walk->speed *= 1.0f / walk->speed_factor;
} }
copy_m3_m4(mat, rv3d->viewinv); copy_m3_m4(mat, rv3d->viewinv);
@@ -1093,16 +1042,7 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
y = (float)moffset[1]; y = (float)moffset[1];
/* Speed factor. */ /* Speed factor. */
#ifdef USE_TABLET_SUPPORT y *= walk_rotate_constant_factor;
if (walk->is_cursor_absolute) {
y /= region->winy;
y *= WALK_ROTATE_TABLET_FAC;
}
else
#endif
{
y *= WALK_ROTATE_CONSTANT_FAC;
}
/* user adjustment factor */ /* user adjustment factor */
y *= walk->mouse_speed; y *= walk->mouse_speed;
@@ -1111,10 +1051,10 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
/* it ranges from 90.0f to -90.0f */ /* it ranges from 90.0f to -90.0f */
angle = -asinf(rv3d->viewmat[2][2]); angle = -asinf(rv3d->viewmat[2][2]);
if (angle > WALK_TOP_LIMIT && y > 0.0f) { if (angle > walk_top_limit && y > 0.0f) {
y = 0.0f; y = 0.0f;
} }
else if (angle < WALK_BOTTOM_LIMIT && y < 0.0f) { else if (angle < walk_bottom_limit && y < 0.0f) {
y = 0.0f; y = 0.0f;
} }
@@ -1142,16 +1082,7 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
x = (float)moffset[0]; x = (float)moffset[0];
/* Speed factor. */ /* Speed factor. */
#ifdef USE_TABLET_SUPPORT x *= walk_rotate_constant_factor;
if (walk->is_cursor_absolute) {
x /= region->winx;
x *= WALK_ROTATE_TABLET_FAC;
}
else
#endif
{
x *= WALK_ROTATE_CONSTANT_FAC;
}
/* user adjustment factor */ /* user adjustment factor */
x *= walk->mouse_speed; x *= walk->mouse_speed;
@@ -1176,10 +1107,10 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
axis_angle_to_quat(tmp_quat, axis_angle_to_quat(tmp_quat,
upvec, upvec,
roll * time_redraw_clamped * walk->zlock_momentum * roll * time_redraw_clamped * walk->zlock_momentum *
WALK_ZUP_CORRECT_FAC); walk_zup_correct_fac);
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
walk->zlock_momentum += WALK_ZUP_CORRECT_ACCEL; walk->zlock_momentum += walk_zup_correct_accel;
} }
else { else {
/* Lock fixed, don't need to check it ever again. */ /* Lock fixed, don't need to check it ever again. */
@@ -1279,7 +1210,7 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
/* the distance we would fall naturally smoothly enough that we /* the distance we would fall naturally smoothly enough that we
* can manually drop the object without activating gravity */ * can manually drop the object without activating gravity */
fall_distance = time_redraw * walk->speed * WALK_BOOST_FACTOR; fall_distance = time_redraw * walk->speed * walk->speed_factor;
if (fabsf(difference) < fall_distance) { if (fabsf(difference) < fall_distance) {
/* slope/stairs */ /* slope/stairs */
@@ -1392,11 +1323,6 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
} }
return OPERATOR_FINISHED; return OPERATOR_FINISHED;
#undef WALK_ROTATE_TABLET_FAC
#undef WALK_TOP_LIMIT
#undef WALK_BOTTOM_LIMIT
#undef WALK_MOVE_SPEED
#undef WALK_BOOST_FACTOR
} }
#ifdef WITH_INPUT_NDOF #ifdef WITH_INPUT_NDOF
@@ -1445,7 +1371,7 @@ static int walk_invoke(bContext *C, wmOperator *op, const wmEvent *event)
op->customdata = walk; op->customdata = walk;
if (initWalkInfo(C, walk, op) == false) { if (initWalkInfo(C, walk, op, event) == false) {
MEM_freeN(op->customdata); MEM_freeN(op->customdata);
return OPERATOR_CANCELLED; return OPERATOR_CANCELLED;
} }
@@ -1524,7 +1450,7 @@ void VIEW3D_OT_walk(wmOperatorType *ot)
ot->poll = ED_operator_region_view3d_active; ot->poll = ED_operator_region_view3d_active;
/* flags */ /* flags */
ot->flag = OPTYPE_BLOCKING; ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY | OPTYPE_GRAB_CURSOR_FORCE;
} }
/** \} */ /** \} */

View File

@@ -184,6 +184,9 @@ enum {
OPTYPE_LOCK_BYPASS = (1 << 9), OPTYPE_LOCK_BYPASS = (1 << 9),
/** Special type of undo which doesn't store itself multiple times. */ /** Special type of undo which doesn't store itself multiple times. */
OPTYPE_UNDO_GROUPED = (1 << 10), OPTYPE_UNDO_GROUPED = (1 << 10),
/** Force cursor warp. */
OPTYPE_GRAB_CURSOR_FORCE = (1 << 11),
}; };
/** For #WM_cursor_grab_enable wrap axis. */ /** For #WM_cursor_grab_enable wrap axis. */

View File

@@ -271,9 +271,7 @@ void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4])
if ((G.debug & G_DEBUG) == 0) { if ((G.debug & G_DEBUG) == 0) {
if (win->ghostwin) { if (win->ghostwin) {
if (win->eventstate->tablet.is_motion_absolute == false) { GHOST_SetCursorGrab(win->ghostwin, mode, mode_axis, bounds, NULL);
GHOST_SetCursorGrab(win->ghostwin, mode, mode_axis, bounds, NULL);
}
win->grabcursor = mode; win->grabcursor = mode;
} }

View File

@@ -1397,7 +1397,7 @@ static int wm_operator_invoke(bContext *C,
int bounds[4] = {-1, -1, -1, -1}; int bounds[4] = {-1, -1, -1, -1};
int wrap = WM_CURSOR_WRAP_NONE; int wrap = WM_CURSOR_WRAP_NONE;
if (event && (U.uiflag & USER_CONTINUOUS_MOUSE)) { if (event && (U.uiflag & USER_CONTINUOUS_MOUSE || op->flag & OPTYPE_GRAB_CURSOR_FORCE)) {
const wmOperator *op_test = op->opm ? op->opm : op; const wmOperator *op_test = op->opm ? op->opm : op;
const wmOperatorType *ot_test = op_test->type; const wmOperatorType *ot_test = op_test->type;
if ((ot_test->flag & OPTYPE_GRAB_CURSOR_XY) || if ((ot_test->flag & OPTYPE_GRAB_CURSOR_XY) ||