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