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