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 BOOL bWinHotkeyActive
= FALSE
;
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 ERR("Start up the debugger hotkeys!! Should see this once!\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
;
184 if (wVk
== VK_SHIFT
|| wVk
== VK_CONTROL
|| wVk
== VK_MENU
||
185 wVk
== VK_LWIN
|| wVk
== VK_RWIN
)
187 /* Those keys are specified by modifiers */
191 fModifiers
= IntGetModifiers(gafAsyncKeyState
);
193 /* Check if it is a hotkey */
194 pHotKey
= IsHotKey(fModifiers
, wVk
);
198 TRACE("Hot key pressed (pWnd %p, id %d)\n", pHotKey
->pWnd
, pHotKey
->id
);
200 /* FIXME: See comment about "UserDebuggerHotKey" on top of this file. */
201 if (pHotKey
->id
== IDHK_SHIFTF12
|| pHotKey
->id
== IDHK_F12
)
205 ERR("Hot key pressed for Debug Activation! ShiftF12 = %d or F12 = %d\n",pHotKey
->id
== IDHK_SHIFTF12
, pHotKey
->id
== IDHK_F12
);
206 //DoNotPostMsg = co_ActivateDebugger(); // FIXME
211 /* Process hotkey if it is key up event */
214 /* WIN and F12 keys are not hardcoded here. See comments on top of this file. */
215 if (pHotKey
->id
== IDHK_WINKEY
&& bWinHotkeyActive
== TRUE
)
217 pWnd
= ValidateHwndNoErr(InputWindowStation
->ShellWindow
);
220 TRACE("System Hot key Id %d Key %d\n",pHotKey
->id
, wVk
);
221 UserPostMessage(UserHMGetHandle(pWnd
), WM_SYSCOMMAND
, SC_TASKLIST
, 0);
222 //ptiLastInput = pWnd->head.pti;
223 bWinHotkeyActive
= FALSE
;
229 { /* The user pressed the win key */
230 if (pHotKey
->id
== IDHK_WINKEY
)
232 bWinHotkeyActive
= TRUE
;
241 TRACE("UPTM Hot key Id %d Key %d\n",pHotKey
->id
, wVk
);
242 UserPostThreadMessage(pHotKey
->pti
, WM_HOTKEY
, pHotKey
->id
, MAKELONG(fModifiers
, wVk
));
243 //ptiLastInput = pHotKey->pti;
244 return TRUE
; /* Don't send any message */
248 if (pHotKey
->pWnd
== PWND_BOTTOM
)
250 if (gpqForeground
!= NULL
)
252 pWnd
= gpqForeground
->spwndFocus
;
259 pWnd
= pHotKey
->pWnd
;
262 { // pWnd->head.rpdesk->pDeskInfo->spwndShell needs testing.
263 if (pWnd
== ValidateHwndNoErr(InputWindowStation
->ShellWindow
) && pHotKey
->id
== SC_TASKLIST
)
265 ERR("Sending to shell window w/o IDHK_WINKEY..\n");
266 UserPostMessage(UserHMGetHandle(pWnd
), WM_SYSCOMMAND
, SC_TASKLIST
, 0);
270 TRACE("UPM Hot key Id %d Key %d\n",pHotKey
->id
, wVk
);
271 UserPostMessage(UserHMGetHandle(pWnd
), WM_HOTKEY
, pHotKey
->id
, MAKELONG(fModifiers
, wVk
));
273 //ptiLastInput = pWnd->head.pti;
274 return TRUE
; /* Don't send any message */
286 * GetHotKey message support
289 DefWndGetHotKey(PWND pWnd
)
291 PHOT_KEY pHotKey
= gphkFirst
;
293 WARN("DefWndGetHotKey\n");
297 if (pHotKey
->pWnd
== pWnd
&& pHotKey
->id
== IDHK_REACTOS
)
299 /* We have found it */
300 return MAKELONG(pHotKey
->vk
, pHotKey
->fsModifiers
);
303 /* Move to the next entry */
304 pHotKey
= pHotKey
->pNext
;
313 * SetHotKey message support
316 DefWndSetHotKey(PWND pWnd
, WPARAM wParam
)
318 UINT fsModifiers
, vk
;
319 PHOT_KEY pHotKey
, *pLink
;
322 WARN("DefWndSetHotKey wParam 0x%x\n", wParam
);
324 // A hot key cannot be associated with a child window.
325 if (pWnd
->style
& WS_CHILD
)
328 // VK_ESCAPE, VK_SPACE, and VK_TAB are invalid hot keys.
329 if (LOWORD(wParam
) == VK_ESCAPE
||
330 LOWORD(wParam
) == VK_SPACE
||
331 LOWORD(wParam
) == VK_TAB
)
337 fsModifiers
= HIWORD(wParam
);
344 if (pHotKey
->fsModifiers
== fsModifiers
&&
346 pHotKey
->id
== IDHK_REACTOS
)
348 if (pHotKey
->pWnd
!= pWnd
)
349 iRet
= 2; // Another window already has the same hot key.
353 /* Move to the next entry */
354 pHotKey
= pHotKey
->pNext
;
362 if (pHotKey
->pWnd
== pWnd
&&
363 pHotKey
->id
== IDHK_REACTOS
)
365 /* This window has already hotkey registered */
369 /* Move to the next entry */
370 pLink
= &pHotKey
->pNext
;
371 pHotKey
= pHotKey
->pNext
;
378 /* Create new hotkey */
379 pHotKey
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOT_KEY
), USERTAG_HOTKEY
);
383 pHotKey
->pWnd
= pWnd
;
384 pHotKey
->id
= IDHK_REACTOS
; // Don't care, these hot keys are unrelated to the hot keys set by RegisterHotKey
385 pHotKey
->pNext
= gphkFirst
;
389 /* A window can only have one hot key. If the window already has a
390 hot key associated with it, the new hot key replaces the old one. */
392 pHotKey
->fsModifiers
= fsModifiers
;
398 *pLink
= pHotKey
->pNext
;
399 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
407 UserRegisterHotKey(PWND pWnd
,
413 PTHREADINFO pHotKeyThread
;
415 /* Find hotkey thread */
416 if (pWnd
== NULL
|| pWnd
== PWND_BOTTOM
)
418 pHotKeyThread
= PsGetCurrentThreadWin32Thread();
422 pHotKeyThread
= pWnd
->head
.pti
;
425 /* Check for existing hotkey */
426 if (IsHotKey(fsModifiers
, vk
))
428 EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED
);
429 WARN("Hotkey already exists\n");
433 /* Create new hotkey */
434 pHotKey
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOT_KEY
), USERTAG_HOTKEY
);
437 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
441 pHotKey
->pti
= pHotKeyThread
;
442 pHotKey
->pWnd
= pWnd
;
443 pHotKey
->fsModifiers
= fsModifiers
;
447 /* Insert hotkey to the global list */
448 pHotKey
->pNext
= gphkFirst
;
455 UserUnregisterHotKey(PWND pWnd
, int id
)
457 PHOT_KEY pHotKey
= gphkFirst
, phkNext
, *pLink
= &gphkFirst
;
462 /* Save next ptr for later use */
463 phkNext
= pHotKey
->pNext
;
465 /* Should we delete this hotkey? */
466 if (pHotKey
->pWnd
== pWnd
&& pHotKey
->id
== id
)
468 /* Update next ptr for previous hotkey and free memory */
470 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
474 else /* This hotkey will stay, use its next ptr */
475 pLink
= &pHotKey
->pNext
;
477 /* Move to the next entry */
484 /* SYSCALLS *****************************************************************/
488 NtUserRegisterHotKey(HWND hWnd
,
495 PTHREADINFO pHotKeyThread
;
498 TRACE("Enter NtUserRegisterHotKey\n");
500 if (fsModifiers
& ~(MOD_ALT
|MOD_CONTROL
|MOD_SHIFT
|MOD_WIN
)) // FIXME: Does Win2k3 support MOD_NOREPEAT?
502 WARN("Invalid modifiers: %x\n", fsModifiers
);
503 EngSetLastError(ERROR_INVALID_FLAGS
);
507 UserEnterExclusive();
509 /* Find hotkey thread */
512 pHotKeyThread
= gptiCurrent
;
516 pWnd
= UserGetWindowObject(hWnd
);
520 pHotKeyThread
= pWnd
->head
.pti
;
522 /* Fix wine msg "Window on another thread" test_hotkey */
523 if (pWnd
->head
.pti
!= gptiCurrent
)
525 EngSetLastError(ERROR_WINDOW_OF_OTHER_THREAD
);
526 WARN("Must be from the same Thread.\n");
531 /* Check for existing hotkey */
532 if (IsHotKey(fsModifiers
, vk
))
534 EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED
);
535 WARN("Hotkey already exists\n");
539 /* Create new hotkey */
540 pHotKey
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOT_KEY
), USERTAG_HOTKEY
);
543 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
547 pHotKey
->pti
= pHotKeyThread
;
548 pHotKey
->pWnd
= pWnd
;
549 pHotKey
->fsModifiers
= fsModifiers
;
553 /* Insert hotkey to the global list */
554 pHotKey
->pNext
= gphkFirst
;
560 TRACE("Leave NtUserRegisterHotKey, ret=%i\n", bRet
);
567 NtUserUnregisterHotKey(HWND hWnd
, int id
)
569 PHOT_KEY pHotKey
= gphkFirst
, phkNext
, *pLink
= &gphkFirst
;
573 TRACE("Enter NtUserUnregisterHotKey\n");
574 UserEnterExclusive();
576 /* Fail if given window is invalid */
577 if (hWnd
&& !(pWnd
= UserGetWindowObject(hWnd
)))
582 /* Save next ptr for later use */
583 phkNext
= pHotKey
->pNext
;
585 /* Should we delete this hotkey? */
586 if (pHotKey
->pWnd
== pWnd
&& pHotKey
->id
== id
)
588 /* Update next ptr for previous hotkey and free memory */
590 ExFreePoolWithTag(pHotKey
, USERTAG_HOTKEY
);
594 else /* This hotkey will stay, use its next ptr */
595 pLink
= &pHotKey
->pNext
;
597 /* Move to the next entry */
602 TRACE("Leave NtUserUnregisterHotKey, ret=%i\n", bRet
);