[WIN32K:NTUSER]
[reactos.git] / reactos / win32ss / user / ntuser / input.c
index a3e4859..62ab66e 100644 (file)
@@ -2,7 +2,7 @@
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS Win32k subsystem
  * PURPOSE:          General input functions
- * FILE:             subsystems/win32/win32k/ntuser/input.c
+ * FILE:             win32ss/user/ntuser/input.c
  * PROGRAMERS:       Casper S. Hornstrup (chorns@users.sourceforge.net)
  *                   Rafal Harabien (rafalh@reactos.org)
  */
@@ -13,12 +13,10 @@ DBG_DEFAULT_CHANNEL(UserInput);
 /* GLOBALS *******************************************************************/
 
 PTHREADINFO ptiRawInput;
-PTHREADINFO ptiKeyboard;
-PTHREADINFO ptiMouse;
 PKTIMER MasterTimer = NULL;
 PATTACHINFO gpai = NULL;
 INT paiCount = 0;
-HANDLE ghKeyboardDevice;
+HANDLE ghKeyboardDevice = NULL;
 
 static DWORD LastInputTick = 0;
 static HANDLE ghMouseDevice;
@@ -62,7 +60,7 @@ DoTheScreenSaver(VOID)
         TO = 1000 * gspv.iScrSaverTimeout;
         if (Test > TO)
         {
-            TRACE("Screensaver Message Start! Tick %d Timeout %d \n", Test, gspv.iScrSaverTimeout);
+            TRACE("Screensaver Message Start! Tick %lu Timeout %d \n", Test, gspv.iScrSaverTimeout);
 
             if (ppiScrnSaver) // We are or we are not the screensaver, prevent reentry...
             {
@@ -102,7 +100,7 @@ OpenInputDevice(PHANDLE pHandle, PFILE_OBJECT *ppObject, CONST WCHAR *pszDeviceN
 
     InitializeObjectAttributes(&ObjectAttributes,
                                &DeviceName,
-                               0,
+                               OBJ_KERNEL_HANDLE,
                                NULL,
                                NULL);
 
@@ -127,17 +125,19 @@ OpenInputDevice(PHANDLE pHandle, PFILE_OBJECT *ppObject, CONST WCHAR *pszDeviceN
  * Reads data from input devices and supports win32 timers
  */
 VOID NTAPI
-RawInputThreadMain()
+RawInputThreadMain(VOID)
 {
     NTSTATUS MouStatus = STATUS_UNSUCCESSFUL, KbdStatus = STATUS_UNSUCCESSFUL, Status;
     IO_STATUS_BLOCK MouIosb, KbdIosb;
-    PFILE_OBJECT pKbdDevice, pMouDevice;
+    PFILE_OBJECT pKbdDevice = NULL, pMouDevice = NULL;
     LARGE_INTEGER ByteOffset;
     //LARGE_INTEGER WaitTimeout;
-    PVOID WaitObjects[3], pSignaledObject = NULL;
-    ULONG cWaitObjects = 0, cMaxWaitObjects = 1;
+    PVOID WaitObjects[4], pSignaledObject = NULL;
+    KWAIT_BLOCK WaitBlockArray[RTL_NUMBER_OF(WaitObjects)];
+    ULONG cWaitObjects = 0, cMaxWaitObjects = 2;
     MOUSE_INPUT_DATA MouseInput;
     KEYBOARD_INPUT_DATA KeyInput;
+    PVOID ShutdownEvent;
 
     ByteOffset.QuadPart = (LONGLONG)0;
     //WaitTimeout.QuadPart = (LONGLONG)(-10000000);
@@ -146,7 +146,7 @@ RawInputThreadMain()
     ptiRawInput->TIF_flags |= TIF_SYSTEMTHREAD;
     ptiRawInput->pClientInfo->dwTIFlags = ptiRawInput->TIF_flags;
 
-    TRACE("Raw Input Thread 0x%x\n", ptiRawInput);
+    TRACE("Raw Input Thread %p\n", ptiRawInput);
 
     KeSetPriorityThread(&PsGetCurrentThread()->Tcb,
                         LOW_REALTIME_PRIORITY + 3);
@@ -155,7 +155,11 @@ RawInputThreadMain()
     StartTheTimers();
     UserLeave();
 
-    for(;;)
+    NT_ASSERT(ghMouseDevice == NULL);
+    NT_ASSERT(ghKeyboardDevice == NULL);
+
+    PoRequestShutdownEvent(&ShutdownEvent);
+    for (;;)
     {
         if (!ghMouseDevice)
         {
@@ -175,11 +179,20 @@ RawInputThreadMain()
             {
                 ++cMaxWaitObjects;
                 TRACE("Keyboard connected!\n");
+                // Get and load keyboard attributes.
+                UserInitKeyboard(ghKeyboardDevice);
+                UserEnterExclusive();
+                // Register the Window hotkey.
+                UserRegisterHotKey(PWND_BOTTOM, IDHK_WINKEY, MOD_WIN, 0);
+                // Register the debug hotkeys.
+                StartDebugHotKeys();
+                UserLeave();
             }
         }
 
         /* Reset WaitHandles array */
         cWaitObjects = 0;
+        WaitObjects[cWaitObjects++] = ShutdownEvent;
         WaitObjects[cWaitObjects++] = MasterTimer;
 
         if (ghMouseDevice)
@@ -232,7 +245,7 @@ RawInputThreadMain()
                                               KernelMode,
                                               TRUE,
                                               NULL,//&WaitTimeout,
-                                              NULL);
+                                              WaitBlockArray);
 
             if ((Status >= STATUS_WAIT_0) &&
                 (Status < (STATUS_WAIT_0 + (LONG)cWaitObjects)))
@@ -241,14 +254,24 @@ RawInputThreadMain()
                 pSignaledObject = WaitObjects[Status - STATUS_WAIT_0];
 
                 /* Check if it is mouse or keyboard and update status */
-                if (pSignaledObject == &pMouDevice->Event)
+                if ((MouStatus == STATUS_PENDING) &&
+                    (pSignaledObject == &pMouDevice->Event))
+                {
                     MouStatus = MouIosb.Status;
-                else if (pSignaledObject == &pKbdDevice->Event)
+                }
+                else if ((KbdStatus == STATUS_PENDING) &&
+                         (pSignaledObject == &pKbdDevice->Event))
+                {
                     KbdStatus = KbdIosb.Status;
+                }
                 else if (pSignaledObject == MasterTimer)
                 {
                     ProcessTimers();
                 }
+                else if (pSignaledObject == ShutdownEvent)
+                {
+                    break;
+                }
                 else ASSERT(FALSE);
             }
         }
@@ -287,6 +310,23 @@ RawInputThreadMain()
         else if (KbdStatus != STATUS_PENDING)
             ERR("Failed to read from keyboard: %x.\n", KbdStatus);
     }
+
+    if (ghMouseDevice)
+    {
+        (void)ZwCancelIoFile(ghMouseDevice, &MouIosb);
+        ObCloseHandle(ghMouseDevice, KernelMode);
+        ObDereferenceObject(pMouDevice);
+        ghMouseDevice = NULL;
+    }
+
+    if (ghKeyboardDevice)
+    {
+        (void)ZwCancelIoFile(ghKeyboardDevice, &KbdIosb);
+        ObCloseHandle(ghKeyboardDevice, KernelMode);
+        ObDereferenceObject(pKbdDevice);
+        ghKeyboardDevice = NULL;
+    }
+
     ERR("Raw Input Thread Exit!\n");
 }
 
@@ -294,7 +334,7 @@ RawInputThreadMain()
  * CreateSystemThreads
  *
  * Called form dedicated thread in CSRSS. RIT is started in context of this
- * thread because it needs valid Win32 process with TEB initialized
+ * thread because it needs valid Win32 process with TEB initialized.
  */
 DWORD NTAPI
 CreateSystemThreads(UINT Type)
@@ -396,24 +436,47 @@ NtUserBlockInput(
     return ret;
 }
 
-PTHREADINFO FASTCALL
-IsThreadAttach(PTHREADINFO ptiTo)
+BOOL
+FASTCALL
+IsRemoveAttachThread(PTHREADINFO pti)
 {
+    NTSTATUS Status;
     PATTACHINFO pai;
+    BOOL Ret = TRUE;
+    PTHREADINFO ptiFrom = NULL, ptiTo = NULL;
 
-    if (!gpai) return NULL;
-
-    pai = gpai;
     do
     {
-        if (pai->pti2 == ptiTo) break;
-        pai = pai->paiNext;
-    } while (pai);
+       if (!gpai) return TRUE;
+
+       pai = gpai; // Bottom of the list.
 
-    if (!pai) return NULL;
+       do
+       {
+          if (pai->pti2 == pti)
+          {
+             ptiFrom = pai->pti1;
+             ptiTo = pti;
+             break;
+          }
+          if (pai->pti1 == pti)
+          {
+             ptiFrom = pti;
+             ptiTo = pai->pti2;
+             break;
+          }
+          pai = pai->paiNext;
 
-    // Return ptiFrom.
-    return pai->pti1;
+       } while (pai);
+
+       if (!pai && !ptiFrom && !ptiTo) break;
+
+       Status = UserAttachThreadInput(ptiFrom, ptiTo, FALSE);
+       if (!NT_SUCCESS(Status)) Ret = FALSE;
+
+    } while (Ret);
+
+    return Ret;
 }
 
 NTSTATUS FASTCALL
@@ -421,6 +484,7 @@ UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
 {
     MSG msg;
     PATTACHINFO pai;
+    PCURICON_OBJECT CurIcon;
 
     /* Can not be the same thread. */
     if (ptiFrom == ptiTo) return STATUS_INVALID_PARAMETER;
@@ -448,43 +512,65 @@ UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
         paiCount++;
         ERR("Attach Allocated! ptiFrom 0x%p  ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
 
-        if (ptiTo->MessageQueue == ptiFrom->MessageQueue)
+        if (ptiTo->MessageQueue != ptiFrom->MessageQueue)
         {
-           ERR("Attach Threads are already associated!\n");
-        }
 
-        ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
+           ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
+
+           if (ptiFrom->MessageQueue == gpqForeground)
+           {
+              ERR("ptiFrom is Foreground\n");
+              ptiTo->MessageQueue->spwndActive  = ptiFrom->MessageQueue->spwndActive;
+              ptiTo->MessageQueue->spwndFocus   = ptiFrom->MessageQueue->spwndFocus;
+              ptiTo->MessageQueue->spwndCapture = ptiFrom->MessageQueue->spwndCapture;
+              ptiTo->MessageQueue->QF_flags    ^= ((ptiTo->MessageQueue->QF_flags ^ ptiFrom->MessageQueue->QF_flags) & QF_CAPTURELOCKED);
+              ptiTo->MessageQueue->CaretInfo    = ptiFrom->MessageQueue->CaretInfo;
+              IntSetFocusMessageQueue(NULL);
+              IntSetFocusMessageQueue(ptiTo->MessageQueue);
+              gptiForeground = ptiTo;
+           }
+           else
+           {
+              ERR("ptiFrom NOT Foreground\n");
+              if ( ptiTo->MessageQueue->spwndActive == 0 )
+                  ptiTo->MessageQueue->spwndActive = ptiFrom->MessageQueue->spwndActive;
+              if ( ptiTo->MessageQueue->spwndFocus == 0 )
+                  ptiTo->MessageQueue->spwndFocus  = ptiFrom->MessageQueue->spwndFocus;
+           }
 
-        /* Keep the original queue in pqAttach (ie do not trash it in a second attachment) */
-        if (ptiFrom->pqAttach == NULL)
-           ptiFrom->pqAttach = ptiFrom->MessageQueue;
-        ptiFrom->MessageQueue = ptiTo->MessageQueue;
+           CurIcon = ptiFrom->MessageQueue->CursorObject;
 
-        ptiFrom->MessageQueue->cThreads++;
-        ERR("ptiTo S Share count %d\n", ptiFrom->MessageQueue->cThreads);
+           MsqDestroyMessageQueue(ptiFrom);
 
-        // FIXME: conditions?
-        if (ptiFrom->pqAttach == gpqForeground)
-        {
-           ERR("ptiFrom is Foreground\n");
-        ptiFrom->MessageQueue->spwndActive = ptiFrom->pqAttach->spwndActive;
-        ptiFrom->MessageQueue->spwndFocus = ptiFrom->pqAttach->spwndFocus;
-        ptiFrom->MessageQueue->CursorObject = ptiFrom->pqAttach->CursorObject;
-        ptiFrom->MessageQueue->spwndCapture = ptiFrom->pqAttach->spwndCapture;
-        ptiFrom->MessageQueue->QF_flags ^= ((ptiFrom->MessageQueue->QF_flags ^ ptiFrom->pqAttach->QF_flags) & QF_CAPTURELOCKED);
-        ptiFrom->MessageQueue->CaretInfo = ptiFrom->pqAttach->CaretInfo;
-        }
-        else
-        {
-           ERR("ptiFrom NOT Foreground\n");
-        }
-        if (ptiTo->MessageQueue == gpqForeground)
-        {
-           ERR("ptiTo is Foreground\n");
+           if (CurIcon)
+           {
+              // Could be global. Keep it above the water line!
+              UserReferenceObject(CurIcon);
+           }
+
+           if (CurIcon && UserObjectInDestroy(UserHMGetHandle(CurIcon)))
+           {
+              UserDereferenceObject(CurIcon);
+              CurIcon = NULL;
+           }
+
+           ptiFrom->MessageQueue = ptiTo->MessageQueue;
+
+           // Pass cursor From if To is null. Pass test_SetCursor parent_id == current pti ID.
+           if (CurIcon && ptiTo->MessageQueue->CursorObject == NULL)
+           {
+              ERR("ptiTo receiving ptiFrom Cursor\n");
+              ptiTo->MessageQueue->CursorObject = CurIcon;
+           }
+
+           ptiFrom->MessageQueue->cThreads++;
+           ERR("ptiTo S Share count %u\n", ptiFrom->MessageQueue->cThreads);
+
+           IntReferenceMessageQueue(ptiTo->MessageQueue);
         }
         else
         {
-           ERR("ptiTo NOT Foreground\n");
+           ERR("Attach Threads are already associated!\n");
         }
     }
     else /* If clear, unlink and free it. */
@@ -513,40 +599,49 @@ UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
 
         if (!Hit) return STATUS_INVALID_PARAMETER;
 
-        ASSERT(ptiFrom->pqAttach);
         ERR("Attach Free! ptiFrom 0x%p  ptiTo 0x%p paiCount %d\n",ptiFrom,ptiTo,paiCount);
 
-        /* Search list and check if the thread is attached one more time */
-        pai = gpai;
-        while(pai)
+        if (ptiTo->MessageQueue == ptiFrom->MessageQueue)
         {
-            /* If the thread is attached again , we are done */
-            if (pai->pti1 == ptiFrom) 
-            {
-                ptiFrom->MessageQueue->cThreads--;
-                ERR("ptiTo L Share count %d\n", ptiFrom->MessageQueue->cThreads);
-                /* Use the message queue of the last attachment */
-                ptiFrom->MessageQueue = pai->pti2->MessageQueue;
-                ptiFrom->MessageQueue->CursorObject = NULL;
-                ptiFrom->MessageQueue->spwndActive = NULL;
-                ptiFrom->MessageQueue->spwndFocus = NULL;
-                ptiFrom->MessageQueue->spwndCapture = NULL;
-                return STATUS_SUCCESS;
-            }
-            pai = pai->paiNext;
-        }
+           PWND spwndActive = ptiTo->MessageQueue->spwndActive;
+           PWND spwndFocus  = ptiTo->MessageQueue->spwndFocus;
 
-        ptiFrom->MessageQueue->cThreads--;
-        ERR("ptiTo E Share count %d\n", ptiFrom->MessageQueue->cThreads);
-        ptiFrom->MessageQueue = ptiFrom->pqAttach;
-        // FIXME: conditions?
-        ptiFrom->MessageQueue->CursorObject = NULL;
-        ptiFrom->MessageQueue->spwndActive = NULL;
-        ptiFrom->MessageQueue->spwndFocus = NULL;
-        ptiFrom->MessageQueue->spwndCapture = NULL;
-        ptiFrom->pqAttach = NULL;
-        ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
+           if (gptiForeground == ptiFrom)
+           {
+              ERR("ptiTo is now pti FG.\n");
+              // MessageQueue foreground is set so switch threads.
+              gptiForeground = ptiTo;
+           }
+           ptiTo->MessageQueue->cThreads--;
+           ERR("ptiTo E Share count %u\n", ptiTo->MessageQueue->cThreads);
+           ASSERT(ptiTo->MessageQueue->cThreads >= 1);
+
+           IntDereferenceMessageQueue(ptiTo->MessageQueue);
+
+           ptiFrom->MessageQueue = MsqCreateMessageQueue(ptiFrom);
+
+           if (spwndActive)
+           {
+              if (spwndActive->head.pti == ptiFrom)
+              {
+                 ptiFrom->MessageQueue->spwndActive = spwndActive;
+                 ptiTo->MessageQueue->spwndActive = 0;
+              }
+           }
+           if (spwndFocus)
+           {
+              if (spwndFocus->head.pti == ptiFrom)
+              {
+                 ptiFrom->MessageQueue->spwndFocus = spwndFocus;
+                 ptiTo->MessageQueue->spwndFocus = 0;
+              }
+           }
+           ptiTo->MessageQueue->iCursorLevel -= ptiFrom->iCursorLevel;
+        }
+        else
+        {
+           ERR("Detaching Threads are not associated!\n");
+        }
     }
     /* Note that key state, which can be ascertained by calls to the GetKeyState
        or GetKeyboardState function, is reset after a call to AttachThreadInput.
@@ -554,6 +649,8 @@ UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
      */
     RtlCopyMemory(ptiTo->MessageQueue->afKeyState, gafAsyncKeyState, sizeof(gafAsyncKeyState));
 
+    ptiTo->MessageQueue->msgDblClk.message = 0;
+
     /* Generate mouse move message */
     msg.message = WM_MOUSEMOVE;
     msg.wParam = UserGetMouseButtonsState();
@@ -564,6 +661,44 @@ UserAttachThreadInput(PTHREADINFO ptiFrom, PTHREADINFO ptiTo, BOOL fAttach)
     return STATUS_SUCCESS;
 }
 
+BOOL
+APIENTRY
+NtUserAttachThreadInput(
+    IN DWORD idAttach,
+    IN DWORD idAttachTo,
+    IN BOOL fAttach)
+{
+  NTSTATUS Status;
+  PTHREADINFO pti, ptiTo;
+  BOOL Ret = FALSE;
+
+  UserEnterExclusive();
+  TRACE("Enter NtUserAttachThreadInput %s\n",(fAttach ? "TRUE" : "FALSE" ));
+
+  pti = IntTID2PTI((HANDLE)idAttach);
+  ptiTo = IntTID2PTI((HANDLE)idAttachTo);
+
+  if ( !pti || !ptiTo )
+  {
+     TRACE("AttachThreadInput pti or ptiTo NULL.\n");
+     EngSetLastError(ERROR_INVALID_PARAMETER);
+     goto Exit;
+  }
+
+  Status = UserAttachThreadInput( pti, ptiTo, fAttach);
+  if (!NT_SUCCESS(Status))
+  {
+     TRACE("AttachThreadInput Error Status 0x%x. \n",Status);
+     EngSetLastError(RtlNtStatusToDosError(Status));
+  }
+  else Ret = TRUE;
+
+Exit:
+  TRACE("Leave NtUserAttachThreadInput, ret=%d\n",Ret);
+  UserLeave();
+  return Ret;
+}
+
 /*
  * NtUserSendInput
  *
@@ -639,7 +774,7 @@ NtUserSendInput(
     }
 
 cleanup:
-    TRACE("Leave NtUserSendInput, ret=%i\n", uRet);
+    TRACE("Leave NtUserSendInput, ret=%u\n", uRet);
     UserLeave();
     return uRet;
 }