[NtUser]
authorJames Tabor <james.tabor@reactos.org>
Mon, 24 Nov 2014 02:50:18 +0000 (02:50 +0000)
committerJames Tabor <james.tabor@reactos.org>
Mon, 24 Nov 2014 02:50:18 +0000 (02:50 +0000)
- Synchronize mouse messages. Part II
- Dedicated to Giannis Adamopoulos and Amine Khaldi.
- Additional fixes are added.
- This should be more accurate, based on "Hit Testing: Which HWND is the mouse over?":
http://blogs.msdn.com/b/dwayneneed/archive/2008/09/08/transparent-windows-in-wpf.aspx
- Note: Older FireFox menu item hilite select seem to work now, do not need to keep moving the mouse to keep it hilited to be selected.
- Test results: https://reactos.org/sites/all/modules/reactos/testman/compare.php?ids=33709,33710,33713

svn path=/trunk/; revision=65472

reactos/win32ss/user/ntuser/keyboard.c
reactos/win32ss/user/ntuser/message.c
reactos/win32ss/user/ntuser/msgqueue.c
reactos/win32ss/user/ntuser/msgqueue.h
reactos/win32ss/user/ntuser/timer.c

index 097b852..c8a7a73 100644 (file)
@@ -936,10 +936,16 @@ ProcessKeyEvent(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD d
                 Msg.lParam |= KF_MENUMODE << 16;
         }
 
+        // Post mouse move before posting key buttons, to keep it syned.
+        if (pFocusQueue->QF_flags & QF_MOUSEMOVED)
+        {
+           IntCoalesceMouseMove(pti);
+        }
+
         /* Post a keyboard message */
         TRACE("Posting keyboard msg %u wParam 0x%x lParam 0x%x\n", Msg.message, Msg.wParam, Msg.lParam);
         if (!Wnd) {ERR("Window is NULL\n");}
-        MsqPostMessage(pti, &Msg, TRUE, QS_KEY, 0);
+        MsqPostMessage(pti, &Msg, TRUE, QS_KEY, 0, dwExtraInfo);
     }
 
     return TRUE;
@@ -1154,7 +1160,7 @@ IntTranslateKbdMessage(LPMSG lpMsg,
         NewMsg.message = (lpMsg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
         NewMsg.wParam = HIWORD(lpMsg->lParam);
         NewMsg.lParam = LOWORD(lpMsg->lParam);
-        MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0);
+        MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0);
         return TRUE;
     }
 
@@ -1183,7 +1189,7 @@ IntTranslateKbdMessage(LPMSG lpMsg,
         {
             TRACE("Msg: %x '%lc' (%04x) %08x\n", NewMsg.message, wch[i], wch[i], NewMsg.lParam);
             NewMsg.wParam = wch[i];
-            MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0);
+            MsqPostMessage(pti, &NewMsg, FALSE, QS_KEY, 0, 0);
         }
         bResult = TRUE;
     }
index e705a8f..4662b4f 100644 (file)
@@ -802,6 +802,12 @@ co_IntPeekMessage( PMSG Msg,
         pti->timeLast = LargeTickCount.u.LowPart;
         pti->pcti->tickLastMsgChecked = LargeTickCount.u.LowPart;
 
+        // Post mouse moves while looping through peek messages.
+        if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED)
+        {
+           IntCoalesceMouseMove(pti);
+        }
+
         /* Dispatch sent messages here. */
         while ( co_MsqDispatchOneSentMessage(pti) )
         {
@@ -1087,7 +1093,7 @@ UserPostThreadMessage( PTHREADINFO pti,
 
     KeQueryTickCount(&LargeTickCount);
     Message.time = MsqCalculateMessageTime(&LargeTickCount);
-    MsqPostMessage(pti, &Message, FALSE, QS_POSTMESSAGE, 0);
+    MsqPostMessage(pti, &Message, FALSE, QS_POSTMESSAGE, 0, 0);
     return TRUE;
 }
 
@@ -1218,7 +1224,7 @@ UserPostMessage( HWND Wnd,
         }
         else
         {
-            MsqPostMessage(pti, &Message, FALSE, QS_POSTMESSAGE, 0);
+            MsqPostMessage(pti, &Message, FALSE, QS_POSTMESSAGE, 0, 0);
         }
     }
     return TRUE;
@@ -1411,6 +1417,7 @@ co_IntSendMessageTimeoutSingle( HWND hWnd,
 
 CLEANUP:
     if (Window) UserDerefObjectCo(Window);
+    // Current Thread and it's a Copy Data message, then free kernel memory.
     if ( !ptiSendTo && Msg == WM_COPYDATA )
     {
        ExFreePool((PVOID) lParam);
@@ -1677,7 +1684,7 @@ CLEANUP:
     END_CLEANUP;
 }
 
-
+#if 0
 /*
   This HACK function posts a message if the destination's message queue belongs to
   another thread, otherwise it sends the message. It does not support broadcast
@@ -1721,6 +1728,7 @@ co_IntPostOrSendMessage( HWND hWnd,
 
     return (LRESULT)Result;
 }
+#endif
 
 static LRESULT FASTCALL
 co_IntDoSendMessage( HWND hWnd,
index 7550aa3..f40c02f 100644 (file)
@@ -15,6 +15,9 @@ DBG_DEFAULT_CHANNEL(UserMsgQ);
 
 static PPAGED_LOOKASIDE_LIST pgMessageLookasideList;
 PUSER_MESSAGE_QUEUE gpqCursor;
+ULONG_PTR gdwMouseMoveExtraInfo = 0;
+DWORD gdwMouseMoveTimeStamp = 0;
+
 
 /* FUNCTIONS *****************************************************************/
 
@@ -311,6 +314,36 @@ UpdateKeyStateFromMsg(PUSER_MESSAGE_QUEUE MessageQueue, MSG* msg)
     }
 }
 
+/*
+    Get down key states from the queue of prior processed input message key states.
+
+    This fixes the left button dragging on the desktop and release sticking outline issue.
+    USB Tablet pointer seems to stick the most and leaves the box outline displayed.
+ */
+WPARAM FASTCALL
+MsqGetDownKeyState(PUSER_MESSAGE_QUEUE MessageQueue)
+{
+    WPARAM ret = 0;
+
+    if (gspv.bMouseBtnSwap)
+    {
+       if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_RBUTTON)) ret |= MK_LBUTTON;
+       if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_LBUTTON)) ret |= MK_RBUTTON;
+    }
+    else
+    {
+       if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_LBUTTON)) ret |= MK_LBUTTON;
+       if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_RBUTTON)) ret |= MK_RBUTTON;
+    }
+
+    if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_MBUTTON))  ret |= MK_MBUTTON;
+    if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_SHIFT))    ret |= MK_SHIFT;
+    if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_CONTROL))  ret |= MK_CONTROL;
+    if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_XBUTTON1)) ret |= MK_XBUTTON1;
+    if (IS_KEY_DOWN(MessageQueue->afKeyState, VK_XBUTTON2)) ret |= MK_XBUTTON2;
+    return ret;
+}
+
 HANDLE FASTCALL
 IntMsqSetWakeMask(DWORD WakeMask)
 {
@@ -397,26 +430,18 @@ MsqWakeQueue(PTHREADINFO pti, DWORD MessageBits, BOOL KeyEvent)
 VOID FASTCALL
 ClearMsgBitsMask(PTHREADINFO pti, UINT MessageBits)
 {
-   PUSER_MESSAGE_QUEUE Queue;
    UINT ClrMask = 0;
 
-   Queue = pti->MessageQueue;
-
    if (MessageBits & QS_KEY)
    {
       if (--pti->nCntsQBits[QSRosKey] == 0) ClrMask |= QS_KEY;
    }
-   if (MessageBits & QS_MOUSEMOVE) // ReactOS hard coded.
+   if (MessageBits & QS_MOUSEMOVE)
    {  // Account for tracking mouse moves..
       if (pti->nCntsQBits[QSRosMouseMove]) 
       {
          pti->nCntsQBits[QSRosMouseMove] = 0; // Throttle down count. Up to > 3:1 entries are ignored.
-      }
-      // Handle mouse move bits here.
-      if (Queue->MouseMoved)
-      {
          ClrMask |= QS_MOUSEMOVE;
-         Queue->MouseMoved = FALSE;
       }
    }
    if (MessageBits & QS_MOUSEBUTTON)
@@ -472,10 +497,11 @@ MsqDecPaintCountQueue(PTHREADINFO pti)
 }
 
 /*
-    Post Mouse Move.
+    Post the move or update the message still pending to be processed.
+    Do not overload the queue with mouse move messages.
  */
 VOID FASTCALL
-MsqPostMouseMove(PTHREADINFO pti, MSG* Msg)
+MsqPostMouseMove(PTHREADINFO pti, MSG* Msg, LONG_PTR ExtraInfo)
 {
     PUSER_MESSAGE Message;
     PLIST_ENTRY ListHead;
@@ -483,22 +509,59 @@ MsqPostMouseMove(PTHREADINFO pti, MSG* Msg)
 
     ListHead = &MessageQueue->HardwareMessagesListHead;
 
-    MessageQueue->MouseMoved = TRUE;
-
+    // Do nothing if empty.
     if (!IsListEmpty(ListHead->Flink))
-    {  // Look at the end of the list,
+    {
+       // Look at the end of the list,
        Message = CONTAINING_RECORD(ListHead->Blink, USER_MESSAGE, ListEntry);
-       // If the mouse move message is existing,
+
+       // If the mouse move message is existing on the list,
        if (Message->Msg.message == WM_MOUSEMOVE)
        {
-          TRACE("Post Old MM Message in Q\n");
-          Message->Msg = *Msg; // Overwrite the message with updated data!
+          // Overwrite the message with updated data!
+          Message->Msg = *Msg;
+
           MsqWakeQueue(pti, QS_MOUSEMOVE, TRUE);
           return;
        }
     }
-    TRACE("Post New MM Message to Q\n");
-    MsqPostMessage(pti, Msg, TRUE, QS_MOUSEMOVE, 0);
+
+    MsqPostMessage(pti, Msg, TRUE, QS_MOUSEMOVE, 0, ExtraInfo);
+}
+
+/*
+    Bring together the mouse move message.
+    Named "Coalesce" from Amine email ;^) (jt).
+ */
+VOID FASTCALL
+IntCoalesceMouseMove(PTHREADINFO pti)
+{
+    MSG Msg;
+    LARGE_INTEGER LargeTickCount;
+
+    // Force time stamp to update, keeping message time in sync.
+    if (gdwMouseMoveTimeStamp == 0)
+    {
+       KeQueryTickCount(&LargeTickCount);
+       gdwMouseMoveTimeStamp = MsqCalculateMessageTime(&LargeTickCount);
+    }
+
+    // Build mouse move message.
+    Msg.hwnd    = NULL;
+    Msg.message = WM_MOUSEMOVE;
+    Msg.wParam  = 0;
+    Msg.lParam  = MAKELONG(gpsi->ptCursor.x, gpsi->ptCursor.y);
+    Msg.time    = gdwMouseMoveTimeStamp;
+    Msg.pt      = gpsi->ptCursor;
+
+    // Post the move.
+    MsqPostMouseMove(pti, &Msg, gdwMouseMoveExtraInfo);
+
+    // Zero the time stamp.
+    gdwMouseMoveTimeStamp = 0;
+
+    // Clear flag since the move was posted.
+    pti->MessageQueue->QF_flags &= ~QF_MOUSEMOVED;
 }
 
 VOID FASTCALL
@@ -626,7 +689,10 @@ co_MsqInsertMouseMessage(MSG* Msg, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook
            gpqCursor = MessageQueue;
 
            /* Mouse move is a special case */
-           MsqPostMouseMove(pti, Msg);
+           MessageQueue->QF_flags |= QF_MOUSEMOVED;
+           gdwMouseMoveExtraInfo = dwExtraInfo;
+           gdwMouseMoveTimeStamp = Msg->time;
+           MsqWakeQueue(pti, QS_MOUSEMOVE, TRUE);
        }
        else
        {
@@ -636,8 +702,15 @@ co_MsqInsertMouseMessage(MSG* Msg, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook
              // ptiLastInput = pti; // Once this is set during Reboot or Shutdown, this prevents the exit window having foreground.
              // Find all the Move Mouse calls and fix mouse set active focus issues......
            }
+
+           // Post mouse move before posting mouse buttons, keep it in sync.
+           if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED)
+           {
+              IntCoalesceMouseMove(pti);
+           }
+
            TRACE("Posting mouse message to hwnd=%p!\n", UserHMGetHandle(pwnd));
-           MsqPostMessage(pti, Msg, TRUE, QS_MOUSEBUTTON, 0);
+           MsqPostMessage(pti, Msg, TRUE, QS_MOUSEBUTTON, 0, dwExtraInfo);
        }
    }
    else if (hdcScreen)
@@ -1207,7 +1280,8 @@ MsqPostMessage(PTHREADINFO pti,
                MSG* Msg,
                BOOLEAN HardwareMessage,
                DWORD MessageBits,
-               DWORD dwQEvent)
+               DWORD dwQEvent,
+               LONG_PTR ExtraInfo)
 {
    PUSER_MESSAGE Message;
    PUSER_MESSAGE_QUEUE MessageQueue;
@@ -1244,6 +1318,7 @@ MsqPostMessage(PTHREADINFO pti,
 
    if (Msg->message == WM_HOTKEY) MessageBits |= QS_HOTKEY; // Justin Case, just set it.
    Message->dwQEvent = dwQEvent;
+   Message->ExtraInfo = ExtraInfo;
    Message->QS_Flags = MessageBits;
    Message->pti = pti;
    MsqWakeQueue(pti, MessageBits, TRUE);
@@ -1375,12 +1450,13 @@ BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, UINT first, UINT
     }
     else
     {
-        pwndMsg = co_WinPosWindowFromPoint(NULL, &msg->pt, &hittest, FALSE);//TRUE);
+        pwndMsg = co_WinPosWindowFromPoint(pwndMsg, &msg->pt, &hittest, FALSE);
     }
 
     TRACE("Got mouse message for %p, hittest: 0x%x\n", msg->hwnd, hittest);
 
-    if (pwndMsg == NULL || pwndMsg->head.pti != pti)
+    // Null window or not the same "Hardware" message queue.
+    if (pwndMsg == NULL || pwndMsg->head.pti->MessageQueue != pti->MessageQueue)
     {
         /* Remove and ignore the message */
         *RemoveMessages = TRUE;
@@ -1470,6 +1546,12 @@ BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, UINT first, UINT
             TRACE("Message out of range!!!\n");
             RETURN(FALSE);
         }
+
+        // Update mouse move down keys.
+        if (message == WM_MOUSEMOVE)
+        {
+           msg->wParam = MsqGetDownKeyState(MessageQueue);
+        }
     }
 
     if(gspv.bMouseClickLock)
@@ -1739,7 +1821,8 @@ co_MsqPeekHardwareMessage(IN PTHREADINFO pti,
  */
       if ( ( !Window || // 1
             ( Window == PWND_BOTTOM && CurrentMessage->Msg.hwnd == NULL ) || // 2
-            ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) ) && // 3
+            ( Window != PWND_BOTTOM && Window->head.h == CurrentMessage->Msg.hwnd ) || // 3
+            ( CurrentMessage->Msg.message == WM_MOUSEMOVE ) ) && // Null window for mouse moves.
             ( ( ( MsgFilterLow == 0 && MsgFilterHigh == 0 ) && CurrentMessage->QS_Flags & QSflags ) ||
               ( MsgFilterLow <= CurrentMessage->Msg.message && MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
         {
index 61f5227..c7c61d7 100644 (file)
@@ -51,9 +51,7 @@ typedef struct _USER_MESSAGE_QUEUE
 
   /* Queue for hardware messages for the queue. */
   LIST_ENTRY HardwareMessagesListHead;
-  /* True if a WM_MOUSEMOVE is pending */
-  BOOLEAN MouseMoved;
-  /* Current WM_MOUSEMOVE message */
+  /* Last click message for translating double clicks */
   MSG msgDblClk;
   /* Current capture window for this queue. */
   PWND spwndCapture;
@@ -92,7 +90,7 @@ typedef struct _USER_MESSAGE_QUEUE
 #define QF_FMENUSTATUSBREAK       0x00000004
 #define QF_FMENUSTATUS            0x00000008
 #define QF_FF10STATUS             0x00000010
-#define QF_MOUSEMOVED             0x00000020 // See MouseMoved.
+#define QF_MOUSEMOVED             0x00000020
 #define QF_ACTIVATIONCHANGE       0x00000040
 #define QF_TABSWITCHING           0x00000080
 #define QF_KEYSTATERESET          0x00000100
@@ -122,7 +120,7 @@ NTSTATUS FASTCALL co_MsqSendMessage(PTHREADINFO ptirec,
            UINT uTimeout, BOOL Block, INT HookMessage, ULONG_PTR *uResult);
 PUSER_MESSAGE FASTCALL MsqCreateMessage(LPMSG Msg);
 VOID FASTCALL MsqDestroyMessage(PUSER_MESSAGE Message);
-VOID FASTCALL MsqPostMessage(PTHREADINFO, MSG*, BOOLEAN, DWORD, DWORD);
+VOID FASTCALL MsqPostMessage(PTHREADINFO, MSG*, BOOLEAN, DWORD, DWORD, LONG_PTR);
 VOID FASTCALL MsqPostQuitMessage(PTHREADINFO pti, ULONG ExitCode);
 BOOLEAN APIENTRY
 MsqPeekMessage(IN PTHREADINFO pti,
@@ -186,6 +184,7 @@ co_MsqSendMessageAsync(PTHREADINFO ptiReceiver,
                        BOOL HasPackedLParam,
                        INT HookMessage);
 
+VOID FASTCALL IntCoalesceMouseMove(PTHREADINFO);
 LRESULT FASTCALL IntDispatchMessage(MSG* Msg);
 BOOL FASTCALL IntTranslateKbdMessage(LPMSG lpMsg, UINT flags);
 VOID FASTCALL co_MsqInsertMouseMessage(MSG* Msg, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook);
@@ -246,6 +245,7 @@ BOOL FASTCALL co_MsqReplyMessage(LRESULT);
 VOID FASTCALL MsqWakeQueue(PTHREADINFO,DWORD,BOOL);
 VOID FASTCALL ClearMsgBitsMask(PTHREADINFO,UINT);
 BOOL FASTCALL IntCallMsgFilter(LPMSG,INT);
+WPARAM FASTCALL MsqGetDownKeyState(PUSER_MESSAGE_QUEUE);
 
 int UserShowCursor(BOOL bShow);
 PCURICON_OBJECT
index f3355a5..76697ac 100644 (file)
@@ -320,7 +320,7 @@ SystemTimerProc(HWND hwnd,
              {
                 if (pDesk->htEx == HTCLIENT) // In a client area.
                 {
-                   wParam = UserGetMouseButtonsState();
+                   wParam = MsqGetDownKeyState(pWnd->head.pti->MessageQueue);
                    Msg = WM_MOUSEHOVER;
 
                    if (pWnd->ExStyle & WS_EX_LAYOUTRTL)
@@ -405,7 +405,7 @@ PostTimerMessages(PWND Window)
            Msg.wParam  = (WPARAM) pTmr->nID;
            Msg.lParam  = (LPARAM) pTmr->pfn;
 
-           MsqPostMessage(pti, &Msg, FALSE, (QS_POSTMESSAGE|QS_ALLPOSTMESSAGE), 0);
+           MsqPostMessage(pti, &Msg, FALSE, (QS_POSTMESSAGE|QS_ALLPOSTMESSAGE), 0, 0);
            pTmr->flags &= ~TMRF_READY;
            ClearMsgBitsMask(pti, QS_TIMER);
            Hit = TRUE;