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);
256 TRACE("UPTM Hot key Id %d Key %u\n", pHotKey
->id
, wVk
);
257 UserPostThreadMessage(pHotKey
->pti
, WM_HOTKEY
, pHotKey
->id
, MAKELONG(fModifiers
, wVk
));
258 //ptiLastInput = pHotKey->pti;
259 return TRUE
; /* Don't send any message */
263 pWnd
= pHotKey
->pWnd
;
264 if (pWnd
== PWND_BOTTOM
)
266 if (gpqForeground
== NULL
)
269 pWnd
= gpqForeground
->spwndFocus
;
274 // pWnd->head.rpdesk->pDeskInfo->spwndShell needs testing.
275 if (pWnd
== ValidateHwndNoErr(InputWindowStation
->ShellWindow
) && pHotKey
->id
== SC_TASKLIST
)
277 UserPostMessage(UserHMGetHandle(pWnd
), WM_SYSCOMMAND
, SC_TASKLIST
, 0);
278 co_IntShellHookNotify(HSHELL_TASKMAN
, 0, 0);
282 TRACE("UPM Hot key Id %d Key %u\n", pHotKey
->id
, wVk
);
283 UserPostMessage(UserHMGetHandle(pWnd
), WM_HOTKEY
, pHotKey
->id
, MAKELONG(fModifiers
, wVk
));
285 //ptiLastInput = pWnd->head.pti;
286 return TRUE
; /* Don't send any message */
297 * GetHotKey message support
300 DefWndGetHotKey(PWND pWnd
)
302 PHOT_KEY pHotKey
= gphkFirst
;
304 WARN("DefWndGetHotKey\n");
308 if (pHotKey
->pWnd
== pWnd
&& pHotKey
->id
== IDHK_REACTOS
)
310 /* We have found it */
311 return MAKELONG(pHotKey
->vk
, pHotKey
->fsModifiers
);
314 /* Move to the next entry */
315 pHotKey
= pHotKey
->pNext
;
324 * SetHotKey message support
327 DefWndSetHotKey(PWND pWnd
, WPARAM wParam
)
329 UINT fsModifiers
, vk
;
330 PHOT_KEY pHotKey
, *pLink
;
333 WARN("DefWndSetHotKey wParam 0x%x\n", wParam
);
335 // A hot key cannot be associated with a child window.
336 if (pWnd
->style
& WS_CHILD
)
339 // VK_ESCAPE, VK_SPACE, and VK_TAB are invalid hot keys.
340 if (LOWORD(wParam
) == VK_ESCAPE
||
341 LOWORD(wParam
) == VK_SPACE
||
342 LOWORD(wParam
) == VK_TAB
)
348 fsModifiers
= HIWORD(wParam
);
355 if (pHotKey
->fsModifiers
== fsModifiers
&&
357 pHotKey
->id
== IDHK_REACTOS
)
359 if (pHotKey
->pWnd
!= pWnd
)
360 iRet
= 2; // Another window already has the same hot key.
364 /* Move to the next entry */
365 pHotKey
= pHotKey
->pNext
;
373 if (pHotKey
->pWnd
== pWnd
&&
374 pHotKey
->id
== IDHK_REACTOS
)
376 /* This window has already hotkey registered */
380 /* Move to the next entry */
381 pLink
= &pHotKey
->pNext
;
382 pHotKey
= pHotKey
->pNext
;
389 /* Create new hotkey */
390 pHotKey
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOT_KEY
), USERTAG_HOTKEY
);
394 pHotKey
->pWnd
= pWnd
;
395 pHotKey
->id
= IDHK_REACTOS
; // Don't care, these hot keys are unrelated to the hot keys set by RegisterHotKey
396 pHotKey
->pNext
= gphkFirst
;
400 /* A window can only have one hot key. If the window already has a
401 hot key associated with it, the new hot key replaces the old one. */
403 pHotKey
->fsModifiers
= fsModifiers
;
409 *pLink
= pHotKey
->pNext
;
410 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
418 UserRegisterHotKey(PWND pWnd
,
424 PTHREADINFO pHotKeyThread
;
426 /* Find hotkey thread */
427 if (pWnd
== NULL
|| pWnd
== PWND_BOTTOM
)
429 pHotKeyThread
= PsGetCurrentThreadWin32Thread();
433 pHotKeyThread
= pWnd
->head
.pti
;
436 /* Check for existing hotkey */
437 if (IsHotKey(fsModifiers
, vk
))
439 EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED
);
440 WARN("Hotkey already exists\n");
444 /* Create new hotkey */
445 pHotKey
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOT_KEY
), USERTAG_HOTKEY
);
448 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
452 pHotKey
->pti
= pHotKeyThread
;
453 pHotKey
->pWnd
= pWnd
;
454 pHotKey
->fsModifiers
= fsModifiers
;
458 /* Insert hotkey to the global list */
459 pHotKey
->pNext
= gphkFirst
;
466 UserUnregisterHotKey(PWND pWnd
, int id
)
468 PHOT_KEY pHotKey
= gphkFirst
, phkNext
, *pLink
= &gphkFirst
;
473 /* Save next ptr for later use */
474 phkNext
= pHotKey
->pNext
;
476 /* Should we delete this hotkey? */
477 if (pHotKey
->pWnd
== pWnd
&& pHotKey
->id
== id
)
479 /* Update next ptr for previous hotkey and free memory */
481 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
485 else /* This hotkey will stay, use its next ptr */
486 pLink
= &pHotKey
->pNext
;
488 /* Move to the next entry */
495 /* SYSCALLS *****************************************************************/
499 NtUserRegisterHotKey(HWND hWnd
,
506 PTHREADINFO pHotKeyThread
;
509 TRACE("Enter NtUserRegisterHotKey\n");
511 if (fsModifiers
& ~(MOD_ALT
|MOD_CONTROL
|MOD_SHIFT
|MOD_WIN
)) // FIXME: Does Win2k3 support MOD_NOREPEAT?
513 WARN("Invalid modifiers: %x\n", fsModifiers
);
514 EngSetLastError(ERROR_INVALID_FLAGS
);
518 UserEnterExclusive();
520 /* Find hotkey thread */
523 pHotKeyThread
= gptiCurrent
;
527 pWnd
= UserGetWindowObject(hWnd
);
531 pHotKeyThread
= pWnd
->head
.pti
;
533 /* Fix wine msg "Window on another thread" test_hotkey */
534 if (pWnd
->head
.pti
!= gptiCurrent
)
536 EngSetLastError(ERROR_WINDOW_OF_OTHER_THREAD
);
537 WARN("Must be from the same Thread.\n");
542 /* Check for existing hotkey */
543 if (IsHotKey(fsModifiers
, vk
))
545 EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED
);
546 WARN("Hotkey already exists\n");
550 /* Create new hotkey */
551 pHotKey
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOT_KEY
), USERTAG_HOTKEY
);
554 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
558 pHotKey
->pti
= pHotKeyThread
;
559 pHotKey
->pWnd
= pWnd
;
560 pHotKey
->fsModifiers
= fsModifiers
;
564 /* Insert hotkey to the global list */
565 pHotKey
->pNext
= gphkFirst
;
571 TRACE("Leave NtUserRegisterHotKey, ret=%i\n", bRet
);
578 NtUserUnregisterHotKey(HWND hWnd
, int id
)
580 PHOT_KEY pHotKey
= gphkFirst
, phkNext
, *pLink
= &gphkFirst
;
584 TRACE("Enter NtUserUnregisterHotKey\n");
585 UserEnterExclusive();
587 /* Fail if given window is invalid */
588 if (hWnd
&& !(pWnd
= UserGetWindowObject(hWnd
)))
593 /* Save next ptr for later use */
594 phkNext
= pHotKey
->pNext
;
596 /* Should we delete this hotkey? */
597 if (pHotKey
->pWnd
== pWnd
&& pHotKey
->id
== id
)
599 /* Update next ptr for previous hotkey and free memory */
601 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
605 else /* This hotkey will stay, use its next ptr */
606 pLink
= &pHotKey
->pNext
;
608 /* Move to the next entry */
613 TRACE("Leave NtUserUnregisterHotKey, ret=%i\n", bRet
);