0fbd6648d4062cfc6ad27f0a6b49b5f3e6b9e23e
[reactos.git] / reactos / win32ss / user / ntuser / hotkey.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: HotKey support
5 * FILE: subsystems/win32/win32k/ntuser/hotkey.c
6 * PROGRAMER: Eric Kohl
7 */
8
9 /*
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).
13 * -- Gunnar
14 */
15
16 #include <win32k.h>
17 DBG_DEFAULT_CHANNEL(UserHotkey);
18
19 /* GLOBALS *******************************************************************/
20
21 /*
22 * Hardcoded hotkeys. See http://ivanlef0u.fr/repo/windoz/VI20051005.html
23 * or http://repo.meh.or.id/Windows/VI20051005.html .
24 *
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.
32 */
33 /* thread hwnd modifiers vk id next */
34 // HOT_KEY hkF12 = {NULL, NULL, 0, VK_F12, IDHK_F12, NULL};
35 // HOT_KEY hkShiftF12 = {NULL, NULL, MOD_SHIFT, VK_F12, IDHK_SHIFTF12, &hkF12};
36 // HOT_KEY hkWinKey = {NULL, NULL, MOD_WIN, 0, IDHK_WINKEY, &hkShiftF12};
37 HOT_KEY hkWinKey = {NULL, NULL, MOD_WIN, 0, IDHK_WINKEY, NULL};
38
39 PHOT_KEY gphkFirst = &hkWinKey;
40 BOOL bWinHotkeyActive = FALSE;
41
42 /* FUNCTIONS *****************************************************************/
43
44 /*
45 * IntGetModifiers
46 *
47 * Returns a value that indicates if the key is a modifier key, and
48 * which one.
49 */
50 static
51 UINT FASTCALL
52 IntGetModifiers(PBYTE pKeyState)
53 {
54 UINT fModifiers = 0;
55
56 if (IS_KEY_DOWN(pKeyState, VK_SHIFT))
57 fModifiers |= MOD_SHIFT;
58
59 if (IS_KEY_DOWN(pKeyState, VK_CONTROL))
60 fModifiers |= MOD_CONTROL;
61
62 if (IS_KEY_DOWN(pKeyState, VK_MENU))
63 fModifiers |= MOD_ALT;
64
65 if (IS_KEY_DOWN(pKeyState, VK_LWIN) || IS_KEY_DOWN(pKeyState, VK_RWIN))
66 fModifiers |= MOD_WIN;
67
68 return fModifiers;
69 }
70
71 /*
72 * UnregisterWindowHotKeys
73 *
74 * Removes hotkeys registered by specified window on its cleanup
75 */
76 VOID FASTCALL
77 UnregisterWindowHotKeys(PWND pWnd)
78 {
79 PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst;
80 HWND hWnd = pWnd->head.h;
81
82 while (pHotKey)
83 {
84 /* Save next ptr for later use */
85 phkNext = pHotKey->pNext;
86
87 /* Should we delete this hotkey? */
88 if (pHotKey->hWnd == hWnd)
89 {
90 /* Update next ptr for previous hotkey and free memory */
91 *pLink = phkNext;
92 ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY);
93 }
94 else /* This hotkey will stay, use its next ptr */
95 pLink = &pHotKey->pNext;
96
97 /* Move to the next entry */
98 pHotKey = phkNext;
99 }
100 }
101
102 /*
103 * UnregisterThreadHotKeys
104 *
105 * Removes hotkeys registered by specified thread on its cleanup
106 */
107 VOID FASTCALL
108 UnregisterThreadHotKeys(struct _ETHREAD *pThread)
109 {
110 PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst;
111
112 while (pHotKey)
113 {
114 /* Save next ptr for later use */
115 phkNext = pHotKey->pNext;
116
117 /* Should we delete this hotkey? */
118 if (pHotKey->pThread == pThread)
119 {
120 /* Update next ptr for previous hotkey and free memory */
121 *pLink = phkNext;
122 ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY);
123 }
124 else /* This hotkey will stay, use its next ptr */
125 pLink = &pHotKey->pNext;
126
127 /* Move to the next entry */
128 pHotKey = phkNext;
129 }
130 }
131
132 /*
133 * IsHotKey
134 *
135 * Checks if given key and modificators have corresponding hotkey
136 */
137 static PHOT_KEY FASTCALL
138 IsHotKey(UINT fsModifiers, WORD wVk)
139 {
140 PHOT_KEY pHotKey = gphkFirst;
141
142 while (pHotKey)
143 {
144 if (pHotKey->fsModifiers == fsModifiers &&
145 pHotKey->vk == wVk)
146 {
147 /* We have found it */
148 return pHotKey;
149 }
150
151 /* Move to the next entry */
152 pHotKey = pHotKey->pNext;
153 }
154
155 return NULL;
156 }
157
158 /*
159 * co_UserProcessHotKeys
160 *
161 * Sends WM_HOTKEY message if given keys are hotkey
162 */
163 BOOL NTAPI
164 co_UserProcessHotKeys(WORD wVk, BOOL bIsDown)
165 {
166 UINT fModifiers;
167 PHOT_KEY pHotKey;
168
169 if (wVk == VK_SHIFT || wVk == VK_CONTROL || wVk == VK_MENU ||
170 wVk == VK_LWIN || wVk == VK_RWIN)
171 {
172 /* Those keys are specified by modifiers */
173 wVk = 0;
174 }
175
176 /* Check if it is a hotkey */
177 fModifiers = IntGetModifiers(gafAsyncKeyState);
178 pHotKey = IsHotKey(fModifiers, wVk);
179 if (pHotKey)
180 {
181 /* Process hotkey if it is key up event */
182 if (!bIsDown)
183 {
184 TRACE("Hot key pressed (hWnd %p, id %d)\n", pHotKey->hWnd, pHotKey->id);
185
186 /* WIN and F12 keys are hardcoded here. See comments on top of this file. */
187 if (pHotKey == &hkWinKey)
188 {
189 if(bWinHotkeyActive == TRUE)
190 {
191 UserPostMessage(InputWindowStation->ShellWindow, WM_SYSCOMMAND, SC_TASKLIST, 0);
192 bWinHotkeyActive = FALSE;
193 }
194 }
195 #if 0 /* FIXME: See comment about "UserDebuggerHotKey" on top of this file. */
196 else if (pHotKey == &hkF12 || pHotKey == &hkShiftF12)
197 {
198 //co_ActivateDebugger(); // FIXME
199 }
200 #endif
201 else if (pHotKey->id == IDHK_REACTOS && !pHotKey->pThread) // FIXME: Those hotkeys doesn't depend on RegisterHotKey
202 {
203 UserPostMessage(pHotKey->hWnd, WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)pHotKey->hWnd);
204 }
205 else
206 {
207 /* If a hotkey with the WIN modifier was activated, do not treat the release of the WIN key as a hotkey*/
208 if((pHotKey->fsModifiers & MOD_WIN) != 0)
209 bWinHotkeyActive = FALSE;
210
211 MsqPostHotKeyMessage(pHotKey->pThread,
212 pHotKey->hWnd,
213 (WPARAM)pHotKey->id,
214 MAKELPARAM((WORD)fModifiers, wVk));
215 }
216 }
217 else
218 {
219 if (pHotKey == &hkWinKey)
220 {
221 /* The user pressed the win key */
222 bWinHotkeyActive = TRUE;
223 }
224 }
225
226 return TRUE; /* Don't send any message */
227 }
228
229 return FALSE;
230 }
231
232
233 /*
234 * DefWndGetHotKey
235 *
236 * GetHotKey message support
237 */
238 UINT FASTCALL
239 DefWndGetHotKey(HWND hWnd)
240 {
241 PHOT_KEY pHotKey = gphkFirst;
242
243 WARN("DefWndGetHotKey\n");
244
245 while (pHotKey)
246 {
247 if (pHotKey->hWnd == hWnd && pHotKey->id == IDHK_REACTOS)
248 {
249 /* We have found it */
250 return MAKELONG(pHotKey->vk, pHotKey->fsModifiers);
251 }
252
253 /* Move to the next entry */
254 pHotKey = pHotKey->pNext;
255 }
256
257 return 0;
258 }
259
260 /*
261 * DefWndSetHotKey
262 *
263 * SetHotKey message support
264 */
265 INT FASTCALL
266 DefWndSetHotKey(PWND pWnd, WPARAM wParam)
267 {
268 UINT fsModifiers, vk;
269 PHOT_KEY pHotKey, *pLink;
270 HWND hWnd;
271 INT iRet = 1;
272
273 WARN("DefWndSetHotKey wParam 0x%x\n", wParam);
274
275 // A hot key cannot be associated with a child window.
276 if (pWnd->style & WS_CHILD)
277 return 0;
278
279 // VK_ESCAPE, VK_SPACE, and VK_TAB are invalid hot keys.
280 if (LOWORD(wParam) == VK_ESCAPE ||
281 LOWORD(wParam) == VK_SPACE ||
282 LOWORD(wParam) == VK_TAB)
283 {
284 return -1;
285 }
286
287 vk = LOWORD(wParam);
288 fsModifiers = HIWORD(wParam);
289 hWnd = UserHMGetHandle(pWnd);
290
291 if (wParam)
292 {
293 pHotKey = gphkFirst;
294 while (pHotKey)
295 {
296 if (pHotKey->fsModifiers == fsModifiers &&
297 pHotKey->vk == vk &&
298 pHotKey->id == IDHK_REACTOS)
299 {
300 if (pHotKey->hWnd != hWnd)
301 iRet = 2; // Another window already has the same hot key.
302 break;
303 }
304
305 /* Move to the next entry */
306 pHotKey = pHotKey->pNext;
307 }
308 }
309
310 pHotKey = gphkFirst;
311 pLink = &gphkFirst;
312 while (pHotKey)
313 {
314 if (pHotKey->hWnd == hWnd &&
315 pHotKey->id == IDHK_REACTOS)
316 {
317 /* This window has already hotkey registered */
318 break;
319 }
320
321 /* Move to the next entry */
322 pLink = &pHotKey->pNext;
323 pHotKey = pHotKey->pNext;
324 }
325
326 if (wParam)
327 {
328 if (!pHotKey)
329 {
330 /* Create new hotkey */
331 pHotKey = ExAllocatePoolWithTag(PagedPool, sizeof(HOT_KEY), USERTAG_HOTKEY);
332 if (pHotKey == NULL)
333 return 0;
334
335 pHotKey->hWnd = hWnd;
336 pHotKey->id = IDHK_REACTOS; // Don't care, these hot keys are unrelated to the hot keys set by RegisterHotKey
337 pHotKey->pNext = gphkFirst;
338 gphkFirst = pHotKey;
339 }
340
341 /* A window can only have one hot key. If the window already has a
342 hot key associated with it, the new hot key replaces the old one. */
343 pHotKey->pThread = NULL;
344 pHotKey->fsModifiers = fsModifiers;
345 pHotKey->vk = vk;
346 }
347 else if (pHotKey)
348 {
349 /* Remove hotkey */
350 *pLink = pHotKey->pNext;
351 ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY);
352 }
353
354 return iRet;
355 }
356
357 /* SYSCALLS *****************************************************************/
358
359
360 BOOL APIENTRY
361 NtUserRegisterHotKey(HWND hWnd,
362 int id,
363 UINT fsModifiers,
364 UINT vk)
365 {
366 PHOT_KEY pHotKey;
367 PWND pWnd;
368 PETHREAD pHotKeyThread;
369 BOOL bRet = FALSE;
370
371 TRACE("Enter NtUserRegisterHotKey\n");
372
373 if (fsModifiers & ~(MOD_ALT|MOD_CONTROL|MOD_SHIFT|MOD_WIN)) // FIXME: Does Win2k3 support MOD_NOREPEAT?
374 {
375 WARN("Invalid modifiers: %x\n", fsModifiers);
376 EngSetLastError(ERROR_INVALID_FLAGS);
377 return 0;
378 }
379
380 UserEnterExclusive();
381
382 /* Find hotkey thread */
383 if (hWnd == NULL)
384 {
385 pHotKeyThread = PsGetCurrentThread();
386 }
387 else
388 {
389 pWnd = UserGetWindowObject(hWnd);
390 if (!pWnd)
391 goto cleanup;
392
393 pHotKeyThread = pWnd->head.pti->pEThread;
394 }
395
396 /* Check for existing hotkey */
397 if (IsHotKey(fsModifiers, vk))
398 {
399 EngSetLastError(ERROR_HOTKEY_ALREADY_REGISTERED);
400 WARN("Hotkey already exists\n");
401 goto cleanup;
402 }
403
404 /* Create new hotkey */
405 pHotKey = ExAllocatePoolWithTag(PagedPool, sizeof(HOT_KEY), USERTAG_HOTKEY);
406 if (pHotKey == NULL)
407 {
408 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
409 goto cleanup;
410 }
411
412 pHotKey->pThread = pHotKeyThread;
413 pHotKey->hWnd = hWnd;
414 pHotKey->fsModifiers = fsModifiers;
415 pHotKey->vk = vk;
416 pHotKey->id = id;
417
418 /* Insert hotkey to the global list */
419 pHotKey->pNext = gphkFirst;
420 gphkFirst = pHotKey;
421
422 bRet = TRUE;
423
424 cleanup:
425 TRACE("Leave NtUserRegisterHotKey, ret=%i\n", bRet);
426 UserLeave();
427 return bRet;
428 }
429
430
431 BOOL APIENTRY
432 NtUserUnregisterHotKey(HWND hWnd, int id)
433 {
434 PHOT_KEY pHotKey = gphkFirst, phkNext, *pLink = &gphkFirst;
435 BOOL bRet = FALSE;
436
437 TRACE("Enter NtUserUnregisterHotKey\n");
438 UserEnterExclusive();
439
440 /* Fail if given window is invalid */
441 if (hWnd && !UserGetWindowObject(hWnd))
442 goto cleanup;
443
444 while (pHotKey)
445 {
446 /* Save next ptr for later use */
447 phkNext = pHotKey->pNext;
448
449 /* Should we delete this hotkey? */
450 if (pHotKey->hWnd == hWnd && pHotKey->id == id)
451 {
452 /* Update next ptr for previous hotkey and free memory */
453 *pLink = phkNext;
454 ExFreePoolWithTag(pHotKey, USERTAG_HOTKEY);
455
456 bRet = TRUE;
457 }
458 else /* This hotkey will stay, use its next ptr */
459 pLink = &pHotKey->pNext;
460
461 /* Move to the next entry */
462 pHotKey = phkNext;
463 }
464
465 cleanup:
466 TRACE("Leave NtUserUnregisterHotKey, ret=%i\n", bRet);
467 UserLeave();
468 return bRet;
469 }
470
471 /* EOF */