[CMAKE]
[reactos.git] / subsystems / win32 / win32k / ntuser / input.c
index a86b198..604ede7 100644 (file)
@@ -1,27 +1,8 @@
 /*
- *  ReactOS W32 Subsystem
- *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/* $Id$
- *
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
  * PURPOSE:          Window classes
- * FILE:             subsys/win32k/ntuser/class.c
+ * FILE:             subsystems/win32/win32k/ntuser/input.c
  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
  * REVISION HISTORY:
  *       06-06-2001  CSH  Created
 
 /* INCLUDES ******************************************************************/
 
-#include <w32k.h>
+#include <win32k.h>
 #include <ntddkbd.h>
 
 #define NDEBUG
 #include <debug.h>
 
 extern BYTE gQueueKeyStateTable[];
+extern NTSTATUS Win32kInitWin32Thread(PETHREAD Thread);
 
 /* GLOBALS *******************************************************************/
 
+PTHREADINFO ptiRawInput;
+PTHREADINFO ptiKeyboard;
+PTHREADINFO ptiMouse;
+PKTIMER MasterTimer = NULL;
+PATTACHINFO gpai = NULL;
 
 static HANDLE MouseDeviceHandle;
 static HANDLE MouseThreadHandle;
@@ -46,12 +33,14 @@ static CLIENT_ID MouseThreadId;
 static HANDLE KeyboardThreadHandle;
 static CLIENT_ID KeyboardThreadId;
 static HANDLE KeyboardDeviceHandle;
+static HANDLE RawInputThreadHandle;
+static CLIENT_ID RawInputThreadId;
 static KEVENT InputThreadsStart;
 static BOOLEAN InputThreadsRunning = FALSE;
+static BYTE TrackSysKey = 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
+                                or a WM_KEYUP message */
 
 /* FUNCTIONS *****************************************************************/
-ULONG FASTCALL
-IntSystemParametersInfo(UINT uiAction, UINT uiParam,PVOID pvParam, UINT fWinIni);
 DWORD IntLastInputTick(BOOL LastInputTickSetGet);
 
 #define ClearMouseInput(mi) \
@@ -64,7 +53,7 @@ DWORD IntLastInputTick(BOOL LastInputTickSetGet);
   if(mi.dx != 0 || mi.dy != 0) \
     mi.dwFlags |= MOUSEEVENTF_MOVE; \
   if(mi.dwFlags) \
-    IntMouseInput(&mi); \
+    IntMouseInput(&mi,FALSE); \
   ClearMouseInput(mi);
 
 
@@ -81,32 +70,32 @@ DWORD IntLastInputTick(BOOL LastInputTickSetGet)
 }
 
 BOOL
-STDCALL
+APIENTRY
 NtUserGetLastInputInfo(PLASTINPUTINFO plii)
 {
     BOOL ret = TRUE;
 
     UserEnterShared();
 
-    _SEH_TRY
+    _SEH2_TRY
     {
         if (ProbeForReadUint(&plii->cbSize) != sizeof(LASTINPUTINFO))
         {
-            SetLastWin32Error(ERROR_INVALID_PARAMETER);
+            EngSetLastError(ERROR_INVALID_PARAMETER);
             ret = FALSE;
-            _SEH_LEAVE;
+            _SEH2_LEAVE;
         }
 
         ProbeForWrite(plii, sizeof(LASTINPUTINFO), sizeof(DWORD));
 
         plii->dwTime = IntLastInputTick(FALSE);
     }
-    _SEH_HANDLE
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
     {
-        SetLastNtError(_SEH_GetExceptionCode());
+        SetLastNtError(_SEH2_GetExceptionCode());
         ret = FALSE;
     }
-    _SEH_END;
+    _SEH2_END;
 
     UserLeave();
 
@@ -130,6 +119,13 @@ ProcessMouseInputData(PMOUSE_INPUT_DATA Data, ULONG InputCount)
       mi.dx += mid->LastX;
       mi.dy += mid->LastY;
 
+      /* Check if the mouse move is absolute */
+      if (mid->Flags == MOUSE_MOVE_ABSOLUTE)
+      {
+         /* Set flag to convert to screen location */
+         mi.dwFlags |= MOUSEEVENTF_ABSOLUTE;
+      }
+
       if(mid->ButtonFlags)
       {
          if(mid->ButtonFlags & MOUSE_LEFT_BUTTON_DOWN)
@@ -201,13 +197,17 @@ ProcessMouseInputData(PMOUSE_INPUT_DATA Data, ULONG InputCount)
 
 
 
-VOID STDCALL
+VOID APIENTRY
 MouseThreadMain(PVOID StartContext)
 {
    UNICODE_STRING MouseDeviceName = RTL_CONSTANT_STRING(L"\\Device\\PointerClass0");
    OBJECT_ATTRIBUTES MouseObjectAttributes;
    IO_STATUS_BLOCK Iosb;
    NTSTATUS Status;
+   MOUSE_ATTRIBUTES MouseAttr;
+
+   KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
+                       LOW_REALTIME_PRIORITY + 3);
 
    InitializeObjectAttributes(&MouseObjectAttributes,
                               &MouseDeviceName,
@@ -229,6 +229,18 @@ MouseThreadMain(PVOID StartContext)
                        FILE_SYNCHRONOUS_IO_ALERT);
    } while (!NT_SUCCESS(Status));
 
+ /* Need to setup basic win32k for this thread to process WH_MOUSE_LL messages. */
+   Status = Win32kInitWin32Thread(PsGetCurrentThread());
+   if (!NT_SUCCESS(Status))
+   {
+      DPRINT1("Win32K: Failed making mouse thread a win32 thread.\n");
+      return; //(Status);
+   }
+
+   ptiMouse = PsGetCurrentThreadWin32Thread();
+   ptiMouse->TIF_flags |= TIF_SYSTEMTHREAD;
+   DPRINT("Mouse Thread 0x%x \n", ptiMouse);
+
    KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
                        LOW_REALTIME_PRIORITY + 3);
 
@@ -245,6 +257,20 @@ MouseThreadMain(PVOID StartContext)
                                      NULL);
       DPRINT("Mouse Input Thread Starting...\n");
 
+      /*FIXME: Does mouse attributes need to be used for anything */
+      Status = NtDeviceIoControlFile(MouseDeviceHandle,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     &Iosb,
+                                     IOCTL_MOUSE_QUERY_ATTRIBUTES,
+                                     &MouseAttr, sizeof(MOUSE_ATTRIBUTES),
+                                     NULL, 0);
+      if(!NT_SUCCESS(Status))
+      {
+         DPRINT("Failed to get mouse attributes\n");
+      }
+
       /*
        * Receive and process mouse input.
        */
@@ -290,7 +316,7 @@ MouseThreadMain(PVOID StartContext)
 /* Returns a value that indicates if the key is a modifier key, and
  * which one.
  */
-static UINT STDCALL
+static UINT APIENTRY
 IntKeyboardGetModifiers(KEYBOARD_INPUT_DATA *InputData)
 {
    if (InputData->Flags & KEY_E1)
@@ -337,7 +363,7 @@ IntKeyboardGetModifiers(KEYBOARD_INPUT_DATA *InputData)
 /* Asks the keyboard driver to send a small table that shows which
  * lights should connect with which scancodes
  */
-static NTSTATUS STDCALL
+static NTSTATUS APIENTRY
 IntKeyboardGetIndicatorTrans(HANDLE KeyboardDeviceHandle,
                              PKEYBOARD_INDICATOR_TRANSLATION *IndicatorTrans)
 {
@@ -350,7 +376,7 @@ IntKeyboardGetIndicatorTrans(HANDLE KeyboardDeviceHandle,
 
    Ret = ExAllocatePoolWithTag(PagedPool,
                                Size,
-                               TAG_KEYBOARD);
+                               USERTAG_KBDTABLE);
 
    while (Ret)
    {
@@ -366,13 +392,13 @@ IntKeyboardGetIndicatorTrans(HANDLE KeyboardDeviceHandle,
       if (Status != STATUS_BUFFER_TOO_SMALL)
          break;
 
-      ExFreePool(Ret);
+      ExFreePoolWithTag(Ret, USERTAG_KBDTABLE);
 
       Size += sizeof(KEYBOARD_INDICATOR_TRANSLATION);
 
       Ret = ExAllocatePoolWithTag(PagedPool,
                                   Size,
-                                  TAG_KEYBOARD);
+                                  USERTAG_KBDTABLE);
    }
 
    if (!Ret)
@@ -380,7 +406,7 @@ IntKeyboardGetIndicatorTrans(HANDLE KeyboardDeviceHandle,
 
    if (Status != STATUS_SUCCESS)
    {
-      ExFreePool(Ret);
+      ExFreePoolWithTag(Ret, USERTAG_KBDTABLE);
       return Status;
    }
 
@@ -390,7 +416,7 @@ IntKeyboardGetIndicatorTrans(HANDLE KeyboardDeviceHandle,
 
 /* Sends the keyboard commands to turn on/off the lights.
  */
-static NTSTATUS STDCALL
+static NTSTATUS APIENTRY
 IntKeyboardUpdateLeds(HANDLE KeyboardDeviceHandle,
                       PKEYBOARD_INPUT_DATA KeyInput,
                       PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans)
@@ -431,10 +457,10 @@ IntKeyboardUpdateLeds(HANDLE KeyboardDeviceHandle,
    return STATUS_SUCCESS;
 }
 
-static VOID STDCALL
+static VOID APIENTRY
 IntKeyboardSendWinKeyMsg()
 {
-   PWINDOW_OBJECT Window;
+   PWND Window;
    MSG Mesg;
 
    if (!(Window = UserGetWindowObject(InputWindowStation->ShellWindow)))
@@ -449,16 +475,17 @@ IntKeyboardSendWinKeyMsg()
    Mesg.lParam = 0;
 
    /* The QS_HOTKEY is just a guess */
-   MsqPostMessage(Window->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
+   MsqPostMessage(Window->head.pti->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
 }
 
-static VOID STDCALL
+static VOID APIENTRY
 co_IntKeyboardSendAltKeyMsg()
 {
-   co_MsqPostKeyboardMessage(WM_SYSCOMMAND,SC_KEYMENU,0);
+   DPRINT1("co_IntKeyboardSendAltKeyMsg\n");
+//   co_MsqPostKeyboardMessage(WM_SYSCOMMAND,SC_KEYMENU,0); This sends everything into a msg loop!
 }
 
-static VOID STDCALL
+static VOID APIENTRY
 KeyboardThreadMain(PVOID StartContext)
 {
    UNICODE_STRING KeyboardDeviceName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardClass0");
@@ -468,7 +495,6 @@ KeyboardThreadMain(PVOID StartContext)
    MSG msg;
    PUSER_MESSAGE_QUEUE FocusQueue;
    struct _ETHREAD *FocusThread;
-   extern NTSTATUS Win32kInitWin32Thread(PETHREAD Thread);
 
    PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans = NULL;
    UINT ModifierState = 0;
@@ -512,6 +538,10 @@ KeyboardThreadMain(PVOID StartContext)
       return; //(Status);
    }
 
+   ptiKeyboard = PsGetCurrentThreadWin32Thread();
+   ptiKeyboard->TIF_flags |= TIF_SYSTEMTHREAD;
+   DPRINT("Keyboard Thread 0x%x \n", ptiKeyboard);
+
    KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
                        LOW_REALTIME_PRIORITY + 3);
 
@@ -679,6 +709,7 @@ KeyboardThreadMain(PVOID StartContext)
 
                   if (ModifierState == 0)
                   {
+                     UserEnterExclusive();
                      if (fsModifiers == MOD_WIN)
                         IntKeyboardSendWinKeyMsg();
                      else if (fsModifiers == MOD_ALT)
@@ -694,6 +725,7 @@ KeyboardThreadMain(PVOID StartContext)
                         }
                         co_IntKeyboardSendAltKeyMsg();
                      }
+                     UserLeave();
                      continue;
                   }
 
@@ -702,9 +734,12 @@ KeyboardThreadMain(PVOID StartContext)
             }
          }
 
+         UserEnterExclusive();
+
          for (;NumKeys;memcpy(&KeyInput, &NextKeyInput, sizeof(KeyInput)),
                NumKeys--)
          {
+            PKBL keyboardLayout = NULL;
             lParam = 0;
 
             IntKeyboardUpdateLeds(KeyboardDeviceHandle,
@@ -738,7 +773,7 @@ KeyboardThreadMain(PVOID StartContext)
                }
                else
                {
-                  RepeatCount = 0;
+                  RepeatCount = 1;
                   LastFlags = KeyInput.Flags & (KEY_E0 | KEY_E1);
                   LastMakeCode = KeyInput.MakeCode;
                }
@@ -759,7 +794,7 @@ KeyboardThreadMain(PVOID StartContext)
 
             if (ModifierState & MOD_ALT)
             {
-               lParam |= (1 << 29);
+               lParam |= (1 << 29); // wine -> (HIWORD(lParam) & KEYDATA_ALT) #define KEYDATA_ALT 0x2000
 
                if (!(KeyInput.Flags & KEY_BREAK))
                   msg.message = WM_SYSKEYDOWN;
@@ -775,29 +810,30 @@ KeyboardThreadMain(PVOID StartContext)
             }
 
             /* Find the target thread whose locale is in effect */
-               FocusQueue = IntGetFocusMessageQueue();
+            FocusQueue = IntGetFocusMessageQueue();
 
-            /* This might cause us to lose hot keys, which are important
-             * (ctrl-alt-del secure attention sequence). Not sure if it
-             * can happen though.
-             */
-            if (!FocusQueue)
-               continue;
-
-            msg.lParam = lParam;
-            msg.hwnd = FocusQueue->FocusWindow;
+            if (FocusQueue)
+            {
+                msg.hwnd = FocusQueue->FocusWindow;
 
-            FocusThread = FocusQueue->Thread;
+                FocusThread = FocusQueue->Thread;
+                if (FocusThread && FocusThread->Tcb.Win32Thread)
+                {
+                    keyboardLayout = ((PTHREADINFO)FocusThread->Tcb.Win32Thread)->KeyboardLayout;
+                }
+            }
+            if (!keyboardLayout)
+            {
+                keyboardLayout = W32kGetDefaultKeyLayout();
+            }
 
-            if (!(FocusThread && FocusThread->Tcb.Win32Thread &&
-                  ((PW32THREAD)FocusThread->Tcb.Win32Thread)->KeyboardLayout))
-               continue;
+            msg.lParam = lParam;
 
             /* This function uses lParam to fill wParam according to the
              * keyboard layout in use.
              */
             W32kKeyProcessMessage(&msg,
-                                  ((PW32THREAD)FocusThread->Tcb.Win32Thread)->KeyboardLayout->KBTables,
+                                  keyboardLayout->KBTables,
                                   KeyInput.Flags & KEY_E0 ? 0xE0 :
                                   (KeyInput.Flags & KEY_E1 ? 0xE1 : 0));
 
@@ -819,11 +855,18 @@ KeyboardThreadMain(PVOID StartContext)
                continue; /* Eat key up motion too */
             }
 
+            if (!FocusQueue)
+            {
+                /* There is no focused window to receive a keyboard message */
+                continue;
+            }
             /*
              * Post a keyboard message.
              */
             co_MsqPostKeyboardMessage(msg.message,msg.wParam,msg.lParam);
          }
+
+         UserLeave();
       }
 
 KeyboardEscape:
@@ -832,13 +875,107 @@ KeyboardEscape:
 }
 
 
-NTSTATUS FASTCALL
+static PVOID Objects[2];
+/*
+    Raw Input Thread.
+    Since this relies on InputThreadsStart, just fake it.
+ */
+static VOID APIENTRY
+RawInputThreadMain(PVOID StartContext)
+{
+  NTSTATUS Status;
+  LARGE_INTEGER DueTime;
+
+  DueTime.QuadPart = (LONGLONG)(-10000000);
+
+  do
+  {
+      KEVENT Event;
+      KeInitializeEvent(&Event, NotificationEvent, FALSE);
+      Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &DueTime);
+  } while (!NT_SUCCESS(Status));
+
+
+  Objects[0] = &InputThreadsStart;
+  Objects[1] = MasterTimer;
+
+  // This thread requires win32k!
+  Status = Win32kInitWin32Thread(PsGetCurrentThread());
+  if (!NT_SUCCESS(Status))
+  {
+     DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
+     return; //(Status);
+  }
+
+  ptiRawInput = PsGetCurrentThreadWin32Thread();
+  ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
+  DPRINT("Raw Input Thread 0x%x \n", ptiRawInput);
+
+  KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
+                       LOW_REALTIME_PRIORITY + 3);
+
+  UserEnterExclusive();
+  StartTheTimers();
+  UserLeave();
+
+  //
+  // ATM, we just have one job to handle, merge the other two later.
+  //
+  for(;;)
+  {
+      DPRINT( "Raw Input Thread Waiting for start event\n" );
+
+      Status = KeWaitForMultipleObjects( 2,
+                                         Objects,
+                                         WaitAll, //WaitAny,
+                                         WrUserRequest,
+                                         KernelMode,
+                                         TRUE,
+                                         NULL,
+                                         NULL);
+      DPRINT( "Raw Input Thread Starting...\n" );
+
+      ProcessTimers();
+  }
+  DPRINT1("Raw Input Thread Exit!\n");
+}
+
+INIT_FUNCTION
+NTSTATUS
+NTAPI
 InitInputImpl(VOID)
 {
    NTSTATUS Status;
 
    KeInitializeEvent(&InputThreadsStart, NotificationEvent, FALSE);
 
+   MasterTimer = ExAllocatePoolWithTag(NonPagedPool, sizeof(KTIMER), USERTAG_SYSTEM);
+   if (!MasterTimer)
+   {
+      DPRINT1("Win32K: Failed making Raw Input thread a win32 thread.\n");
+      ASSERT(FALSE);
+      return STATUS_UNSUCCESSFUL;
+   }
+   KeInitializeTimer(MasterTimer);
+
+   /* Initialize the default keyboard layout */
+   if(!UserInitDefaultKeyboardLayout())
+   {
+      DPRINT1("Failed to initialize default keyboard layout!\n");
+   }
+
+   Status = PsCreateSystemThread(&RawInputThreadHandle,
+                                 THREAD_ALL_ACCESS,
+                                 NULL,
+                                 NULL,
+                                 &RawInputThreadId,
+                                 RawInputThreadMain,
+                                 NULL);
+   if (!NT_SUCCESS(Status))
+   {
+      DPRINT1("Win32K: Failed to create raw thread.\n");
+   }
+
    Status = PsCreateSystemThread(&KeyboardThreadHandle,
                                  THREAD_ALL_ACCESS,
                                  NULL,
@@ -851,12 +988,6 @@ InitInputImpl(VOID)
       DPRINT1("Win32K: Failed to create keyboard thread.\n");
    }
 
-   /* Initialize the default keyboard layout */
-   if(!UserInitDefaultKeyboardLayout())
-   {
-      DPRINT1("Failed to initialize default keyboard layout!\n");
-   }
-
    Status = PsCreateSystemThread(&MouseThreadHandle,
                                  THREAD_ALL_ACCESS,
                                  NULL,
@@ -881,23 +1012,13 @@ CleanupInputImp(VOID)
    return(STATUS_SUCCESS);
 }
 
-BOOL
-STDCALL
-NtUserDragDetect(
-   HWND hWnd,
-   POINT pt) // Just like the User call.
-{
-   UNIMPLEMENTED
-   return 0;
-}
-
 BOOL FASTCALL
-IntBlockInput(PW32THREAD W32Thread, BOOL BlockIt)
+IntBlockInput(PTHREADINFO W32Thread, BOOL BlockIt)
 {
-   PW32THREAD OldBlock;
+   PTHREADINFO OldBlock;
    ASSERT(W32Thread);
 
-   if(!W32Thread->Desktop || (W32Thread->IsExiting && BlockIt))
+   if(!W32Thread->rpdesk || ((W32Thread->TIF_flags & TIF_INCLEANUP) && BlockIt))
    {
       /*
        * fail blocking if exiting the thread
@@ -911,31 +1032,31 @@ IntBlockInput(PW32THREAD W32Thread, BOOL BlockIt)
     *         e.g. services running in the service window station cannot block input
     */
    if(!ThreadHasInputAccess(W32Thread) ||
-         !IntIsActiveDesktop(W32Thread->Desktop))
+         !IntIsActiveDesktop(W32Thread->rpdesk))
    {
-      SetLastWin32Error(ERROR_ACCESS_DENIED);
+      EngSetLastError(ERROR_ACCESS_DENIED);
       return FALSE;
    }
 
-   ASSERT(W32Thread->Desktop);
-   OldBlock = W32Thread->Desktop->BlockInputThread;
+   ASSERT(W32Thread->rpdesk);
+   OldBlock = W32Thread->rpdesk->BlockInputThread;
    if(OldBlock)
    {
       if(OldBlock != W32Thread)
       {
-         SetLastWin32Error(ERROR_ACCESS_DENIED);
+         EngSetLastError(ERROR_ACCESS_DENIED);
          return FALSE;
       }
-      W32Thread->Desktop->BlockInputThread = (BlockIt ? W32Thread : NULL);
+      W32Thread->rpdesk->BlockInputThread = (BlockIt ? W32Thread : NULL);
       return OldBlock == NULL;
    }
 
-   W32Thread->Desktop->BlockInputThread = (BlockIt ? W32Thread : NULL);
+   W32Thread->rpdesk->BlockInputThread = (BlockIt ? W32Thread : NULL);
    return OldBlock == NULL;
 }
 
 BOOL
-STDCALL
+APIENTRY
 NtUserBlockInput(
    BOOL BlockIt)
 {
@@ -953,67 +1074,25 @@ CLEANUP:
 }
 
 BOOL FASTCALL
-IntSwapMouseButton(PWINSTATION_OBJECT WinStaObject, BOOL Swap)
-{
-   PSYSTEM_CURSORINFO CurInfo;
-   BOOL res;
-
-   CurInfo = IntGetSysCursorInfo(WinStaObject);
-   res = CurInfo->SwapButtons;
-   CurInfo->SwapButtons = Swap;
-   return res;
-}
-
-BOOL FASTCALL
-IntMouseInput(MOUSEINPUT *mi)
+IntMouseInput(MOUSEINPUT *mi, BOOL Injected)
 {
    const UINT SwapBtnMsg[2][2] =
       {
-         {
-            WM_LBUTTONDOWN, WM_RBUTTONDOWN
-         },
+         {WM_LBUTTONDOWN, WM_RBUTTONDOWN},
          {WM_LBUTTONUP, WM_RBUTTONUP}
       };
    const WPARAM SwapBtn[2] =
       {
          MK_LBUTTON, MK_RBUTTON
       };
-   POINT MousePos = {0}, OrgPos;
+   POINT MousePos;
    PSYSTEM_CURSORINFO CurInfo;
-   PWINSTATION_OBJECT WinSta;
-   BOOL DoMove, SwapButtons;
+   BOOL SwapButtons;
    MSG Msg;
-   HBITMAP hBitmap;
-   BITMAPOBJ *BitmapObj;
-   SURFOBJ *SurfObj;
-   PDC dc;
-   PWINDOW_OBJECT DesktopWindow;
-
-#if 1
-
-   HDC hDC;
-
-   /* FIXME - get the screen dc from the window station or desktop */
-   if(!(hDC = IntGetScreenDC()))
-   {
-      return FALSE;
-   }
-#endif
 
    ASSERT(mi);
-#if 0
-
-   WinSta = PsGetCurrentProcessWin32Process()->WindowStation;
-#else
-   /* FIXME - ugly hack but as long as we're using this dumb callback from the
-   mouse class driver, we can't access the window station from the calling
-   process */
-   WinSta = InputWindowStation;
-#endif
 
-   ASSERT(WinSta);
-
-   CurInfo = IntGetSysCursorInfo(WinSta);
+   CurInfo = IntGetSysCursorInfo();
 
    if(!mi->time)
    {
@@ -1022,141 +1101,92 @@ IntMouseInput(MOUSEINPUT *mi)
       mi->time = MsqCalculateMessageTime(&LargeTickCount);
    }
 
-   SwapButtons = CurInfo->SwapButtons;
-   DoMove = FALSE;
+   SwapButtons = gspv.bMouseBtnSwap;
 
-   IntGetCursorLocation(WinSta, &MousePos);
-   OrgPos.x = MousePos.x;
-   OrgPos.y = MousePos.y;
+   MousePos = gpsi->ptCursor;
 
    if(mi->dwFlags & MOUSEEVENTF_MOVE)
    {
       if(mi->dwFlags & MOUSEEVENTF_ABSOLUTE)
       {
-         MousePos.x = mi->dx;
-         MousePos.y = mi->dy;
+         MousePos.x = mi->dx * UserGetSystemMetrics(SM_CXVIRTUALSCREEN) >> 16;
+         MousePos.y = mi->dy * UserGetSystemMetrics(SM_CYVIRTUALSCREEN) >> 16;
       }
       else
       {
          MousePos.x += mi->dx;
          MousePos.y += mi->dy;
       }
-
-      DesktopWindow = IntGetWindowObject(WinSta->ActiveDesktop->DesktopWindow);
-
-      if (DesktopWindow)
-      {
-         if(MousePos.x >= DesktopWindow->Wnd->ClientRect.right)
-            MousePos.x = DesktopWindow->Wnd->ClientRect.right - 1;
-         if(MousePos.y >= DesktopWindow->Wnd->ClientRect.bottom)
-            MousePos.y = DesktopWindow->Wnd->ClientRect.bottom - 1;
-         UserDereferenceObject(DesktopWindow);
-      }
-
-      if(MousePos.x < 0)
-         MousePos.x = 0;
-      if(MousePos.y < 0)
-         MousePos.y = 0;
-
-      if(CurInfo->CursorClipInfo.IsClipped)
-      {
-         /* The mouse cursor needs to be clipped */
-
-         if(MousePos.x >= (LONG)CurInfo->CursorClipInfo.Right)
-            MousePos.x = (LONG)CurInfo->CursorClipInfo.Right;
-         if(MousePos.x < (LONG)CurInfo->CursorClipInfo.Left)
-            MousePos.x = (LONG)CurInfo->CursorClipInfo.Left;
-         if(MousePos.y >= (LONG)CurInfo->CursorClipInfo.Bottom)
-            MousePos.y = (LONG)CurInfo->CursorClipInfo.Bottom;
-         if(MousePos.y < (LONG)CurInfo->CursorClipInfo.Top)
-            MousePos.y = (LONG)CurInfo->CursorClipInfo.Top;
-      }
-
-      DoMove = (MousePos.x != OrgPos.x || MousePos.y != OrgPos.y);
-   }
-
-   if (DoMove)
-   {
-      dc = DC_LockDc(hDC);
-      if (dc)
-      {
-         hBitmap = dc->w.hBitmap;
-         DC_UnlockDc(dc);
-
-         BitmapObj = BITMAPOBJ_LockBitmap(hBitmap);
-         if (BitmapObj)
-         {
-            SurfObj = &BitmapObj->SurfObj;
-
-            if (CurInfo->ShowingCursor)
-            {
-               IntEngMovePointer(SurfObj, MousePos.x, MousePos.y, &(GDIDEV(SurfObj)->Pointer.Exclude));
-            }
-            /* Only now, update the info in the GDIDEVICE, so EngMovePointer can
-            * use the old values to move the pointer image */
-            GDIDEV(SurfObj)->Pointer.Pos.x = MousePos.x;
-            GDIDEV(SurfObj)->Pointer.Pos.y = MousePos.y;
-
-            BITMAPOBJ_UnlockBitmap(BitmapObj);
-         }
-      }
    }
 
    /*
     * Insert the messages into the system queue
     */
-
-   Msg.wParam = CurInfo->ButtonsDown;
+   Msg.wParam = 0;
    Msg.lParam = MAKELPARAM(MousePos.x, MousePos.y);
    Msg.pt = MousePos;
-   if(DoMove)
+
+   if (gQueueKeyStateTable[VK_SHIFT] & 0xc0)
    {
-      Msg.message = WM_MOUSEMOVE;
-      MsqInsertSystemMessage(&Msg);
+      Msg.wParam |= MK_SHIFT;
    }
 
-   Msg.message = 0;
+   if (gQueueKeyStateTable[VK_CONTROL] & 0xc0)
+   {
+      Msg.wParam |= MK_CONTROL;
+   }
+
+   if(mi->dwFlags & MOUSEEVENTF_MOVE)
+   {
+      UserSetCursorPos(MousePos.x, MousePos.y, Injected, mi->dwExtraInfo, TRUE);
+   }
    if(mi->dwFlags & MOUSEEVENTF_LEFTDOWN)
    {
       gQueueKeyStateTable[VK_LBUTTON] |= 0xc0;
       Msg.message = SwapBtnMsg[0][SwapButtons];
       CurInfo->ButtonsDown |= SwapBtn[SwapButtons];
-      MsqInsertSystemMessage(&Msg);
+      Msg.wParam |= CurInfo->ButtonsDown;
+      co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
    }
    else if(mi->dwFlags & MOUSEEVENTF_LEFTUP)
    {
       gQueueKeyStateTable[VK_LBUTTON] &= ~0x80;
       Msg.message = SwapBtnMsg[1][SwapButtons];
       CurInfo->ButtonsDown &= ~SwapBtn[SwapButtons];
-      MsqInsertSystemMessage(&Msg);
+      Msg.wParam |= CurInfo->ButtonsDown;
+      co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
    }
    if(mi->dwFlags & MOUSEEVENTF_MIDDLEDOWN)
    {
       gQueueKeyStateTable[VK_MBUTTON] |= 0xc0;
       Msg.message = WM_MBUTTONDOWN;
       CurInfo->ButtonsDown |= MK_MBUTTON;
-      MsqInsertSystemMessage(&Msg);
+      Msg.wParam |= CurInfo->ButtonsDown;
+      co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
    }
    else if(mi->dwFlags & MOUSEEVENTF_MIDDLEUP)
    {
       gQueueKeyStateTable[VK_MBUTTON] &= ~0x80;
       Msg.message = WM_MBUTTONUP;
       CurInfo->ButtonsDown &= ~MK_MBUTTON;
-      MsqInsertSystemMessage(&Msg);
+      Msg.wParam |= CurInfo->ButtonsDown;
+      co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
    }
    if(mi->dwFlags & MOUSEEVENTF_RIGHTDOWN)
    {
       gQueueKeyStateTable[VK_RBUTTON] |= 0xc0;
       Msg.message = SwapBtnMsg[0][!SwapButtons];
       CurInfo->ButtonsDown |= SwapBtn[!SwapButtons];
-      MsqInsertSystemMessage(&Msg);
+      Msg.wParam |= CurInfo->ButtonsDown;
+      co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
    }
    else if(mi->dwFlags & MOUSEEVENTF_RIGHTUP)
    {
       gQueueKeyStateTable[VK_RBUTTON] &= ~0x80;
       Msg.message = SwapBtnMsg[1][!SwapButtons];
       CurInfo->ButtonsDown &= ~SwapBtn[!SwapButtons];
-      MsqInsertSystemMessage(&Msg);
+      Msg.wParam |= CurInfo->ButtonsDown;
+      co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
    }
 
    if((mi->dwFlags & (MOUSEEVENTF_XDOWN | MOUSEEVENTF_XUP)) &&
@@ -1172,16 +1202,16 @@ IntMouseInput(MOUSEINPUT *mi)
       if(mi->mouseData & XBUTTON1)
       {
          gQueueKeyStateTable[VK_XBUTTON1] |= 0xc0;
+         CurInfo->ButtonsDown |= MK_XBUTTON1;
          Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
-         CurInfo->ButtonsDown |= XBUTTON1;
-         MsqInsertSystemMessage(&Msg);
+         co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
       }
       if(mi->mouseData & XBUTTON2)
       {
          gQueueKeyStateTable[VK_XBUTTON2] |= 0xc0;
+         CurInfo->ButtonsDown |= MK_XBUTTON2;
          Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
-         CurInfo->ButtonsDown |= XBUTTON2;
-         MsqInsertSystemMessage(&Msg);
+         co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
       }
    }
    else if(mi->dwFlags & MOUSEEVENTF_XUP)
@@ -1190,42 +1220,253 @@ IntMouseInput(MOUSEINPUT *mi)
       if(mi->mouseData & XBUTTON1)
       {
          gQueueKeyStateTable[VK_XBUTTON1] &= ~0x80;
+         CurInfo->ButtonsDown &= ~MK_XBUTTON1;
          Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON1);
-         CurInfo->ButtonsDown &= ~XBUTTON1;
-         MsqInsertSystemMessage(&Msg);
+         co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
       }
       if(mi->mouseData & XBUTTON2)
       {
          gQueueKeyStateTable[VK_XBUTTON2] &= ~0x80;
+         CurInfo->ButtonsDown &= ~MK_XBUTTON2;
          Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, XBUTTON2);
-         CurInfo->ButtonsDown &= ~XBUTTON2;
-         MsqInsertSystemMessage(&Msg);
+         co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
       }
    }
    if(mi->dwFlags & MOUSEEVENTF_WHEEL)
    {
       Msg.message = WM_MOUSEWHEEL;
       Msg.wParam = MAKEWPARAM(CurInfo->ButtonsDown, mi->mouseData);
-      MsqInsertSystemMessage(&Msg);
+      co_MsqInsertMouseMessage(&Msg, Injected, mi->dwExtraInfo, TRUE);
+   }
+
+   return TRUE;
+}
+
+BOOL FASTCALL
+IntKeyboardInput(KEYBDINPUT *ki, BOOL Injected)
+{
+   PUSER_MESSAGE_QUEUE FocusMessageQueue;
+   MSG Msg;
+   LARGE_INTEGER LargeTickCount;
+   KBDLLHOOKSTRUCT KbdHookData;
+   WORD flags, wVkStripped, wVkL, wVkR, wVk = ki->wVk, vk_hook = ki->wVk;
+
+   Msg.lParam = 0;
+
+   // Condition may arise when calling MsqPostMessage and waiting for an event.
+   ASSERT (UserIsEntered());
+
+   wVk = LOBYTE(wVk);
+   Msg.wParam = wVk;
+   flags = LOBYTE(ki->wScan);
+
+   if (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) flags |= KF_EXTENDED;
+   /* FIXME: set KF_DLGMODE and KF_MENUMODE when needed */
+
+   /* strip left/right for menu, control, shift */
+   switch (wVk)
+   {
+   case VK_MENU:
+   case VK_LMENU:
+   case VK_RMENU:
+      wVk = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) ? VK_RMENU : VK_LMENU;
+      wVkStripped = VK_MENU;
+      wVkL = VK_LMENU;
+      wVkR = VK_RMENU;
+      break;
+   case VK_CONTROL:
+   case VK_LCONTROL:
+   case VK_RCONTROL:
+      wVk = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) ? VK_RCONTROL : VK_LCONTROL;
+      wVkStripped = VK_CONTROL;
+      wVkL = VK_LCONTROL;
+      wVkR = VK_RCONTROL;
+      break;
+   case VK_SHIFT:
+   case VK_LSHIFT:
+   case VK_RSHIFT:
+      wVk = (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) ? VK_RSHIFT : VK_LSHIFT;
+      wVkStripped = VK_SHIFT;
+      wVkL = VK_LSHIFT;
+      wVkR = VK_RSHIFT;
+      break;
+   default:
+      wVkStripped = wVkL = wVkR = wVk;
+   }
+
+   if (ki->dwFlags & KEYEVENTF_KEYUP)
+   {
+      Msg.message = WM_KEYUP;
+      if (((gQueueKeyStateTable[VK_MENU] & 0x80) &&
+          ((wVkStripped == VK_MENU) || (wVkStripped == VK_CONTROL)
+           || !(gQueueKeyStateTable[VK_CONTROL] & 0x80)))
+          || (wVkStripped == VK_F10))
+      {
+         if( TrackSysKey == VK_MENU || /* <ALT>-down/<ALT>-up sequence */
+             (wVkStripped != VK_MENU)) /* <ALT>-down...<something else>-up */
+             Msg.message = WM_SYSKEYUP;
+         TrackSysKey = 0;
+      }
+      flags |= KF_REPEAT | KF_UP;
+   }
+   else
+   {
+      Msg.message = WM_KEYDOWN;
+      if (((gQueueKeyStateTable[VK_MENU] & 0x80 || wVkStripped == VK_MENU) &&
+          !(gQueueKeyStateTable[VK_CONTROL] & 0x80 || wVkStripped == VK_CONTROL))
+          || (wVkStripped == VK_F10))
+      {
+         Msg.message = WM_SYSKEYDOWN;
+         TrackSysKey = wVkStripped;
+      }
+      if (!(ki->dwFlags & KEYEVENTF_UNICODE) && gQueueKeyStateTable[wVk] & 0x80) flags |= KF_REPEAT;
+   }
+
+   if (ki->dwFlags & KEYEVENTF_UNICODE)
+   {
+      vk_hook = Msg.wParam = wVk = VK_PACKET;
+      Msg.lParam = MAKELPARAM(1 /* repeat count */, ki->wScan);
+   }
+
+   FocusMessageQueue = IntGetFocusMessageQueue();
+
+   Msg.hwnd = 0;
+
+   if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
+       Msg.hwnd = FocusMessageQueue->FocusWindow;
+
+   if (!ki->time)
+   {
+      KeQueryTickCount(&LargeTickCount);
+      Msg.time = MsqCalculateMessageTime(&LargeTickCount);
+   }
+   else
+      Msg.time = ki->time;
+
+   /* All messages have to contain the cursor point. */
+   Msg.pt = gpsi->ptCursor;
+
+   KbdHookData.vkCode = vk_hook;
+   KbdHookData.scanCode = ki->wScan;
+   KbdHookData.flags = flags >> 8;
+   if (Injected) KbdHookData.flags |= LLKHF_INJECTED;
+   KbdHookData.time = Msg.time;
+   KbdHookData.dwExtraInfo = ki->dwExtraInfo;
+   if (co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
+   {
+      DPRINT1("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
+             Msg.message, vk_hook, Msg.lParam);
+
+      return FALSE;
+   }
+
+   if (!(ki->dwFlags & KEYEVENTF_UNICODE))
+   {
+      if (ki->dwFlags & KEYEVENTF_KEYUP)
+      {
+         gQueueKeyStateTable[wVk] &= ~0x80;
+         gQueueKeyStateTable[wVkStripped] = gQueueKeyStateTable[wVkL] | gQueueKeyStateTable[wVkR];
+      }
+      else
+      {
+         if (!(gQueueKeyStateTable[wVk] & 0x80)) gQueueKeyStateTable[wVk] ^= 0x01;
+         gQueueKeyStateTable[wVk] |= 0xc0;
+         gQueueKeyStateTable[wVkStripped] = gQueueKeyStateTable[wVkL] | gQueueKeyStateTable[wVkR];
+      }
+
+      if (gQueueKeyStateTable[VK_MENU] & 0x80) flags |= KF_ALTDOWN;
+
+      if (wVkStripped == VK_SHIFT) flags &= ~KF_EXTENDED;
+
+      Msg.lParam = MAKELPARAM(1 /* repeat count */, flags);
+   }
+
+   if (FocusMessageQueue == NULL)
+   {
+         DPRINT("No focus message queue\n");
+
+         return FALSE;
+   }
+
+   if (FocusMessageQueue->FocusWindow != (HWND)0)
+   {
+         Msg.hwnd = FocusMessageQueue->FocusWindow;
+         DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
+
+         FocusMessageQueue->Desktop->pDeskInfo->LastInputWasKbd = TRUE;
+
+         Msg.pt = gpsi->ptCursor;
+      // Post to hardware queue, based on the first part of wine "some GetMessage tests"
+      // in test_PeekMessage()
+         MsqPostMessage(FocusMessageQueue, &Msg, TRUE, QS_KEY);
+   }
+   else
+   {
+         DPRINT("Invalid focus window handle\n");
    }
 
    return TRUE;
 }
 
 BOOL FASTCALL
-IntKeyboardInput(KEYBDINPUT *ki)
+UserAttachThreadInput( PTHREADINFO pti, PTHREADINFO ptiTo, BOOL fAttach)
 {
-   return FALSE;
+   PATTACHINFO pai;
+
+   /* Can not be the same thread.*/
+   if (pti == ptiTo) return FALSE;
+
+   /* Do not attach to system threads or between different desktops. */
+   if ( pti->TIF_flags & TIF_DONTATTACHQUEUE ||
+        ptiTo->TIF_flags & TIF_DONTATTACHQUEUE ||
+        pti->rpdesk != ptiTo->rpdesk )
+      return FALSE;
+
+   /* If Attach set, allocate and link. */
+   if ( fAttach )
+   {
+      pai = ExAllocatePoolWithTag(PagedPool, sizeof(ATTACHINFO), USERTAG_ATTACHINFO);
+      if ( !pai ) return FALSE;
+
+      pai->paiNext = gpai;
+      pai->pti1 = pti;
+      pai->pti2 = ptiTo;
+      gpai = pai;
+   }
+   else /* If clear, unlink and free it. */
+   {
+      PATTACHINFO paiprev = NULL;
+
+      if ( !gpai ) return FALSE;
+
+      pai = gpai;
+
+      /* Search list and free if found or return false. */
+      do
+      {
+        if ( pai->pti2 == ptiTo && pai->pti1 == pti ) break;
+        paiprev = pai;
+        pai = pai->paiNext;
+      } while (pai);
+
+      if ( !pai ) return FALSE;
+
+      if (paiprev) paiprev->paiNext = pai->paiNext;
+
+      ExFreePoolWithTag(pai, USERTAG_ATTACHINFO);
+  }
+
+  return TRUE;
 }
 
 UINT
-STDCALL
+APIENTRY
 NtUserSendInput(
    UINT nInputs,
    LPINPUT pInput,
    INT cbSize)
 {
-   PW32THREAD W32Thread;
+   PTHREADINFO W32Thread;
    UINT cnt;
    DECLARE_RETURN(UINT);
 
@@ -1235,14 +1476,14 @@ NtUserSendInput(
    W32Thread = PsGetCurrentThreadWin32Thread();
    ASSERT(W32Thread);
 
-   if(!W32Thread->Desktop)
+   if(!W32Thread->rpdesk)
    {
       RETURN( 0);
    }
 
    if(!nInputs || !pInput || (cbSize != sizeof(INPUT)))
    {
-      SetLastWin32Error(ERROR_INVALID_PARAMETER);
+      EngSetLastError(ERROR_INVALID_PARAMETER);
       RETURN( 0);
    }
 
@@ -1251,9 +1492,9 @@ NtUserSendInput(
     *         e.g. services running in the service window station cannot block input
     */
    if(!ThreadHasInputAccess(W32Thread) ||
-         !IntIsActiveDesktop(W32Thread->Desktop))
+         !IntIsActiveDesktop(W32Thread->rpdesk))
    {
-      SetLastWin32Error(ERROR_ACCESS_DENIED);
+      EngSetLastError(ERROR_ACCESS_DENIED);
       RETURN( 0);
    }
 
@@ -1273,13 +1514,13 @@ NtUserSendInput(
       switch(SafeInput.type)
       {
          case INPUT_MOUSE:
-            if(IntMouseInput(&SafeInput.mi))
+            if(IntMouseInput(&SafeInput.mi, TRUE))
             {
                cnt++;
             }
             break;
          case INPUT_KEYBOARD:
-            if(IntKeyboardInput(&SafeInput.ki))
+            if(IntKeyboardInput(&SafeInput.ki, TRUE))
             {
                cnt++;
             }
@@ -1304,4 +1545,280 @@ CLEANUP:
    END_CLEANUP;
 }
 
+BOOL
+FASTCALL
+IntQueryTrackMouseEvent(
+   LPTRACKMOUSEEVENT lpEventTrack)
+{
+   PDESKTOP pDesk;
+   PTHREADINFO pti;
+
+   pti = PsGetCurrentThreadWin32Thread();
+   pDesk = pti->rpdesk;
+
+   /* Always cleared with size set and return true. */
+   RtlZeroMemory(lpEventTrack ,sizeof(TRACKMOUSEEVENT));
+   lpEventTrack->cbSize = sizeof(TRACKMOUSEEVENT);
+
+   if ( pDesk->dwDTFlags & (DF_TME_LEAVE|DF_TME_HOVER) &&
+        pDesk->spwndTrack &&
+        pti->MessageQueue == pDesk->spwndTrack->head.pti->MessageQueue )
+   {
+      if ( pDesk->htEx != HTCLIENT )
+         lpEventTrack->dwFlags |= TME_NONCLIENT;
+
+      if ( pDesk->dwDTFlags & DF_TME_LEAVE )
+         lpEventTrack->dwFlags |= TME_LEAVE;
+
+      if ( pDesk->dwDTFlags & DF_TME_HOVER )
+      {
+         lpEventTrack->dwFlags |= TME_HOVER;
+         lpEventTrack->dwHoverTime = pDesk->dwMouseHoverTime;
+      }
+      lpEventTrack->hwndTrack = UserHMGetHandle(pDesk->spwndTrack);
+   }
+   return TRUE;
+}
+
+BOOL
+FASTCALL
+IntTrackMouseEvent(
+   LPTRACKMOUSEEVENT lpEventTrack)
+{
+   PDESKTOP pDesk;
+   PTHREADINFO pti;
+   PWND pWnd;
+   POINT point;
+
+   pti = PsGetCurrentThreadWin32Thread();
+   pDesk = pti->rpdesk;
+
+   if (!(pWnd = UserGetWindowObject(lpEventTrack->hwndTrack)))
+      return FALSE;
+
+   if ( pDesk->spwndTrack != pWnd ||
+       (pDesk->htEx != HTCLIENT) ^ !!(lpEventTrack->dwFlags & TME_NONCLIENT) )
+   {
+      if ( lpEventTrack->dwFlags & TME_LEAVE && !(lpEventTrack->dwFlags & TME_CANCEL) )
+      {
+         UserPostMessage( lpEventTrack->hwndTrack,
+                          lpEventTrack->dwFlags & TME_NONCLIENT ? WM_NCMOUSELEAVE : WM_MOUSELEAVE,
+                          0, 0);
+      }
+      DPRINT("IntTrackMouseEvent spwndTrack 0x%x pwnd 0x%x\n", pDesk->spwndTrack,pWnd);
+      return TRUE;
+   }
+
+   /* Tracking spwndTrack same as pWnd */
+   if ( lpEventTrack->dwFlags & TME_CANCEL ) // Canceled mode.
+   {
+      if ( lpEventTrack->dwFlags & TME_LEAVE )
+         pDesk->dwDTFlags &= ~DF_TME_LEAVE;
+
+      if ( lpEventTrack->dwFlags & TME_HOVER )
+      {
+         if ( pDesk->dwDTFlags & DF_TME_HOVER )
+         { // Kill hover timer.
+            IntKillTimer(pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, TRUE);
+            pDesk->dwDTFlags &= ~DF_TME_HOVER;
+         }
+      }
+   }
+   else // Not Canceled.
+   {
+      if ( lpEventTrack->dwFlags & TME_LEAVE )
+         pDesk->dwDTFlags |= DF_TME_LEAVE;
+
+      if ( lpEventTrack->dwFlags & TME_HOVER )
+      {
+         pDesk->dwDTFlags |= DF_TME_HOVER;
+
+         if ( !lpEventTrack->dwHoverTime || lpEventTrack->dwHoverTime == HOVER_DEFAULT )
+            pDesk->dwMouseHoverTime = gspv.iMouseHoverTime; // use the system default hover time-out.
+         else
+            pDesk->dwMouseHoverTime = lpEventTrack->dwHoverTime;
+         // Start timer for the hover period.
+         IntSetTimer( pWnd, ID_EVENT_SYSTIMER_MOUSEHOVER, pDesk->dwMouseHoverTime, SystemTimerProc, TMRF_SYSTEM);
+         // Get windows thread message points.
+         point = pWnd->head.pti->ptLast;
+         // Set desktop mouse hover from the system default hover rectangle.
+         RECTL_vSetRect(&pDesk->rcMouseHover,
+                         point.x - gspv.iMouseHoverWidth  / 2,
+                         point.y - gspv.iMouseHoverHeight / 2,
+                         point.x + gspv.iMouseHoverWidth  / 2,
+                         point.y + gspv.iMouseHoverHeight / 2);
+      }
+   }
+   return TRUE;
+}
+
+BOOL
+APIENTRY
+NtUserTrackMouseEvent(
+   LPTRACKMOUSEEVENT lpEventTrack)
+{
+   TRACKMOUSEEVENT saveTME;
+   BOOL Ret = FALSE;
+
+   DPRINT("Enter NtUserTrackMouseEvent\n");
+   UserEnterExclusive();
+
+   _SEH2_TRY
+   {
+      ProbeForRead(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
+      RtlCopyMemory(&saveTME, lpEventTrack, sizeof(TRACKMOUSEEVENT));
+   }
+   _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+   {
+      SetLastNtError(_SEH2_GetExceptionCode());
+      _SEH2_YIELD(goto Exit;)
+   }
+   _SEH2_END;
+
+   if ( saveTME.cbSize != sizeof(TRACKMOUSEEVENT) )
+   {
+      EngSetLastError(ERROR_INVALID_PARAMETER);
+      goto Exit;
+   }
+
+   if (saveTME.dwFlags & ~(TME_CANCEL|TME_QUERY|TME_NONCLIENT|TME_LEAVE|TME_HOVER) )
+   {
+      EngSetLastError(ERROR_INVALID_FLAGS);
+      goto Exit;
+   }
+
+   if ( saveTME.dwFlags & TME_QUERY )
+   {
+      Ret = IntQueryTrackMouseEvent(&saveTME);
+      _SEH2_TRY
+      {
+         ProbeForWrite(lpEventTrack, sizeof(TRACKMOUSEEVENT), 1);
+         RtlCopyMemory(lpEventTrack, &saveTME, sizeof(TRACKMOUSEEVENT));
+      }
+      _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+      {
+         SetLastNtError(_SEH2_GetExceptionCode());
+         Ret = FALSE;
+      }
+      _SEH2_END;
+   }
+   else
+   {
+      Ret = IntTrackMouseEvent(&saveTME);
+   }
+   
+Exit:
+   DPRINT("Leave NtUserTrackMouseEvent, ret=%i\n",Ret);
+   UserLeave();
+   return Ret;
+}
+
+extern MOUSEMOVEPOINT MouseHistoryOfMoves[];
+extern INT gcur_count; 
+
+DWORD
+APIENTRY
+NtUserGetMouseMovePointsEx(
+   UINT cbSize,
+   LPMOUSEMOVEPOINT lpptIn,
+   LPMOUSEMOVEPOINT lpptOut,
+   int nBufPoints,
+   DWORD resolution)
+{
+   MOUSEMOVEPOINT Safeppt;
+   BOOL Hit;
+   INT Count = -1;
+   DECLARE_RETURN(DWORD);
+
+   DPRINT("Enter NtUserGetMouseMovePointsEx\n");
+   UserEnterExclusive();
+
+   if ((cbSize != sizeof(MOUSEMOVEPOINT)) || (nBufPoints < 0) || (nBufPoints > 64))
+   {
+      EngSetLastError(ERROR_INVALID_PARAMETER);
+      RETURN( -1);
+   }
+
+   if (!lpptIn || (!lpptOut && nBufPoints))
+   {
+      EngSetLastError(ERROR_NOACCESS);
+      RETURN( -1);
+   }
+
+   _SEH2_TRY
+   {
+      ProbeForRead( lpptIn, cbSize, 1);
+      RtlCopyMemory(&Safeppt, lpptIn, cbSize);
+   }
+   _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+   {
+      SetLastNtError(_SEH2_GetExceptionCode());
+      _SEH2_YIELD(RETURN( -1))
+   }
+   _SEH2_END;
+
+   // http://msdn.microsoft.com/en-us/library/ms646259(v=vs.85).aspx
+   // This explains the math issues in transforming points.
+   Count = gcur_count; // FIFO is forward so retrieve backward.
+   Hit = FALSE;
+   do
+   {
+       if (Safeppt.x == 0 && Safeppt.y == 0)
+          break; // No test.
+       // Finds the point, it returns the last nBufPoints prior to and including the supplied point. 
+       if (MouseHistoryOfMoves[Count].x == Safeppt.x && MouseHistoryOfMoves[Count].y == Safeppt.y)
+       {
+          if ( Safeppt.time ) // Now test time and it seems to be absolute.
+          {
+             if (Safeppt.time == MouseHistoryOfMoves[Count].time)
+             {
+                Hit = TRUE;
+                break;
+             }
+             else
+             {
+                if (--Count < 0) Count = 63;
+                continue;
+             }
+          }
+          Hit = TRUE;
+          break;
+       }
+       if (--Count < 0) Count = 63;
+   }
+   while ( Count != gcur_count);
+
+   switch(resolution)
+   {
+     case GMMP_USE_DISPLAY_POINTS:
+        if (nBufPoints)
+        {
+           _SEH2_TRY
+           {
+              ProbeForWrite(lpptOut, cbSize, 1);
+           }
+           _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+           {
+              SetLastNtError(_SEH2_GetExceptionCode());
+              _SEH2_YIELD(RETURN( -1))
+           }
+           _SEH2_END;
+        }
+        Count = nBufPoints;
+        break;
+     case GMMP_USE_HIGH_RESOLUTION_POINTS:
+        break;
+     default:
+        EngSetLastError(ERROR_POINT_NOT_FOUND);
+        RETURN( -1);
+   }
+
+   RETURN( Count);
+
+CLEANUP:
+   DPRINT("Leave NtUserGetMouseMovePointsEx, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
+}
+
 /* EOF */