2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: HotKey support
5 * FILE: win32ss/user/ntuser/hotkey.c
10 * FIXME: Hotkey notifications are triggered by keyboard input (physical or programatically)
11 * and since only desktops on WinSta0 can receive input in seems very wrong to allow
12 * windows/threads on destops not belonging to WinSta0 to set hotkeys (receive notifications).
17 DBG_DEFAULT_CHANNEL(UserHotkey
);
19 /* GLOBALS *******************************************************************/
22 * Hardcoded hotkeys. See http://ivanlef0u.fr/repo/windoz/VI20051005.html
23 * or http://repo.meh.or.id/Windows/VI20051005.html .
25 * NOTE: The (Shift-)F12 keys are used only for the "UserDebuggerHotKey" setting
26 * which enables setting a key shortcut which, when pressed, establishes a
27 * breakpoint in the code being debugged:
28 * see http://technet.microsoft.com/en-us/library/cc786263(v=ws.10).aspx
29 * and http://flylib.com/books/en/4.441.1.33/1/ for more details.
30 * By default the key is VK-F12 on a 101-key keyboard, and is VK_SUBTRACT
31 * (hyphen / substract sign) on a 82-key keyboard.
33 /* pti pwnd modifiers vk id next */
34 // HOT_KEY hkF12 = {NULL, 1, 0, VK_F12, IDHK_F12, NULL};
35 // HOT_KEY hkShiftF12 = {NULL, 1, MOD_SHIFT, VK_F12, IDHK_SHIFTF12, &hkF12};
36 // HOT_KEY hkWinKey = {NULL, 1, MOD_WIN, 0, IDHK_WINKEY, &hkShiftF12};
38 PHOT_KEY gphkFirst
= NULL
;
39 UINT gfsModOnlyCandidate
;
41 /* FUNCTIONS *****************************************************************/
44 StartDebugHotKeys(VOID
)
47 UserUnregisterHotKey(PWND_BOTTOM
, IDHK_F12
);
48 UserUnregisterHotKey(PWND_BOTTOM
, IDHK_SHIFTF12
);
49 if (!ENHANCED_KEYBOARD(gKeyboardInfo
.KeyboardIdentifier
))
53 UserRegisterHotKey(PWND_BOTTOM
, IDHK_SHIFTF12
, MOD_SHIFT
, vk
);
54 UserRegisterHotKey(PWND_BOTTOM
, IDHK_F12
, 0, vk
);
55 TRACE("Start up the debugger hotkeys!! If you see this you eneabled debugprints. Congrats!\n");
61 * Returns a value that indicates if the key is a modifier key, and
66 IntGetModifiers(PBYTE pKeyState
)
70 if (IS_KEY_DOWN(pKeyState
, VK_SHIFT
))
71 fModifiers
|= MOD_SHIFT
;
73 if (IS_KEY_DOWN(pKeyState
, VK_CONTROL
))
74 fModifiers
|= MOD_CONTROL
;
76 if (IS_KEY_DOWN(pKeyState
, VK_MENU
))
77 fModifiers
|= MOD_ALT
;
79 if (IS_KEY_DOWN(pKeyState
, VK_LWIN
) || IS_KEY_DOWN(pKeyState
, VK_RWIN
))
80 fModifiers
|= MOD_WIN
;
86 * UnregisterWindowHotKeys
88 * Removes hotkeys registered by specified window on its cleanup
91 UnregisterWindowHotKeys(PWND pWnd
)
93 PHOT_KEY pHotKey
= gphkFirst
, phkNext
, *pLink
= &gphkFirst
;
97 /* Save next ptr for later use */
98 phkNext
= pHotKey
->pNext
;
100 /* Should we delete this hotkey? */
101 if (pHotKey
->pWnd
== pWnd
)
103 /* Update next ptr for previous hotkey and free memory */
105 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
107 else /* This hotkey will stay, use its next ptr */
108 pLink
= &pHotKey
->pNext
;
110 /* Move to the next entry */
116 * UnregisterThreadHotKeys
118 * Removes hotkeys registered by specified thread on its cleanup
121 UnregisterThreadHotKeys(PTHREADINFO pti
)
123 PHOT_KEY pHotKey
= gphkFirst
, phkNext
, *pLink
= &gphkFirst
;
127 /* Save next ptr for later use */
128 phkNext
= pHotKey
->pNext
;
130 /* Should we delete this hotkey? */
131 if (pHotKey
->pti
== pti
)
133 /* Update next ptr for previous hotkey and free memory */
135 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
137 else /* This hotkey will stay, use its next ptr */
138 pLink
= &pHotKey
->pNext
;
140 /* Move to the next entry */
148 * Checks if given key and modificators have corresponding hotkey
150 static PHOT_KEY FASTCALL
151 IsHotKey(UINT fsModifiers
, WORD wVk
)
153 PHOT_KEY pHotKey
= gphkFirst
;
157 if (pHotKey
->fsModifiers
== fsModifiers
&&
160 /* We have found it */
164 /* Move to the next entry */
165 pHotKey
= pHotKey
->pNext
;
172 * co_UserProcessHotKeys
174 * Sends WM_HOTKEY message if given keys are hotkey
177 co_UserProcessHotKeys(WORD wVk
, BOOL bIsDown
)
182 BOOL DoNotPostMsg
= FALSE
;
183 BOOL IsModifier
= FALSE
;
185 if (wVk
== VK_SHIFT
|| wVk
== VK_CONTROL
|| wVk
== VK_MENU
||
186 wVk
== VK_LWIN
|| wVk
== VK_RWIN
)
188 /* Remember that this was a modifier */
192 fModifiers
= IntGetModifiers(gafAsyncKeyState
);
198 /* Modifier key down -- no hotkey trigger, but remember this */
199 gfsModOnlyCandidate
= fModifiers
;
204 /* Regular key down -- check for hotkey, and reset mod candidates */
205 pHotKey
= IsHotKey(fModifiers
, wVk
);
206 gfsModOnlyCandidate
= 0;
213 /* Modifier key up -- modifier-only keys are triggered here */
214 pHotKey
= IsHotKey(gfsModOnlyCandidate
, 0);
215 gfsModOnlyCandidate
= 0;
219 /* Regular key up -- no hotkey, but reset mod-only candidates */
220 gfsModOnlyCandidate
= 0;
227 TRACE("Hot key pressed (pWnd %p, id %d)\n", pHotKey
->pWnd
, pHotKey
->id
);
229 /* FIXME: See comment about "UserDebuggerHotKey" on top of this file. */
230 if (pHotKey
->id
== IDHK_SHIFTF12
|| pHotKey
->id
== IDHK_F12
)
234 ERR("Hot key pressed for Debug Activation! ShiftF12 = %d or F12 = %d\n",pHotKey
->id
== IDHK_SHIFTF12
, pHotKey
->id
== IDHK_F12
);
235 //DoNotPostMsg = co_ActivateDebugger(); // FIXME
240 /* WIN and F12 keys are not hardcoded here. See comments on top of this file. */
241 if (pHotKey
->id
== IDHK_WINKEY
)
244 pWnd
= ValidateHwndNoErr(InputWindowStation
->ShellWindow
);
247 TRACE("System Hot key Id %d Key %u\n", pHotKey
->id
, wVk
);
248 UserPostMessage(UserHMGetHandle(pWnd
), WM_SYSCOMMAND
, SC_TASKLIST
, 0);
249 co_IntShellHookNotify(HSHELL_TASKMAN
, 0, 0);
254 if (pHotKey
->id
== IDHK_SNAP_LEFT
||
255 pHotKey
->id
== IDHK_SNAP_RIGHT
||
256 pHotKey
->id
== IDHK_SNAP_UP
||
257 pHotKey
->id
== IDHK_SNAP_DOWN
)
259 HWND topWnd
= UserGetForegroundWindow();
262 UserPostMessage(topWnd
, WM_KEYDOWN
, wVk
, 0);
269 TRACE("UPTM Hot key Id %d Key %u\n", pHotKey
->id
, wVk
);
270 UserPostThreadMessage(pHotKey
->pti
, WM_HOTKEY
, pHotKey
->id
, MAKELONG(fModifiers
, wVk
));
271 //ptiLastInput = pHotKey->pti;
272 return TRUE
; /* Don't send any message */
276 pWnd
= pHotKey
->pWnd
;
277 if (pWnd
== PWND_BOTTOM
)
279 if (gpqForeground
== NULL
)
282 pWnd
= gpqForeground
->spwndFocus
;
287 // pWnd->head.rpdesk->pDeskInfo->spwndShell needs testing.
288 if (pWnd
== ValidateHwndNoErr(InputWindowStation
->ShellWindow
) && pHotKey
->id
== SC_TASKLIST
)
290 UserPostMessage(UserHMGetHandle(pWnd
), WM_SYSCOMMAND
, SC_TASKLIST
, 0);
291 co_IntShellHookNotify(HSHELL_TASKMAN
, 0, 0);
295 TRACE("UPM Hot key Id %d Key %u\n", pHotKey
->id
, wVk
);
296 UserPostMessage(UserHMGetHandle(pWnd
), WM_HOTKEY
, pHotKey
->id
, MAKELONG(fModifiers
, wVk
));
298 //ptiLastInput = pWnd->head.pti;
299 return TRUE
; /* Don't send any message */
310 * GetHotKey message support
313 DefWndGetHotKey(PWND pWnd
)
315 PHOT_KEY pHotKey
= gphkFirst
;
317 WARN("DefWndGetHotKey\n");
321 if (pHotKey
->pWnd
== pWnd
&& pHotKey
->id
== IDHK_REACTOS
)
323 /* We have found it */
324 return MAKELONG(pHotKey
->vk
, pHotKey
->fsModifiers
);
327 /* Move to the next entry */
328 pHotKey
= pHotKey
->pNext
;
337 * SetHotKey message support
340 DefWndSetHotKey(PWND pWnd
, WPARAM wParam
)
342 UINT fsModifiers
, vk
;
343 PHOT_KEY pHotKey
, *pLink
;
346 WARN("DefWndSetHotKey wParam 0x%x\n", wParam
);
348 // A hot key cannot be associated with a child window.
349 if (pWnd
->style
& WS_CHILD
)
352 // VK_ESCAPE, VK_SPACE, and VK_TAB are invalid hot keys.
353 if (LOWORD(wParam
) == VK_ESCAPE
||
354 LOWORD(wParam
) == VK_SPACE
||
355 LOWORD(wParam
) == VK_TAB
)
361 fsModifiers
= HIWORD(wParam
);
368 if (pHotKey
->fsModifiers
== fsModifiers
&&
370 pHotKey
->id
== IDHK_REACTOS
)
372 if (pHotKey
->pWnd
!= pWnd
)
373 iRet
= 2; // Another window already has the same hot key.
377 /* Move to the next entry */
378 pHotKey
= pHotKey
->pNext
;
386 if (pHotKey
->pWnd
== pWnd
&&
387 pHotKey
->id
== IDHK_REACTOS
)
389 /* This window has already hotkey registered */
393 /* Move to the next entry */
394 pLink
= &pHotKey
->pNext
;
395 pHotKey
= pHotKey
->pNext
;
402 /* Create new hotkey */
403 pHotKey
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOT_KEY
), USERTAG_HOTKEY
);
407 pHotKey
->pWnd
= pWnd
;
408 pHotKey
->id
= IDHK_REACTOS
; // Don't care, these hot keys are unrelated to the hot keys set by RegisterHotKey
409 pHotKey
->pNext
= gphkFirst
;
413 /* A window can only have one hot key. If the window already has a
414 hot key associated with it, the new hot key replaces the old one. */
416 pHotKey
->fsModifiers
= fsModifiers
;
422 *pLink
= pHotKey
->pNext
;
423 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
431 UserRegisterHotKey(PWND pWnd
,
437 PTHREADINFO pHotKeyThread
;
439 /* Find hotkey thread */
440 if (pWnd
== NULL
|| pWnd
== PWND_BOTTOM
)
442 pHotKeyThread
= PsGetCurrentThreadWin32Thread();
446 pHotKeyThread
= pWnd
->head
.pti
;
449 /* Check for existing hotkey */
450 if (IsHotKey(fsModifiers
, vk
))
452 EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED
);
453 WARN("Hotkey already exists\n");
457 /* Create new hotkey */
458 pHotKey
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOT_KEY
), USERTAG_HOTKEY
);
461 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
465 pHotKey
->pti
= pHotKeyThread
;
466 pHotKey
->pWnd
= pWnd
;
467 pHotKey
->fsModifiers
= fsModifiers
;
471 /* Insert hotkey to the global list */
472 pHotKey
->pNext
= gphkFirst
;
479 UserUnregisterHotKey(PWND pWnd
, int id
)
481 PHOT_KEY pHotKey
= gphkFirst
, phkNext
, *pLink
= &gphkFirst
;
486 /* Save next ptr for later use */
487 phkNext
= pHotKey
->pNext
;
489 /* Should we delete this hotkey? */
490 if (pHotKey
->pWnd
== pWnd
&& pHotKey
->id
== id
)
492 /* Update next ptr for previous hotkey and free memory */
494 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
498 else /* This hotkey will stay, use its next ptr */
499 pLink
= &pHotKey
->pNext
;
501 /* Move to the next entry */
508 /* SYSCALLS *****************************************************************/
512 NtUserRegisterHotKey(HWND hWnd
,
519 PTHREADINFO pHotKeyThread
;
522 TRACE("Enter NtUserRegisterHotKey\n");
524 if (fsModifiers
& ~(MOD_ALT
|MOD_CONTROL
|MOD_SHIFT
|MOD_WIN
)) // FIXME: Does Win2k3 support MOD_NOREPEAT?
526 WARN("Invalid modifiers: %x\n", fsModifiers
);
527 EngSetLastError(ERROR_INVALID_FLAGS
);
531 UserEnterExclusive();
533 /* Find hotkey thread */
536 pHotKeyThread
= gptiCurrent
;
540 pWnd
= UserGetWindowObject(hWnd
);
544 pHotKeyThread
= pWnd
->head
.pti
;
546 /* Fix wine msg "Window on another thread" test_hotkey */
547 if (pWnd
->head
.pti
!= gptiCurrent
)
549 EngSetLastError(ERROR_WINDOW_OF_OTHER_THREAD
);
550 WARN("Must be from the same Thread.\n");
555 /* Check for existing hotkey */
556 if (IsHotKey(fsModifiers
, vk
))
558 EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED
);
559 WARN("Hotkey already exists\n");
563 /* Create new hotkey */
564 pHotKey
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOT_KEY
), USERTAG_HOTKEY
);
567 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
571 pHotKey
->pti
= pHotKeyThread
;
572 pHotKey
->pWnd
= pWnd
;
573 pHotKey
->fsModifiers
= fsModifiers
;
577 /* Insert hotkey to the global list */
578 pHotKey
->pNext
= gphkFirst
;
584 TRACE("Leave NtUserRegisterHotKey, ret=%i\n", bRet
);
591 NtUserUnregisterHotKey(HWND hWnd
, int id
)
593 PHOT_KEY pHotKey
= gphkFirst
, phkNext
, *pLink
= &gphkFirst
;
597 TRACE("Enter NtUserUnregisterHotKey\n");
598 UserEnterExclusive();
600 /* Fail if given window is invalid */
601 if (hWnd
&& !(pWnd
= UserGetWindowObject(hWnd
)))
606 /* Save next ptr for later use */
607 phkNext
= pHotKey
->pNext
;
609 /* Should we delete this hotkey? */
610 if (pHotKey
->pWnd
== pWnd
&& pHotKey
->id
== id
)
612 /* Update next ptr for previous hotkey and free memory */
614 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
618 else /* This hotkey will stay, use its next ptr */
619 pLink
= &pHotKey
->pNext
;
621 /* Move to the next entry */
626 TRACE("Leave NtUserUnregisterHotKey, ret=%i\n", bRet
);