Present for GreatLord:
Implement WH_KEYBOARD_LL hook
Modified: trunk/reactos/lib/user32/windows/hook.c
Modified: trunk/reactos/subsys/win32k/include/msgqueue.h
Modified: trunk/reactos/subsys/win32k/main/dllmain.c
Modified: trunk/reactos/subsys/win32k/ntuser/callback.c
Modified: trunk/reactos/subsys/win32k/ntuser/hook.c
Modified: trunk/reactos/subsys/win32k/ntuser/input.c
Modified: trunk/reactos/subsys/win32k/ntuser/message.c
Modified: trunk/reactos/subsys/win32k/ntuser/msgqueue.c
Modified: trunk/reactos/w32api/include/winuser.h

Modified: trunk/reactos/lib/user32/windows/hook.c
--- trunk/reactos/lib/user32/windows/hook.c	2005-05-14 17:57:31 UTC (rev 15280)
+++ trunk/reactos/lib/user32/windows/hook.c	2005-05-14 18:03:31 UTC (rev 15281)
@@ -281,6 +281,7 @@
   PHOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS CbtCreatewndExtra;
   WPARAM wParam;
   LPARAM lParam;
+  KBDLLHOOKSTRUCT *KeyboardLlData;
 
   Common = (PHOOKPROC_CALLBACK_ARGUMENTS) Arguments;
 
@@ -353,6 +354,10 @@
           break;
         }
       break;
+    case WH_KEYBOARD_LL:
+      KeyboardLlData = (KBDLLHOOKSTRUCT *)((PCHAR) Common + Common->lParam);
+      Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) KeyboardLlData);
+      break;
     default:
       return ZwCallbackReturn(NULL, 0, STATUS_NOT_SUPPORTED);
     }

Modified: trunk/reactos/subsys/win32k/include/msgqueue.h
--- trunk/reactos/subsys/win32k/include/msgqueue.h	2005-05-14 17:57:31 UTC (rev 15280)
+++ trunk/reactos/subsys/win32k/include/msgqueue.h	2005-05-14 18:03:31 UTC (rev 15281)
@@ -28,6 +28,7 @@
   ULONG_PTR CompletionCallbackContext;
   /* entry in the dispatching list of the sender's message queue */
   LIST_ENTRY DispatchingListEntry;
+  BOOL HookMessage;
 } USER_SENT_MESSAGE, *PUSER_SENT_MESSAGE;
 
 typedef struct _USER_SENT_MESSAGE_NOTIFY
@@ -127,7 +128,8 @@
 NTSTATUS FASTCALL
 MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
 	       HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
-               UINT uTimeout, BOOL Block, ULONG_PTR *uResult);
+               UINT uTimeout, BOOL Block, BOOL HookMessage,
+               ULONG_PTR *uResult);
 PUSER_MESSAGE FASTCALL
 MsqCreateMessage(LPMSG Msg, BOOLEAN FreeLParam);
 VOID FASTCALL

Modified: trunk/reactos/subsys/win32k/main/dllmain.c
--- trunk/reactos/subsys/win32k/main/dllmain.c	2005-05-14 17:57:31 UTC (rev 15280)
+++ trunk/reactos/subsys/win32k/main/dllmain.c	2005-05-14 18:03:31 UTC (rev 15281)
@@ -240,7 +240,44 @@
   return STATUS_SUCCESS;
 }
 
+/* Only used in ntuser/input.c KeyboardThreadMain(). If it's
+   not called there anymore, please delete */
+NTSTATUS
+Win32kInitWin32Thread(PETHREAD Thread)
+{
+  PEPROCESS Process;
 
+  Process = Thread->ThreadsProcess;
+
+  if (Process->Win32Process == NULL)
+    {
+      /* FIXME - lock the process */
+      Process->Win32Process = ExAllocatePool(NonPagedPool, sizeof(W32PROCESS));
+
+      if (Process->Win32Process == NULL)
+	return STATUS_NO_MEMORY;
+
+      RtlZeroMemory(Process->Win32Process, sizeof(W32PROCESS));
+      /* FIXME - unlock the process */
+
+      Win32kProcessCallback(Process, TRUE);
+    }
+
+  if (Thread->Tcb.Win32Thread == NULL)
+    {
+      Thread->Tcb.Win32Thread = ExAllocatePool (NonPagedPool, sizeof(W32THREAD));
+      if (Thread->Tcb.Win32Thread == NULL)
+	return STATUS_NO_MEMORY;
+
+      RtlZeroMemory(Thread->Tcb.Win32Thread, sizeof(W32THREAD));
+
+      Win32kThreadCallback(Thread, TRUE);
+    }
+
+  return(STATUS_SUCCESS);
+}
+
+
 /*
  * This definition doesn't work
  */

Modified: trunk/reactos/subsys/win32k/ntuser/callback.c
--- trunk/reactos/subsys/win32k/ntuser/callback.c	2005-05-14 17:57:31 UTC (rev 15280)
+++ trunk/reactos/subsys/win32k/ntuser/callback.c	2005-05-14 18:03:31 UTC (rev 15281)
@@ -293,6 +293,9 @@
           return 0;
         }
       break;
+    case WH_KEYBOARD_LL:
+      ArgumentLength += sizeof(KBDLLHOOKSTRUCT);
+      break;
     default:
       DPRINT1("Trying to call unsupported window hook %d\n", HookId);
       return 0;
@@ -343,6 +346,10 @@
           break;
         }
       break;
+    case WH_KEYBOARD_LL:
+      RtlCopyMemory(Extra, (PVOID) lParam, sizeof(KBDLLHOOKSTRUCT));
+      Common->lParam = (LPARAM) (Extra - (PCHAR) Common);
+      break;
     }
 
   ResultPointer = &Result;

Modified: trunk/reactos/subsys/win32k/ntuser/hook.c
--- trunk/reactos/subsys/win32k/ntuser/hook.c	2005-05-14 17:57:31 UTC (rev 15280)
+++ trunk/reactos/subsys/win32k/ntuser/hook.c	2005-05-14 18:03:31 UTC (rev 15281)
@@ -182,8 +182,10 @@
   RtlFreeUnicodeString(&Hook->ModuleName);
 
   /* Dereference thread if required */
-  if(Hook->Flags & HOOK_THREAD_REFERENCED)
-    ObDereferenceObject(Hook->Thread);
+  if (Hook->Flags & HOOK_THREAD_REFERENCED)
+    {
+      ObDereferenceObject(Hook->Thread);
+    }
 
   /* Close handle */
   ObmCloseHandle(WinStaObj->HandleTable, Hook->Self);
@@ -191,7 +193,7 @@
 
 /* remove a hook, freeing it if the chain is not in use */
 STATIC FASTCALL VOID
-IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj)
+IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj, BOOL TableAlreadyLocked)
 {
   PHOOKTABLE Table = IntGetTable(Hook);
 
@@ -201,7 +203,10 @@
       return;
     }
 
-  IntLockHookTable(Table);
+  if (! TableAlreadyLocked)
+    {
+      IntLockHookTable(Table);
+    }
   if (0 != Table->Counts[HOOKID_TO_INDEX(Hook->HookId)])
     {
       Hook->Proc = NULL; /* chain is in use, just mark it and return */
@@ -210,7 +215,10 @@
     {
       IntFreeHook(Table, Hook, WinStaObj);
     }
-  IntUnLockHookTable(Table);
+  if (! TableAlreadyLocked)
+    {
+      IntUnLockHookTable(Table);
+    }
 }
 
 /* release a hook chain, removing deleted hooks if the use count drops to 0 */
@@ -249,17 +257,42 @@
   IntUnLockHookTable(Table);
 }
 
+static LRESULT FASTCALL
+IntCallLowLevelHook(INT HookId, INT Code, WPARAM wParam, LPARAM lParam, PHOOK Hook)
+{
+  NTSTATUS Status;
+  ULONG_PTR uResult;
+
+  /* FIXME should get timeout from
+   * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
+  Status = MsqSendMessage(Hook->Thread->Tcb.Win32Thread->MessageQueue, (HWND) Code, HookId,
+                          wParam, lParam, /*500*/0, TRUE, TRUE, &uResult);
+
+  return NT_SUCCESS(Status) ? uResult : 0;
+}
+
 LRESULT FASTCALL
 HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam)
 {
   PHOOK Hook;
-  PHOOKTABLE Table = MsqGetHooks(PsGetWin32Thread()->MessageQueue);
+  PW32THREAD Win32Thread;
+  PHOOKTABLE Table;
   LRESULT Result;
   PWINSTATION_OBJECT WinStaObj;
   NTSTATUS Status;
 
   ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
 
+  Win32Thread = PsGetWin32Thread();
+  if (NULL == Win32Thread)
+    {
+      Table = NULL;
+    }
+  else
+    {
+      Table = MsqGetHooks(Win32Thread->MessageQueue);
+    }
+
   if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
     {
       /* try global table */
@@ -270,6 +303,13 @@
         }
     }
 
+  if (Hook->Thread != PsGetCurrentThread()
+      && (WH_KEYBOARD_LL == HookId || WH_MOUSE_LL == HookId))
+    {
+      DPRINT("Calling hook in owning thread\n");
+      return IntCallLowLevelHook(HookId, Code, wParam, lParam, Hook);
+    }
+
   if (Hook->Thread != PsGetCurrentThread())
     {
       DPRINT1("Calling hooks in other threads not implemented yet");
@@ -294,7 +334,7 @@
 				          0,
 				          &WinStaObj);
 
-  if(! NT_SUCCESS(Status))
+  if (! NT_SUCCESS(Status))
     {
       DPRINT1("Invalid window station????\n");
     }
@@ -324,7 +364,7 @@
                                               0,
                                               &WinStaObj);
 
-      if(! NT_SUCCESS(Status))
+      if (! NT_SUCCESS(Status))
         {
           DPRINT1("Invalid window station????\n");
           return;
@@ -344,7 +384,7 @@
                   Elem = Elem->Flink;
                   if (HookObj->Thread == Thread)
                     {
-                      IntRemoveHook(HookObj, WinStaObj);
+                      IntRemoveHook(HookObj, WinStaObj, TRUE);
                     }
                 }
               break;
@@ -372,7 +412,7 @@
 				          0,
 				          &WinStaObj);
 
-  if(! NT_SUCCESS(Status))
+  if (! NT_SUCCESS(Status))
     {
       SetLastNtError(Status);
       return FALSE;
@@ -433,7 +473,7 @@
   BOOL Ansi)
 {
   PWINSTATION_OBJECT WinStaObj;
-  BOOLEAN Global, ReleaseThread;
+  BOOLEAN Global;
   PETHREAD Thread;
   PHOOK Hook;
   UNICODE_STRING ModuleName;
@@ -473,15 +513,23 @@
           SetLastWin32Error(ERROR_INVALID_PARAMETER);
           return NULL;
         }
-      ReleaseThread = TRUE;
     }
   else  /* system-global hook */
     {
-      ReleaseThread = FALSE;
       if (HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL)
         {
           Mod = NULL;
           Thread = PsGetCurrentThread();
+          Status = ObReferenceObjectByPointer(Thread,
+				              THREAD_ALL_ACCESS,
+				              PsThreadType,
+				              KernelMode);
+
+          if (! NT_SUCCESS(Status))
+            {
+              SetLastNtError(Status);
+              return (HANDLE) NULL;
+            }
         }
       else if (NULL ==  Mod)
         {
@@ -495,16 +543,20 @@
       Global = TRUE;
     }
 
-  /* We only (partially) support local WH_CBT hooks for now */
-  if (WH_CBT != HookId || Global)
+  /* We only (partially) support local WH_CBT hooks and
+   * WH_KEYBOARD_LL/WH_MOUSE_LL hooks for now */
+  if ((WH_CBT != HookId || Global)
+      && WH_KEYBOARD_LL != HookId && WH_MOUSE_LL != HookId)
     {
 #if 0 /* Removed to get winEmbed working again */
       UNIMPLEMENTED
 #else
       DPRINT1("Not implemented: HookId %d Global %s\n", HookId, Global ? "TRUE" : "FALSE");
 #endif
-      if(ReleaseThread)
-        ObDereferenceObject(Thread);
+      if (NULL != Thread)
+        {
+          ObDereferenceObject(Thread);
+        }
       SetLastWin32Error(ERROR_NOT_SUPPORTED);
       return NULL;
     }
@@ -514,10 +566,12 @@
 				          0,
 				          &WinStaObj);
 
-  if(! NT_SUCCESS(Status))
+  if (! NT_SUCCESS(Status))
     {
-      if(ReleaseThread && Thread)
-        ObDereferenceObject(Thread);
+      if (NULL != Thread)
+        {
+          ObDereferenceObject(Thread);
+        }
       SetLastNtError(Status);
       return (HANDLE) NULL;
     }
@@ -525,14 +579,18 @@
   Hook = IntAddHook(Thread, HookId, Global, WinStaObj);
   if (NULL == Hook)
     {
-      if(ReleaseThread)
-        ObDereferenceObject(Thread);
+      if (NULL != Thread)
+        {
+          ObDereferenceObject(Thread);
+        }
       ObDereferenceObject(WinStaObj);
       return NULL;
     }
 
-  if(ReleaseThread)
+  if (NULL != Thread)
+    {
     Hook->Flags |= HOOK_THREAD_REFERENCED;
+    }
 
   if (NULL != Mod)
     {
@@ -540,9 +598,11 @@
       if (! NT_SUCCESS(Status))
         {
           ObmDereferenceObject(Hook);
-          IntRemoveHook(Hook, WinStaObj);
-          if(ReleaseThread)
-            ObDereferenceObject(Thread);
+          IntRemoveHook(Hook, WinStaObj, FALSE);
+          if (NULL != Thread)
+            {
+              ObDereferenceObject(Thread);
+            }
           ObDereferenceObject(WinStaObj);
           SetLastNtError(Status);
           return NULL;
@@ -553,9 +613,11 @@
       if (NULL == Hook->ModuleName.Buffer)
         {
           ObmDereferenceObject(Hook);
-          IntRemoveHook(Hook, WinStaObj);
-          if(ReleaseThread)
-            ObDereferenceObject(Thread);
+          IntRemoveHook(Hook, WinStaObj, FALSE);
+          if (NULL != Thread)
+            {
+              ObDereferenceObject(Thread);
+            }
           ObDereferenceObject(WinStaObj);
           SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
           return NULL;
@@ -567,9 +629,11 @@
       if (! NT_SUCCESS(Status))
         {
           ObmDereferenceObject(Hook);
-          IntRemoveHook(Hook, WinStaObj);
-          if(ReleaseThread)
-            ObDereferenceObject(Thread);
+          IntRemoveHook(Hook, WinStaObj, FALSE);
+          if (NULL != Thread)
+            {
+              ObDereferenceObject(Thread);
+            }
           ObDereferenceObject(WinStaObj);
           SetLastNtError(Status);
           return NULL;
@@ -618,7 +682,7 @@
 				          0,
 				          &WinStaObj);
 
-  if(! NT_SUCCESS(Status))
+  if (! NT_SUCCESS(Status))
     {
       SetLastNtError(Status);
       return FALSE;
@@ -635,7 +699,7 @@
     }
   ASSERT(Hook == HookObj->Self);
 
-  IntRemoveHook(HookObj, WinStaObj);
+  IntRemoveHook(HookObj, WinStaObj, FALSE);
 
   ObmDereferenceObject(HookObj);
   ObDereferenceObject(WinStaObj);

Modified: trunk/reactos/subsys/win32k/ntuser/input.c
--- trunk/reactos/subsys/win32k/ntuser/input.c	2005-05-14 17:57:31 UTC (rev 15280)
+++ trunk/reactos/subsys/win32k/ntuser/input.c	2005-05-14 18:03:31 UTC (rev 15281)
@@ -184,7 +184,7 @@
     DPRINT("Mouse Input Thread Starting...\n");
 
     /*
-     * Receive and process keyboard input.
+     * Receive and process mouse input.
      */
     while(InputThreadsRunning)
     {
@@ -409,7 +409,9 @@
   MSG msg;
   PUSER_MESSAGE_QUEUE FocusQueue;
   struct _ETHREAD *FocusThread;
+  extern NTSTATUS Win32kInitWin32Thread(PETHREAD Thread);
 
+
   PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans = NULL;
   UINT ModifierState = 0;
   USHORT LastMakeCode = 0;
@@ -434,6 +436,22 @@
       return; //(Status);
     }
 
+  /* Not sure if converting this thread to a win32 thread is such
+     a great idea. Since we're posting keyboard messages to the focus
+     window message queue, we'll be (indirectly) doing sendmessage
+     stuff from this thread (for WH_KEYBOARD_LL processing), which
+     means we need our own message queue. If keyboard messages were
+     instead queued to the system message queue, the thread removing
+     the message from the system message queue would be responsible
+     for WH_KEYBOARD_LL processing and we wouldn't need this thread
+     to be a win32 thread. */
+  Status = Win32kInitWin32Thread(PsGetCurrentThread());
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT1("Win32K: Failed making keyboard thread a win32 thread.\n");
+      return; //(Status);
+    }
+
   IntKeyboardGetIndicatorTrans(KeyboardDeviceHandle,
 		  	       &IndicatorTrans);
 

Modified: trunk/reactos/subsys/win32k/ntuser/message.c
--- trunk/reactos/subsys/win32k/ntuser/message.c	2005-05-14 17:57:31 UTC (rev 15280)
+++ trunk/reactos/subsys/win32k/ntuser/message.c	2005-05-14 18:03:31 UTC (rev 15281)
@@ -1332,7 +1332,7 @@
   }
 
   Status = MsqSendMessage(Window->MessageQueue, hWnd, Msg, wParam, lParam,
-                          uTimeout, (uFlags & SMTO_BLOCK), uResult);
+                          uTimeout, (uFlags & SMTO_BLOCK), FALSE, uResult);
   IntReleaseWindowObject(Window);
   if (STATUS_TIMEOUT == Status)
     {

Modified: trunk/reactos/subsys/win32k/ntuser/msgqueue.c
--- trunk/reactos/subsys/win32k/ntuser/msgqueue.c	2005-05-14 17:57:31 UTC (rev 15280)
+++ trunk/reactos/subsys/win32k/ntuser/msgqueue.c	2005-05-14 18:03:31 UTC (rev 15281)
@@ -618,6 +618,7 @@
   PUSER_MESSAGE_QUEUE FocusMessageQueue;
   MSG Msg;
   LARGE_INTEGER LargeTickCount;
+  KBDLLHOOKSTRUCT KbdHookData;
 
   DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
     uMsg, wParam, lParam);
@@ -632,6 +633,20 @@
   /* We can't get the Msg.pt point here since we don't know thread
      (and thus the window station) the message will end up in yet. */
 
+  KbdHookData.vkCode = Msg.wParam;
+  KbdHookData.scanCode = (Msg.lParam >> 16) & 0xff;
+  KbdHookData.flags = (0 == (Msg.lParam & 0x01000000) ? 0 : LLKHF_EXTENDED) |
+                      (0 == (Msg.lParam & 0x20000000) ? 0 : LLKHF_ALTDOWN) |
+                      (0 == (Msg.lParam & 0x80000000) ? 0 : LLKHF_UP);
+  KbdHookData.time = Msg.time;
+  KbdHookData.dwExtraInfo = 0;
+  if (HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
+    {
+      DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
+             Msg.message, Msg.wParam, Msg.lParam);
+      return;
+    }
+
   FocusMessageQueue = IntGetFocusMessageQueue();
   if( !IntGetScreenDC() ) {
     /* FIXME: What to do about Msg.pt here? */
@@ -793,11 +808,21 @@
 
   IntUnLockMessageQueue(MessageQueue);
 
-  /* Call the window procedure. */
-  Result = IntSendMessage(Message->Msg.hwnd,
-                          Message->Msg.message,
-                          Message->Msg.wParam,
-                          Message->Msg.lParam);
+  if (Message->HookMessage)
+    {
+      Result = HOOK_CallHooks(Message->Msg.message,
+                              (INT) Message->Msg.hwnd,
+                              Message->Msg.wParam,
+                              Message->Msg.lParam);
+    }
+  else
+    {
+      /* Call the window procedure. */
+      Result = IntSendMessage(Message->Msg.hwnd,
+                              Message->Msg.message,
+                              Message->Msg.wParam,
+                              Message->Msg.lParam);
+    }
 
   /* remove the message from the local dispatching list, because it doesn't need
      to be cleaned up on thread termination anymore */
@@ -957,7 +982,8 @@
 NTSTATUS FASTCALL
 MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
 	       HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
-               UINT uTimeout, BOOL Block, ULONG_PTR *uResult)
+               UINT uTimeout, BOOL Block, BOOL HookMessage,
+               ULONG_PTR *uResult)
 {
   PUSER_SENT_MESSAGE Message;
   KEVENT CompletionEvent;
@@ -992,6 +1018,7 @@
   Message->SenderQueue = ThreadQueue;
   IntReferenceMessageQueue(ThreadQueue);
   Message->CompletionCallback = NULL;
+  Message->HookMessage = HookMessage;
 
   IntReferenceMessageQueue(MessageQueue);
 

Modified: trunk/reactos/w32api/include/winuser.h
--- trunk/reactos/w32api/include/winuser.h	2005-05-14 17:57:31 UTC (rev 15280)
+++ trunk/reactos/w32api/include/winuser.h	2005-05-14 18:03:31 UTC (rev 15281)
@@ -2147,7 +2147,10 @@
 #define MOD_ON_KEYUP  2048
 #define MOD_RIGHT 16384
 #define MOD_LEFT 32768
+#define LLKHF_EXTENDED 0x00000001
+#define LLKHF_INJECTED 0x00000010
 #define LLKHF_ALTDOWN  0x00000020
+#define LLKHF_UP  0x00000080
 #if (WINVER >= 0x0500)
 #define FLASHW_STOP 0
 #define FLASHW_CAPTION 1