From 5553d614211998b5a10529f6b3ec68d2b25dc07a Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Fri, 22 Oct 2021 17:01:33 +0000
Subject: SL-16203 Fix for wonky handling of mouse deltas.

---
 indra/llcommon/llsingleton.h       |   1 +
 indra/llwindow/llwindow.h          |   3 +
 indra/llwindow/llwindowheadless.h  |   3 +
 indra/llwindow/llwindowwin32.cpp   | 530 ++++++++++++++-----------------------
 indra/llwindow/llwindowwin32.h     |  13 +-
 indra/newview/lldrawpoolavatar.cpp |  10 +-
 indra/newview/llviewerwindow.cpp   |   7 +
 7 files changed, 230 insertions(+), 337 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 7c81d65a8b..2e43a3cbed 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -455,6 +455,7 @@ public:
 
     static DERIVED_TYPE* getInstance()
     {
+        LL_PROFILE_ZONE_SCOPED;
         // We know the viewer has LLSingleton dependency circularities. If you
         // feel strongly motivated to eliminate them, cheers and good luck.
         // (At that point we could consider a much simpler locking mechanism.)
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index 0100c3bf0a..1384ddfd82 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -91,6 +91,9 @@ public:
 
     virtual BOOL setCursorPosition(LLCoordWindow position) = 0;
 	virtual BOOL getCursorPosition(LLCoordWindow *position) = 0;
+#if LL_WINDOWS
+    virtual BOOL getCursorDelta(LLCoordCommon* delta) = 0;
+#endif
 	virtual void showCursor() = 0;
 	virtual void hideCursor() = 0;
 	virtual BOOL isCursorHidden() = 0;
diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h
index a7ae28aa24..f8ba9bbed4 100644
--- a/indra/llwindow/llwindowheadless.h
+++ b/indra/llwindow/llwindowheadless.h
@@ -54,6 +54,9 @@ public:
     void destroySharedContext(void*)  {}
 	/*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;};
 	/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;};
+#if LL_WINDOWS
+    /*virtual*/ BOOL getCursorDelta(LLCoordCommon* delta) { return FALSE; }
+#endif
 	/*virtual*/ void showCursor() {};
 	/*virtual*/ void hideCursor() {};
 	/*virtual*/ void showCursorFromMouseMove() {};
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 12d4c6c30e..bf78bcba29 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -28,8 +28,6 @@
 
 #if LL_WINDOWS && !LL_MESA_HEADLESS
 
-#define LL_WINDOW_SINGLE_THREADED 0
-
 #include "llwindowwin32.h"
 
 // LLWindow library includes
@@ -85,7 +83,7 @@ extern BOOL gDebugWindowProc;
 static std::thread::id sWindowThreadId;
 static std::thread::id sMainThreadId;
 
-#if 1 || LL_WINDOW_SINGLE_THREADED
+#if 1 // flip to zero to enable assertions for functions being called from wrong thread
 #define ASSERT_MAIN_THREAD()
 #define ASSERT_WINDOW_THREAD()
 #else
@@ -482,9 +480,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
 {
     sMainThreadId = LLThread::currentID();
     mWindowThread = new LLWindowWin32Thread(this);
-#if !LL_WINDOW_SINGLE_THREADED
     mWindowThread->start();
-#endif
 	//MAINT-516 -- force a load of opengl32.dll just in case windows went sideways 
 	LoadLibrary(L"opengl32.dll");
 
@@ -492,7 +488,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
 	mIconResource = gIconResource;
 	mOverrideAspectRatio = 0.f;
 	mNativeAspectRatio = 0.f;
-	mMousePositionModified = FALSE;
 	mInputProcessingPaused = FALSE;
 	mPreeditor = NULL;
 	mKeyCharCode = 0;
@@ -814,6 +809,13 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
 	initCursors();
 	setCursor( UI_CURSOR_ARROW );
 
+    mRawMouse.usUsagePage = 0x01;          // HID_USAGE_PAGE_GENERIC
+    mRawMouse.usUsage = 0x02;              // HID_USAGE_GENERIC_MOUSE
+    mRawMouse.dwFlags = 0;    // adds mouse and also ignores legacy mouse messages
+    mRawMouse.hwndTarget = 0;
+
+    RegisterRawInputDevices(&mRawMouse, 1, sizeof(mRawMouse));
+
 	// Initialize (boot strap) the Language text input management,
 	// based on the system's (or user's) default settings.
 	allowLanguageTextInput(NULL, FALSE);
@@ -1927,31 +1929,26 @@ BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position)
 {
     ASSERT_MAIN_THREAD();
 
-	if (!mWindowHandle)
-	{
-		return FALSE;
-	}
+    if (!mWindowHandle)
+    {
+        return FALSE;
+    }
 
-    // Inform the application of the new mouse position (needed for per-frame
-	// hover/picking to function).
-	mCallbacks->handleMouseMove(this, position.convert(), (MASK)0);
-	
-    mMousePositionModified = TRUE;
     LLCoordScreen screen_pos(position.convert());
-    
-    mWindowThread->post([=]
+
+    // instantly set the cursor position from the app's point of view
+    mCursorPosition = position;
+    mLastCursorPosition = position;
+
+    // Inform the application of the new mouse position (needed for per-frame
+    // hover/picking to function).
+    mCallbacks->handleMouseMove(this, position.convert(), (MASK)0);
+
+    // actually set the cursor position on the window thread
+    mWindowThread->post([=]()
         {
+            // actually set the OS cursor position
             SetCursorPos(screen_pos.mX, screen_pos.mY);
-            // DEV-18951 VWR-8524 Camera moves wildly when alt-clicking.
-            // Because we have preemptively notified the application of the new
-            // mouse position via handleMouseMove() above, we need to clear out
-            // any stale mouse move events.  RN/JC
-            MSG msg;
-            while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
-            {
-            }
-            
-            mMousePositionModified = FALSE;
         });
 
     return TRUE;
@@ -1960,19 +1957,27 @@ BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position)
 BOOL LLWindowWin32::getCursorPosition(LLCoordWindow *position)
 {
     ASSERT_MAIN_THREAD();
-	POINT cursor_point;
-
-	if (!mWindowHandle 
-		|| !GetCursorPos(&cursor_point)
-		|| !position)
-	{
-		return FALSE;
-	}
+    if (!position)
+    {
+        return FALSE;
+    }
 
-	*position = LLCoordScreen(cursor_point.x, cursor_point.y).convert();
+    *position = mCursorPosition;
 	return TRUE;
 }
 
+BOOL LLWindowWin32::getCursorDelta(LLCoordCommon* delta)
+{
+    if (delta == nullptr)
+    {
+        return FALSE;
+    }
+
+    *delta = mMouseFrameDelta;
+
+    return TRUE;
+}
+
 void LLWindowWin32::hideCursor()
 {
     ASSERT_MAIN_THREAD();
@@ -2153,34 +2158,31 @@ void LLWindowWin32::gatherInput()
     LL_PROFILE_ZONE_SCOPED
     MSG msg;
 
-#if LL_WINDOW_SINGLE_THREADED
-    int	msg_count = 0;
-
-    while ((msg_count < MAX_MESSAGE_PER_UPDATE))
     {
-        LL_PROFILE_ZONE_NAMED("gi - loop");
-        ++msg_count;
-        {
-            LL_PROFILE_ZONE_NAMED("gi - PeekMessage");
-            if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
-            {
-                break;
-            }
-        }
+        LLMutexLock lock(&mRawMouseMutex);
+        mMouseFrameDelta = mRawMouseDelta;
 
-        {
-            LL_PROFILE_ZONE_NAMED("gi - translate");
-            TranslateMessage(&msg);
-        }
+        mRawMouseDelta.mX = 0;
+        mRawMouseDelta.mY = 0;
+    }
 
-        {
-            LL_PROFILE_ZONE_NAMED("gi - dispatch");
-            DispatchMessage(&msg);
-        }
 
+    if (mWindowThread->mFunctionQueue.size() > 0)
+    {
+        LL_PROFILE_ZONE_NAMED("gi - PostMessage");
+        if (mWindowHandle)
+        { // post a nonsense user message to wake up the Window Thread in case any functions are pending
+            // and no windows events came through this frame
+            PostMessage(mWindowHandle, WM_USER + 0x0017, 0xB0B0, 0x1337);
+        }
+    }
+        
+    while (mWindowThread->mMessageQueue.tryPopBack(msg))
+    {
+        LL_PROFILE_ZONE_NAMED("gi - message queue");
         if (mInputProcessingPaused)
         {
-            break;
+            continue;
         }
 
         // For async host by name support.  Really hacky.
@@ -2190,45 +2192,35 @@ void LLWindowWin32::gatherInput()
             gAsyncMsgCallback(msg);
         }
     }
-#else //multi-threaded window impl
+
     {
-        if (mWindowThread->mFunctionQueue.size() > 0)
+        LL_PROFILE_ZONE_NAMED("gi - function queue");
+        //process any pending functions
+        std::function<void()> curFunc;
+        while (mFunctionQueue.tryPopBack(curFunc))
         {
-            LL_PROFILE_ZONE_NAMED("gi - PostMessage");
-            if (mWindowHandle)
-            { // post a nonsense user message to wake up the Window Thread in case any functions are pending
-                // and no windows events came through this frame
-                PostMessage(mWindowHandle, WM_USER + 0x0017, 0xB0B0, 0x1337);
-            }
+            curFunc();
         }
-        
-        while (mWindowThread->mMessageQueue.tryPopBack(msg))
-        {
-            LL_PROFILE_ZONE_NAMED("gi - message queue");
-            if (mInputProcessingPaused)
-            {
-                continue;
-            }
+    }
 
-            // For async host by name support.  Really hacky.
-            if (gAsyncMsgCallback && (LL_WM_HOST_RESOLVED == msg.message))
-            {
-                LL_PROFILE_ZONE_NAMED("gi - callback");
-                gAsyncMsgCallback(msg);
-            }
-        }
+    // send one and only one mouse move event per frame BEFORE handling mouse button presses
+    if (mLastCursorPosition != mCursorPosition)
+    {
+        LL_PROFILE_ZONE_NAMED("gi - mouse move");
+        mCallbacks->handleMouseMove(this, mCursorPosition.convert(), mMouseMask);
     }
+    
+    mLastCursorPosition = mCursorPosition;
 
     {
-        LL_PROFILE_ZONE_NAMED("gi - function queue");
-        //process any pending functions
+        LL_PROFILE_ZONE_NAMED("gi - mouse queue");
+        // handle mouse button presses AFTER updating mouse cursor position
         std::function<void()> curFunc;
-        while (mFunctionQueue.tryPopBack(curFunc))
+        while (mMouseQueue.tryPopBack(curFunc))
         {
             curFunc();
         }
     }
-#endif
 
 	mInputProcessingPaused = FALSE;
 
@@ -2238,11 +2230,7 @@ void LLWindowWin32::gatherInput()
 static LLTrace::BlockTimerStatHandle FTM_KEYHANDLER("Handle Keyboard");
 static LLTrace::BlockTimerStatHandle FTM_MOUSEHANDLER("Handle Mouse");
 
-#if LL_WINDOW_SINGLE_THREADED
-#define WINDOW_IMP_POST(x) x
-#else
 #define WINDOW_IMP_POST(x) window_imp->post([=]() { x; })
-#endif
 
 LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param)
 {
@@ -2278,10 +2266,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
         // mouse is outside window.
         LLCoordWindow window_coord((S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param));
 
-        // This doesn't work, as LOWORD returns unsigned short.
-        //LLCoordWindow window_coord(LOWORD(l_param), HIWORD(l_param));
-        LLCoordGL gl_coord;
-
         // pass along extended flag in mask
         MASK mask = (l_param >> 16 & KF_EXTENDED) ? MASK_EXTENDED : 0x0;
         BOOL eat_keystroke = TRUE;
@@ -2665,35 +2649,19 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
             LL_PROFILE_ZONE_NAMED("mwp - WM_LBUTTONDOWN");
             {
                 LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
-                window_imp->post([=]()
+                window_imp->postMouseButtonEvent([=]()
                     {
-                        auto glc = gl_coord;
                         sHandleLeftMouseUp = true;
-
+                        
                         if (LLWinImm::isAvailable() && window_imp->mPreeditor)
                         {
                             window_imp->interruptLanguageTextInput();
                         }
-
-                        // Because we move the cursor position in the app, we need to query
-                        // to find out where the cursor at the time the event is handled.
-                        // If we don't do this, many clicks could get buffered up, and if the
-                        // first click changes the cursor position, all subsequent clicks
-                        // will occur at the wrong location.  JC
-                        if (window_imp->mMousePositionModified)
-                        {
-                            LLCoordWindow cursor_coord_window;
-                            window_imp->getCursorPosition(&cursor_coord_window);
-                            glc = cursor_coord_window.convert();
-                        }
-                        else
-                        {
-                            glc = window_coord.convert();
-                        }
+                        
                         MASK mask = gKeyboard->currentMask(TRUE);
-                        // generate move event to update mouse coordinates
-                        window_imp->mCallbacks->handleMouseMove(window_imp, glc, mask);
-                        window_imp->mCallbacks->handleMouseDown(window_imp, glc, mask);
+                        auto gl_coord = window_imp->mCursorPosition.convert();
+                        window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+                        window_imp->mCallbacks->handleMouseDown(window_imp, gl_coord, mask);
                     });
 
                 return 0;
@@ -2704,77 +2672,43 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
         case WM_LBUTTONDBLCLK:
         {
             LL_PROFILE_ZONE_NAMED("mwp - WM_LBUTTONDBLCLK");
-            //RN: ignore right button double clicks for now
-            //case WM_RBUTTONDBLCLK:
-            if (!sHandleDoubleClick)
-            {
-                sHandleDoubleClick = true;
-                return 0;
-            }
-
-            // Because we move the cursor position in the app, we need to query
-            // to find out where the cursor at the time the event is handled.
-            // If we don't do this, many clicks could get buffered up, and if the
-            // first click changes the cursor position, all subsequent clicks
-            // will occur at the wrong location.  JC
-            if (window_imp->mMousePositionModified)
-            {
-                LLCoordWindow cursor_coord_window;
-                window_imp->getCursorPosition(&cursor_coord_window);
-                gl_coord = cursor_coord_window.convert();
-            }
-            else
-            {
-                gl_coord = window_coord.convert();
-            }
-            MASK mask = gKeyboard->currentMask(TRUE);
-            // generate move event to update mouse coordinates
-            window_imp->post([=]()
+            window_imp->postMouseButtonEvent([=]()
                 {
-                    window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
-                    window_imp->mCallbacks->handleDoubleClick(window_imp, gl_coord, mask);
+                    //RN: ignore right button double clicks for now
+                    //case WM_RBUTTONDBLCLK:
+                    if (!sHandleDoubleClick)
+                    {
+                        sHandleDoubleClick = true;
+                        return;
+                    }
+                    MASK mask = gKeyboard->currentMask(TRUE);
+
+                    // generate move event to update mouse coordinates
+                    window_imp->mCursorPosition = window_coord;
+                    window_imp->mCallbacks->handleDoubleClick(window_imp, window_imp->mCursorPosition.convert(), mask);
                 });
+
             return 0;
         }
         case WM_LBUTTONUP:
         {
             LL_PROFILE_ZONE_NAMED("mwp - WM_LBUTTONUP");
             {
-                LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
-
-                if (!sHandleLeftMouseUp)
-                {
-                    sHandleLeftMouseUp = true;
-                    return 0;
-                }
-                sHandleDoubleClick = true;
-                window_imp->post([=]()
+                window_imp->postMouseButtonEvent([=]()
                     {
-                        auto glc = gl_coord;
-
-                        //if (gDebugClicks)
-                        //{
-                        //	LL_INFOS("Window") << "WndProc left button up" << LL_ENDL;
-                        //}
-                        // Because we move the cursor position in the app, we need to query
-                        // to find out where the cursor at the time the event is handled.
-                        // If we don't do this, many clicks could get buffered up, and if the
-                        // first click changes the cursor position, all subsequent clicks
-                        // will occur at the wrong location.  JC
-                        if (window_imp->mMousePositionModified)
+                        LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
+                        if (!sHandleLeftMouseUp)
                         {
-                            LLCoordWindow cursor_coord_window;
-                            window_imp->getCursorPosition(&cursor_coord_window);
-                            glc = cursor_coord_window.convert();
-                        }
-                        else
-                        {
-                            glc = window_coord.convert();
+                            sHandleLeftMouseUp = true;
+                            return;
                         }
+                        sHandleDoubleClick = true;
+
+                        
                         MASK mask = gKeyboard->currentMask(TRUE);
                         // generate move event to update mouse coordinates
-                        window_imp->mCallbacks->handleMouseMove(window_imp, glc, mask);
-                        window_imp->mCallbacks->handleMouseUp(window_imp, glc, mask);
+                        window_imp->mCursorPosition = window_coord;
+                        window_imp->mCallbacks->handleMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask);
                     });
             }
             return 0;
@@ -2785,30 +2719,16 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
             LL_PROFILE_ZONE_NAMED("mwp - WM_RBUTTONDOWN");
             {
                 LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
-                if (LLWinImm::isAvailable() && window_imp->mPreeditor)
-                {
-                    WINDOW_IMP_POST(window_imp->interruptLanguageTextInput());
-                }
-
-                // Because we move the cursor position in the llviewerapp, we need to query
-                // to find out where the cursor at the time the event is handled.
-                // If we don't do this, many clicks could get buffered up, and if the
-                // first click changes the cursor position, all subsequent clicks
-                // will occur at the wrong location.  JC
-                if (window_imp->mMousePositionModified)
-                {
-                    LLCoordWindow cursor_coord_window;
-                    window_imp->getCursorPosition(&cursor_coord_window);
-                    gl_coord = cursor_coord_window.convert();
-                }
-                else
-                {
-                    gl_coord = window_coord.convert();
-                }
-                MASK mask = gKeyboard->currentMask(TRUE);
-                // generate move event to update mouse coordinates
                 window_imp->post([=]()
                     {
+                        if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+                        {
+                            WINDOW_IMP_POST(window_imp->interruptLanguageTextInput());
+                        }
+
+                        MASK mask = gKeyboard->currentMask(TRUE);
+                        // generate move event to update mouse coordinates
+                        auto gl_coord = window_imp->mCursorPosition.convert();
                         window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
                         window_imp->mCallbacks->handleRightMouseDown(window_imp, gl_coord, mask);
                     });
@@ -2822,28 +2742,11 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
             LL_PROFILE_ZONE_NAMED("mwp - WM_RBUTTONUP");
             {
                 LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
-                // Because we move the cursor position in the app, we need to query
-                // to find out where the cursor at the time the event is handled.
-                // If we don't do this, many clicks could get buffered up, and if the
-                // first click changes the cursor position, all subsequent clicks
-                // will occur at the wrong location.  JC
-                if (window_imp->mMousePositionModified)
-                {
-                    LLCoordWindow cursor_coord_window;
-                    window_imp->getCursorPosition(&cursor_coord_window);
-                    gl_coord = cursor_coord_window.convert();
-                }
-                else
-                {
-                    gl_coord = window_coord.convert();
-                }
-                MASK mask = gKeyboard->currentMask(TRUE);
-                // generate move event to update mouse coordinates
-                window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
-                if (window_imp->mCallbacks->handleRightMouseUp(window_imp, gl_coord, mask))
-                {
-                    return 0;
-                }
+                window_imp->postMouseButtonEvent([=]()
+                    {
+                        MASK mask = gKeyboard->currentMask(TRUE);
+                        window_imp->mCallbacks->handleRightMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask);
+                    });
             }
         }
         break;
@@ -2854,33 +2757,16 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
             LL_PROFILE_ZONE_NAMED("mwp - WM_MBUTTONDOWN");
             {
                 LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
-                if (LLWinImm::isAvailable() && window_imp->mPreeditor)
-                {
-                    window_imp->interruptLanguageTextInput();
-                }
+                window_imp->postMouseButtonEvent([=]()
+                    {
+                        if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+                        {
+                            window_imp->interruptLanguageTextInput();
+                        }
 
-                // Because we move the cursor position in tllviewerhe app, we need to query
-                // to find out where the cursor at the time the event is handled.
-                // If we don't do this, many clicks could get buffered up, and if the
-                // first click changes the cursor position, all subsequent clicks
-                // will occur at the wrong location.  JC
-                if (window_imp->mMousePositionModified)
-                {
-                    LLCoordWindow cursor_coord_window;
-                    window_imp->getCursorPosition(&cursor_coord_window);
-                    gl_coord = cursor_coord_window.convert();
-                }
-                else
-                {
-                    gl_coord = window_coord.convert();
-                }
-                MASK mask = gKeyboard->currentMask(TRUE);
-                // generate move event to update mouse coordinates
-                window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
-                if (window_imp->mCallbacks->handleMiddleMouseDown(window_imp, gl_coord, mask))
-                {
-                    return 0;
-                }
+                        MASK mask = gKeyboard->currentMask(TRUE);
+                        window_imp->mCallbacks->handleMiddleMouseDown(window_imp, window_imp->mCursorPosition.convert(), mask);
+                    });
             }
         }
         break;
@@ -2890,99 +2776,47 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
             LL_PROFILE_ZONE_NAMED("mwp - WM_MBUTTONUP");
             {
                 LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
-                // Because we move the cursor position in the llviewer app, we need to query
-                // to find out where the cursor at the time the event is handled.
-                // If we don't do this, many clicks could get buffered up, and if the
-                // first click changes the cursor position, all subsequent clicks
-                // will occur at the wrong location.  JC
-                if (window_imp->mMousePositionModified)
-                {
-                    LLCoordWindow cursor_coord_window;
-                    window_imp->getCursorPosition(&cursor_coord_window);
-                    gl_coord = cursor_coord_window.convert();
-                }
-                else
-                {
-                    gl_coord = window_coord.convert();
-                }
-                MASK mask = gKeyboard->currentMask(TRUE);
-                // generate move event to update mouse coordinates
-                window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
-                if (window_imp->mCallbacks->handleMiddleMouseUp(window_imp, gl_coord, mask))
-                {
-                    return 0;
-                }
+                window_imp->postMouseButtonEvent([=]()
+                    {
+                        MASK mask = gKeyboard->currentMask(TRUE);
+                        window_imp->mCallbacks->handleMiddleMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask);
+                    });
             }
         }
         break;
         case WM_XBUTTONDOWN:
         {
             LL_PROFILE_ZONE_NAMED("mwp - WM_XBUTTONDOWN");
-            {
-                LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
-                S32 button = GET_XBUTTON_WPARAM(w_param);
-                if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+            window_imp->postMouseButtonEvent([=]()
                 {
-                    window_imp->interruptLanguageTextInput();
-                }
+                    LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
+                    S32 button = GET_XBUTTON_WPARAM(w_param);
+                    if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+                    {
+                        window_imp->interruptLanguageTextInput();
+                    }
 
-                // Because we move the cursor position in tllviewerhe app, we need to query
-                // to find out where the cursor at the time the event is handled.
-                // If we don't do this, many clicks could get buffered up, and if the
-                // first click changes the cursor position, all subsequent clicks
-                // will occur at the wrong location.  JC
-                if (window_imp->mMousePositionModified)
-                {
-                    LLCoordWindow cursor_coord_window;
-                    window_imp->getCursorPosition(&cursor_coord_window);
-                    gl_coord = cursor_coord_window.convert();
-                }
-                else
-                {
-                    gl_coord = window_coord.convert();
-                }
-                MASK mask = gKeyboard->currentMask(TRUE);
-                // generate move event to update mouse coordinates
-                window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
-                // Windows uses numbers 1 and 2 for buttons, remap to 4, 5
-                if (window_imp->mCallbacks->handleOtherMouseDown(window_imp, gl_coord, mask, button + 3))
-                {
-                    return 0;
-                }
-            }
+                    MASK mask = gKeyboard->currentMask(TRUE);
+                    // Windows uses numbers 1 and 2 for buttons, remap to 4, 5
+                    window_imp->mCallbacks->handleOtherMouseDown(window_imp, window_imp->mCursorPosition.convert(), mask, button + 3);
+                });
+            
         }
         break;
 
         case WM_XBUTTONUP:
         {
             LL_PROFILE_ZONE_NAMED("mwp - WM_XBUTTONUP");
-            {
-                LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
-                S32 button = GET_XBUTTON_WPARAM(w_param);
-                // Because we move the cursor position in the llviewer app, we need to query
-                // to find out where the cursor at the time the event is handled.
-                // If we don't do this, many clicks could get buffered up, and if the
-                // first click changes the cursor position, all subsequent clicks
-                // will occur at the wrong location.  JC
-                if (window_imp->mMousePositionModified)
+            window_imp->postMouseButtonEvent([=]()
                 {
-                    LLCoordWindow cursor_coord_window;
-                    window_imp->getCursorPosition(&cursor_coord_window);
-                    gl_coord = cursor_coord_window.convert();
-                }
-                else
-                {
-                    gl_coord = window_coord.convert();
-                }
-                MASK mask = gKeyboard->currentMask(TRUE);
-                // generate move event to update mouse coordinates
-                window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
-                // Windows uses numbers 1 and 2 for buttons, remap to 4, 5
-                if (window_imp->mCallbacks->handleOtherMouseUp(window_imp, gl_coord, mask, button + 3))
-                {
-                    return 0;
-                }
-            }
+
+                    LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
+
+                    S32 button = GET_XBUTTON_WPARAM(w_param);
+                    MASK mask = gKeyboard->currentMask(TRUE);
+                    // Windows uses numbers 1 and 2 for buttons, remap to 4, 5
+                    window_imp->mCallbacks->handleOtherMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask, button + 3);
+                });
         }
         break;
 
@@ -3022,7 +2856,8 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
             // large deltas, like 480 or so.  Thus we need to scroll more quickly.
             if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta)
             {
-                window_imp->mCallbacks->handleScrollWheel(window_imp, -z_delta / WHEEL_DELTA);
+                short clicks = -z_delta / WHEEL_DELTA;
+                WINDOW_IMP_POST(window_imp->mCallbacks->handleScrollWheel(window_imp, clicks));
                 z_delta = 0;
             }
             return 0;
@@ -3082,11 +2917,16 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
         case WM_MOUSEMOVE:
         {
             LL_PROFILE_ZONE_NAMED("mwp - WM_MOUSEMOVE");
-            if (!window_imp->mMousePositionModified)
-            {
-                MASK mask = gKeyboard->currentMask(TRUE);
-                WINDOW_IMP_POST(window_imp->mCallbacks->handleMouseMove(window_imp, window_coord.convert(), mask));
-            }
+            // DO NOT use mouse event queue for move events to ensure cursor position is updated 
+            // when button events are handled
+            WINDOW_IMP_POST(
+                {
+                    LL_PROFILE_ZONE_NAMED("mwp - WM_MOUSEMOVE lambda");
+
+                    MASK mask = gKeyboard->currentMask(TRUE);
+                    window_imp->mMouseMask = mask;
+                    window_imp->mCursorPosition = window_coord;
+                });
             return 0;
         }
 
@@ -3235,6 +3075,28 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
         }
         break;
         
+        case WM_INPUT:
+        {
+            LL_PROFILE_ZONE_NAMED("MWP - WM_INPUT");
+            
+            UINT dwSize = 0;
+            GetRawInputData((HRAWINPUT)l_param, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
+            llassert(dwSize < 1024);
+
+            U8 lpb[1024];
+            
+            if (GetRawInputData((HRAWINPUT)l_param, RID_INPUT, (void*)lpb, &dwSize, sizeof(RAWINPUTHEADER)) == dwSize)
+            {
+                RAWINPUT* raw = (RAWINPUT*)lpb;
+
+                if (raw->header.dwType == RIM_TYPEMOUSE)
+                {
+                    LLMutexLock lock(&window_imp->mRawMouseMutex);
+                    window_imp->mRawMouseDelta.mX += raw->data.mouse.lLastX;
+                    window_imp->mRawMouseDelta.mY -= raw->data.mouse.lLastY;
+                }
+            }
+        }
         //list of messages we get often that we don't care to log about
         case WM_NCHITTEST:
         case WM_NCMOUSEMOVE:
@@ -4740,18 +4602,16 @@ inline void LLWindowWin32Thread::run()
 
 void LLWindowWin32Thread::post(const std::function<void()>& func)
 {
-#if LL_WINDOW_SINGLE_THREADED
-    func();
-#else
     mFunctionQueue.pushFront(func);
-#endif
 }
 
 void LLWindowWin32::post(const std::function<void()>& func)
 {
-#if LL_WINDOW_SINGLE_THREADED
-    func();
-#else
     mFunctionQueue.pushFront(func);
-#endif
 }
+
+void LLWindowWin32::postMouseButtonEvent(const std::function<void()>& func)
+{
+    mMouseQueue.pushFront(func);
+}
+
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index 5f253b5df3..b44d458fc6 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -35,6 +35,7 @@
 #include "lldragdropwin32.h"
 #include "llthread.h"
 #include "llthreadsafequeue.h"
+#include "llmutex.h"
 
 // Hack for async host by name
 #define LL_WM_HOST_RESOLVED      (WM_APP + 1)
@@ -98,6 +99,7 @@ public:
     void destroySharedContext(void* context) override;
 	/*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
 	/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
+    /*virtual*/ BOOL getCursorDelta(LLCoordCommon* delta);
 	/*virtual*/ void showCursor();
 	/*virtual*/ void hideCursor();
 	/*virtual*/ void showCursorFromMouseMove();
@@ -221,6 +223,14 @@ protected:
 	F32			mNativeAspectRatio;
 
 	HCURSOR		mCursor[ UI_CURSOR_COUNT ];  // Array of all mouse cursors
+    LLCoordWindow mCursorPosition;  // mouse cursor position, should only be mutated on main thread
+    LLMutex mRawMouseMutex;
+    RAWINPUTDEVICE mRawMouse;
+    LLCoordWindow mLastCursorPosition; // mouse cursor position from previous frame
+    LLCoordCommon mRawMouseDelta; // raw mouse delta according to window thread
+    LLCoordCommon mMouseFrameDelta; // how much the mouse moved between the last two calls to gatherInput
+
+    MASK        mMouseMask;
 
 	static BOOL sIsClassRegistered; // has the window class been registered?
 
@@ -231,7 +241,6 @@ protected:
 	BOOL		mCustomGammaSet;
 
 	LPWSTR		mIconResource;
-	BOOL		mMousePositionModified;
 	BOOL		mInputProcessingPaused;
 
 	// The following variables are for Language Text Input control.
@@ -261,7 +270,9 @@ protected:
 
     LLWindowWin32Thread* mWindowThread = nullptr;
     LLThreadSafeQueue<std::function<void()>> mFunctionQueue;
+    LLThreadSafeQueue<std::function<void()>> mMouseQueue;
     void post(const std::function<void()>& func);
+    void postMouseButtonEvent(const std::function<void()>& func);
 
 	friend class LLWindowManager;
     friend class LLWindowWin32Thread;
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index 8dd8c15b87..52d308f6bd 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -2279,7 +2279,15 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
                 
                 if (normal_channel >= 0)
                 {
-                    gGL.getTexUnit(normal_channel)->bindFast(face->getTexture(LLRender::NORMAL_MAP));
+                    auto* texture = face->getTexture(LLRender::NORMAL_MAP);
+                    if (texture)
+                    {
+                        gGL.getTexUnit(normal_channel)->bindFast(texture);
+                    }
+                    //else
+                    //{
+                        // TODO handle missing normal map
+                    //}
                 }
 
 				gGL.getTexUnit(sDiffuseChannel)->bindFast(face->getTexture(LLRender::DIFFUSE_MAP));
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 82ece85c1b..ce73037006 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -3781,8 +3781,15 @@ void LLViewerWindow::updateLayout()
 
 void LLViewerWindow::updateMouseDelta()
 {
+#if LL_WINDOWS
+    LLCoordCommon delta; 
+    mWindow->getCursorDelta(&delta);
+    S32 dx = delta.mX;
+    S32 dy = delta.mY;
+#else
 	S32 dx = lltrunc((F32) (mCurrentMousePoint.mX - mLastMousePoint.mX) * LLUI::getScaleFactor().mV[VX]);
 	S32 dy = lltrunc((F32) (mCurrentMousePoint.mY - mLastMousePoint.mY) * LLUI::getScaleFactor().mV[VY]);
+#endif
 
 	//RN: fix for asynchronous notification of mouse leaving window not working
 	LLCoordWindow mouse_pos;
-- 
cgit v1.2.3