[WIN32SS:NTUSER] Implement "Window Snap" feature (#1226)
[reactos.git] / win32ss / user / ntuser / hotkey.c
index c4d3f2a..02b504f 100644 (file)
@@ -2,14 +2,14 @@
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS Win32k subsystem
  * PURPOSE:          HotKey support
- * FILE:             subsystems/win32/win32k/ntuser/hotkey.c
+ * FILE:             win32ss/user/ntuser/hotkey.c
  * PROGRAMER:        Eric Kohl
  */
 
 /*
  * FIXME: Hotkey notifications are triggered by keyboard input (physical or programatically)
- * and since only desktops on WinSta0 can recieve input in seems very wrong to allow
- * windows/threads on destops not belonging to WinSta0 to set hotkeys (recieve notifications).
+ * and since only desktops on WinSta0 can receive input in seems very wrong to allow
+ * windows/threads on destops not belonging to WinSta0 to set hotkeys (receive notifications).
  *     -- Gunnar
  */
 
@@ -18,17 +18,43 @@ DBG_DEFAULT_CHANNEL(UserHotkey);
 
 /* GLOBALS *******************************************************************/
 
-/* Hardcoded hotkeys. See http://ivanlef0u.fr/repo/windoz/VI20051005.html */
-/*                   thread hwnd  modifiers  vk      id  next */
-HOT_KEY hkF12 =      {NULL, NULL, 0,         VK_F12, IDHK_F12,      NULL};
-HOT_KEY hkShiftF12 = {NULL, NULL, MOD_SHIFT, VK_F12, IDHK_SHIFTF12, &hkF12};
-HOT_KEY hkWinKey =   {NULL, NULL, MOD_WIN,   0,      IDHK_WINKEY,   &hkShiftF12};
+/*
+ * Hardcoded hotkeys. See http://ivanlef0u.fr/repo/windoz/VI20051005.html
+ * or http://repo.meh.or.id/Windows/VI20051005.html .
+ *
+ * NOTE: The (Shift-)F12 keys are used only for the "UserDebuggerHotKey" setting
+ * which enables setting a key shortcut which, when pressed, establishes a
+ * breakpoint in the code being debugged:
+ * see http://technet.microsoft.com/en-us/library/cc786263(v=ws.10).aspx
+ * and http://flylib.com/books/en/4.441.1.33/1/ for more details.
+ * By default the key is VK-F12 on a 101-key keyboard, and is VK_SUBTRACT
+ * (hyphen / substract sign) on a 82-key keyboard.
+ */
+/*                       pti   pwnd  modifiers  vk      id  next */
+// HOT_KEY hkF12 =      {NULL, 1,    0,         VK_F12, IDHK_F12,      NULL};
+// HOT_KEY hkShiftF12 = {NULL, 1,    MOD_SHIFT, VK_F12, IDHK_SHIFTF12, &hkF12};
+// HOT_KEY hkWinKey =   {NULL, 1,    MOD_WIN,   0,      IDHK_WINKEY,   &hkShiftF12};
 
-PHOT_KEY gphkFirst = &hkWinKey;
-BOOL bWinHotkeyActive = FALSE;
+PHOT_KEY gphkFirst = NULL;
+UINT gfsModOnlyCandidate;
 
 /* FUNCTIONS *****************************************************************/
 
+VOID FASTCALL
+StartDebugHotKeys(VOID)
+{
+    UINT vk = VK_F12;
+    UserUnregisterHotKey(PWND_BOTTOM, IDHK_F12);
+    UserUnregisterHotKey(PWND_BOTTOM, IDHK_SHIFTF12);
+    if (!ENHANCED_KEYBOARD(gKeyboardInfo.KeyboardIdentifier))
+    {
+        vk = VK_SUBTRACT;
+    }
+    UserRegisterHotKey(PWND_BOTTOM, IDHK_SHIFTF12, MOD_SHIFT, vk);
+    UserRegisterHotKey(PWND_BOTTOM, IDHK_F12, 0, vk);
+    TRACE("Start up the debugger hotkeys!! If you see this you eneabled debugprints. Congrats!\n");
+}
+
 /*
  * IntGetModifiers
  *
@@ -65,7 +91,6 @@ VOID FASTCALL
 UnregisterWindowHotKeys(PWND pWnd)
 {
     PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst;
-    HWND hWnd = pWnd->head.h;
 
     while (pHotKey)
     {
@@ -73,7 +98,7 @@ UnregisterWindowHotKeys(PWND pWnd)
         phkNext = pHotKey->pNext;
 
         /* Should we delete this hotkey? */
-        if (pHotKey->hWnd == hWnd)
+        if (pHotKey->pWnd == pWnd)
         {
             /* Update next ptr for previous hotkey and free memory */
             *pLink = phkNext;
@@ -93,7 +118,7 @@ UnregisterWindowHotKeys(PWND pWnd)
  * Removes hotkeys registered by specified thread on its cleanup
  */
 VOID FASTCALL
-UnregisterThreadHotKeys(struct _ETHREAD *pThread)
+UnregisterThreadHotKeys(PTHREADINFO pti)
 {
     PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst;
 
@@ -103,7 +128,7 @@ UnregisterThreadHotKeys(struct _ETHREAD *pThread)
         phkNext = pHotKey->pNext;
 
         /* Should we delete this hotkey? */
-        if (pHotKey->pThread == pThread)
+        if (pHotKey->pti == pti)
         {
             /* Update next ptr for previous hotkey and free memory */
             *pLink = phkNext;
@@ -153,65 +178,128 @@ co_UserProcessHotKeys(WORD wVk, BOOL bIsDown)
 {
     UINT fModifiers;
     PHOT_KEY pHotKey;
+    PWND pWnd;
+    BOOL DoNotPostMsg = FALSE;
+    BOOL IsModifier = FALSE;
 
     if (wVk == VK_SHIFT || wVk == VK_CONTROL || wVk == VK_MENU ||
         wVk == VK_LWIN || wVk == VK_RWIN)
     {
-        /* Those keys are specified by modifiers */
-        wVk = 0;
+        /* Remember that this was a modifier */
+        IsModifier = TRUE;
     }
 
-    /* Check if it is a hotkey */
     fModifiers = IntGetModifiers(gafAsyncKeyState);
-    pHotKey = IsHotKey(fModifiers, wVk);
-    if (pHotKey)
+
+    if (bIsDown)
+    {
+        if (IsModifier)
+        {
+            /* Modifier key down -- no hotkey trigger, but remember this */
+            gfsModOnlyCandidate = fModifiers;
+            return FALSE;
+        }
+        else
+        {
+            /* Regular key down -- check for hotkey, and reset mod candidates */
+            pHotKey = IsHotKey(fModifiers, wVk);
+            gfsModOnlyCandidate = 0;
+        }
+    }
+    else
     {
-        /* Process hotkey if it is key up event */
-        if (!bIsDown)
+        if (IsModifier)
+        {
+            /* Modifier key up -- modifier-only keys are triggered here */
+            pHotKey = IsHotKey(gfsModOnlyCandidate, 0);
+            gfsModOnlyCandidate = 0;
+        }
+        else
         {
-            TRACE("Hot key pressed (hWnd %p, id %d)\n", pHotKey->hWnd, pHotKey->id);
+            /* Regular key up -- no hotkey, but reset mod-only candidates */
+            gfsModOnlyCandidate = 0;
+            return FALSE;
+        }
+    }
 
-            /* WIN and F12 keys are hardcoded here. See: http://ivanlef0u.fr/repo/windoz/VI20051005.html */
-            if (pHotKey == &hkWinKey)
-            {
-                if(bWinHotkeyActive == TRUE)
-                {
-                    UserPostMessage(InputWindowStation->ShellWindow, WM_SYSCOMMAND, SC_TASKLIST, 0);
-                    bWinHotkeyActive = FALSE;
-                }
-            }
-            else if (pHotKey == &hkF12 || pHotKey == &hkShiftF12)
+    if (pHotKey)
+    {
+        TRACE("Hot key pressed (pWnd %p, id %d)\n", pHotKey->pWnd, pHotKey->id);
+
+        /* FIXME: See comment about "UserDebuggerHotKey" on top of this file. */
+        if (pHotKey->id == IDHK_SHIFTF12 || pHotKey->id == IDHK_F12)
+        {
+            if (bIsDown)
             {
-                //co_ActivateDebugger(); // FIXME
+                ERR("Hot key pressed for Debug Activation! ShiftF12 = %d or F12 = %d\n",pHotKey->id == IDHK_SHIFTF12 , pHotKey->id == IDHK_F12);
+                //DoNotPostMsg = co_ActivateDebugger(); // FIXME
             }
-            else if (pHotKey->id == IDHK_REACTOS && !pHotKey->pThread) // FIXME: Those hotkeys doesn't depend on RegisterHotKey
+            return DoNotPostMsg;
+        }
+
+        /* WIN and F12 keys are not hardcoded here. See comments on top of this file. */
+        if (pHotKey->id == IDHK_WINKEY)
+        {
+            ASSERT(!bIsDown);
+            pWnd = ValidateHwndNoErr(InputWindowStation->ShellWindow);
+            if (pWnd)
             {
-                UserPostMessage(pHotKey->hWnd, WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)pHotKey->hWnd);
+               TRACE("System Hot key Id %d Key %u\n", pHotKey->id, wVk );
+               UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_TASKLIST, 0);
+               co_IntShellHookNotify(HSHELL_TASKMAN, 0, 0);
+               return FALSE;
             }
-            else
+        }
+        
+        if (pHotKey->id == IDHK_SNAP_LEFT ||
+            pHotKey->id == IDHK_SNAP_RIGHT ||
+            pHotKey->id == IDHK_SNAP_UP ||
+            pHotKey->id == IDHK_SNAP_DOWN)
+        {
+            HWND topWnd = UserGetForegroundWindow();
+            if (topWnd)
             {
-                /* If a hotkey with the WIN modifier was activated, do not treat the release of the WIN key as a hotkey*/
-                if((pHotKey->fsModifiers & MOD_WIN) != 0)
-                    bWinHotkeyActive = FALSE;
-
-                MsqPostHotKeyMessage(pHotKey->pThread,
-                                     pHotKey->hWnd,
-                                     (WPARAM)pHotKey->id,
-                                     MAKELPARAM((WORD)fModifiers, wVk));
+                UserPostMessage(topWnd, WM_KEYDOWN, wVk, 0);
             }
+            return TRUE;
+        }
+
+        if (!pHotKey->pWnd)
+        {
+            TRACE("UPTM Hot key Id %d Key %u\n", pHotKey->id, wVk );
+            UserPostThreadMessage(pHotKey->pti, WM_HOTKEY, pHotKey->id, MAKELONG(fModifiers, wVk));
+            //ptiLastInput = pHotKey->pti;
+            return TRUE; /* Don't send any message */
         }
         else
         {
-            if (pHotKey == &hkWinKey)
+            pWnd = pHotKey->pWnd;
+            if (pWnd == PWND_BOTTOM)
             {
-               /* The user pressed the win key */
-                bWinHotkeyActive = TRUE;
+                if (gpqForeground == NULL)
+                    return FALSE;
+
+                pWnd = gpqForeground->spwndFocus;
             }
-        }
 
-        return TRUE; /* Don't send any message */
+            if (pWnd)
+            {
+                //  pWnd->head.rpdesk->pDeskInfo->spwndShell needs testing.
+                if (pWnd == ValidateHwndNoErr(InputWindowStation->ShellWindow) && pHotKey->id == SC_TASKLIST)
+                {
+                    UserPostMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_TASKLIST, 0);
+                    co_IntShellHookNotify(HSHELL_TASKMAN, 0, 0);
+                }
+                else
+                {
+                    TRACE("UPM Hot key Id %d Key %u\n", pHotKey->id, wVk );
+                    UserPostMessage(UserHMGetHandle(pWnd), WM_HOTKEY, pHotKey->id, MAKELONG(fModifiers, wVk));
+                }
+                //ptiLastInput = pWnd->head.pti;
+                return TRUE; /* Don't send any message */
+            }
+        }
     }
-
     return FALSE;
 }
 
@@ -222,7 +310,7 @@ co_UserProcessHotKeys(WORD wVk, BOOL bIsDown)
  * GetHotKey message support
  */
 UINT FASTCALL
-DefWndGetHotKey(HWND hWnd)
+DefWndGetHotKey(PWND pWnd)
 {
     PHOT_KEY pHotKey = gphkFirst;
 
@@ -230,7 +318,7 @@ DefWndGetHotKey(HWND hWnd)
 
     while (pHotKey)
     {
-        if (pHotKey->hWnd == hWnd && pHotKey->id == IDHK_REACTOS)
+        if (pHotKey->pWnd == pWnd && pHotKey->id == IDHK_REACTOS)
         {
             /* We have found it */
             return MAKELONG(pHotKey->vk, pHotKey->fsModifiers);
@@ -253,7 +341,6 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam)
 {
     UINT fsModifiers, vk;
     PHOT_KEY pHotKey, *pLink;
-    HWND hWnd;
     INT iRet = 1;
 
     WARN("DefWndSetHotKey wParam 0x%x\n", wParam);
@@ -272,7 +359,6 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam)
 
     vk = LOWORD(wParam);
     fsModifiers = HIWORD(wParam);
-    hWnd = UserHMGetHandle(pWnd);
 
     if (wParam)
     {
@@ -283,7 +369,7 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam)
                 pHotKey->vk == vk &&
                 pHotKey->id == IDHK_REACTOS)
             {
-                if (pHotKey->hWnd != hWnd)
+                if (pHotKey->pWnd != pWnd)
                     iRet = 2; // Another window already has the same hot key.
                 break;
             }
@@ -292,12 +378,12 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam)
             pHotKey = pHotKey->pNext;
         }
     }
-    
+
     pHotKey = gphkFirst;
     pLink = &gphkFirst;
     while (pHotKey)
     {
-        if (pHotKey->hWnd == hWnd &&
+        if (pHotKey->pWnd == pWnd &&
             pHotKey->id == IDHK_REACTOS)
         {
             /* This window has already hotkey registered */
@@ -318,7 +404,7 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam)
             if (pHotKey == NULL)
                 return 0;
 
-            pHotKey->hWnd = hWnd;
+            pHotKey->pWnd = pWnd;
             pHotKey->id = IDHK_REACTOS; // Don't care, these hot keys are unrelated to the hot keys set by RegisterHotKey
             pHotKey->pNext = gphkFirst;
             gphkFirst = pHotKey;
@@ -326,7 +412,7 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam)
 
         /* A window can only have one hot key. If the window already has a
            hot key associated with it, the new hot key replaces the old one. */
-        pHotKey->pThread = NULL;
+        pHotKey->pti = NULL;
         pHotKey->fsModifiers = fsModifiers;
         pHotKey->vk = vk;
     }
@@ -340,6 +426,85 @@ DefWndSetHotKey(PWND pWnd, WPARAM wParam)
     return iRet;
 }
 
+
+BOOL FASTCALL
+UserRegisterHotKey(PWND pWnd,
+                   int id,
+                   UINT fsModifiers,
+                   UINT vk)
+{
+    PHOT_KEY pHotKey;
+    PTHREADINFO pHotKeyThread;
+
+    /* Find hotkey thread */
+    if (pWnd == NULL || pWnd == PWND_BOTTOM)
+    {
+        pHotKeyThread = PsGetCurrentThreadWin32Thread();
+    }
+    else
+    {
+        pHotKeyThread = pWnd->head.pti;
+    }
+
+    /* Check for existing hotkey */
+    if (IsHotKey(fsModifiers, vk))
+    {
+        EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED);
+        WARN("Hotkey already exists\n");
+        return FALSE;
+    }
+
+    /* Create new hotkey */
+    pHotKey = ExAllocatePoolWithTag(PagedPool, sizeof(HOT_KEY), USERTAG_HOTKEY);
+    if (pHotKey == NULL)
+    {
+        EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return FALSE;
+    }
+
+    pHotKey->pti = pHotKeyThread;
+    pHotKey->pWnd = pWnd;
+    pHotKey->fsModifiers = fsModifiers;
+    pHotKey->vk = vk;
+    pHotKey->id = id;
+
+    /* Insert hotkey to the global list */
+    pHotKey->pNext = gphkFirst;
+    gphkFirst = pHotKey;
+
+    return TRUE;
+}
+
+BOOL FASTCALL
+UserUnregisterHotKey(PWND pWnd, int id)
+{
+    PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst;
+    BOOL bRet = FALSE;
+
+    while (pHotKey)
+    {
+        /* Save next ptr for later use */
+        phkNext = pHotKey->pNext;
+
+        /* Should we delete this hotkey? */
+        if (pHotKey->pWnd == pWnd && pHotKey->id == id)
+        {
+            /* Update next ptr for previous hotkey and free memory */
+            *pLink = phkNext;
+            ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY);
+
+            bRet = TRUE;
+        }
+        else /* This hotkey will stay, use its next ptr */
+            pLink = &pHotKey->pNext;
+
+        /* Move to the next entry */
+        pHotKey = phkNext;
+    }
+    return bRet;
+}
+
+
 /* SYSCALLS *****************************************************************/
 
 
@@ -350,8 +515,8 @@ NtUserRegisterHotKey(HWND hWnd,
                      UINT vk)
 {
     PHOT_KEY pHotKey;
-    PWND pWnd;
-    PETHREAD pHotKeyThread;
+    PWND pWnd = NULL;
+    PTHREADINFO pHotKeyThread;
     BOOL bRet = FALSE;
 
     TRACE("Enter NtUserRegisterHotKey\n");
@@ -368,7 +533,7 @@ NtUserRegisterHotKey(HWND hWnd,
     /* Find hotkey thread */
     if (hWnd == NULL)
     {
-        pHotKeyThread = PsGetCurrentThread();
+        pHotKeyThread = gptiCurrent;
     }
     else
     {
@@ -376,7 +541,15 @@ NtUserRegisterHotKey(HWND hWnd,
         if (!pWnd)
             goto cleanup;
 
-        pHotKeyThread = pWnd->head.pti->pEThread;
+        pHotKeyThread = pWnd->head.pti;
+
+        /* Fix wine msg "Window on another thread" test_hotkey */
+        if (pWnd->head.pti != gptiCurrent)
+        {
+           EngSetLastError(ERROR_WINDOW_OF_OTHER_THREAD);
+           WARN("Must be from the same Thread.\n");
+           goto cleanup;
+        }
     }
 
     /* Check for existing hotkey */
@@ -395,8 +568,8 @@ NtUserRegisterHotKey(HWND hWnd,
         goto cleanup;
     }
 
-    pHotKey->pThread = pHotKeyThread;
-    pHotKey->hWnd = hWnd;
+    pHotKey->pti = pHotKeyThread;
+    pHotKey->pWnd = pWnd;
     pHotKey->fsModifiers = fsModifiers;
     pHotKey->vk = vk;
     pHotKey->id = id;
@@ -419,12 +592,13 @@ NtUserUnregisterHotKey(HWND hWnd, int id)
 {
     PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst;
     BOOL bRet = FALSE;
+    PWND pWnd = NULL;
 
     TRACE("Enter NtUserUnregisterHotKey\n");
     UserEnterExclusive();
 
     /* Fail if given window is invalid */
-    if (hWnd && !UserGetWindowObject(hWnd))
+    if (hWnd && !(pWnd = UserGetWindowObject(hWnd)))
         goto cleanup;
 
     while (pHotKey)
@@ -433,7 +607,7 @@ NtUserUnregisterHotKey(HWND hWnd, int id)
         phkNext = pHotKey->pNext;
 
         /* Should we delete this hotkey? */
-        if (pHotKey->hWnd == hWnd && pHotKey->id == id)
+        if (pHotKey->pWnd == pWnd && pHotKey->id == id)
         {
             /* Update next ptr for previous hotkey and free memory */
             *pLink = phkNext;