[REACTOS]
[reactos.git] / reactos / win32ss / user / ntuser / accelerator.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window accelerator
5 * FILE: subsystems/win32/win32k/ntuser/accelerator.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Copyright 1993 Martin Ayotte
8 * Copyright 1994 Alexandre Julliard
9 * Copyright 1997 Morten Welinder
10 * Copyright 2011 Rafal Harabien
11 */
12
13 #include <win32k.h>
14 DBG_DEFAULT_CHANNEL(UserAccel);
15
16 #define FVIRT_TBL_END 0x80
17 #define FVIRT_MASK 0x7F
18
19 /* FUNCTIONS *****************************************************************/
20
21 PACCELERATOR_TABLE FASTCALL UserGetAccelObject(HACCEL hAccel)
22 {
23 PACCELERATOR_TABLE Accel;
24
25 if (!hAccel)
26 {
27 EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
28 return NULL;
29 }
30
31 Accel = UserGetObject(gHandleTable, hAccel, TYPE_ACCELTABLE);
32 if (!Accel)
33 {
34 EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
35 return NULL;
36 }
37
38 return Accel;
39 }
40
41
42 static
43 BOOLEAN FASTCALL
44 co_IntTranslateAccelerator(
45 PWND Window,
46 CONST MSG *pMsg,
47 CONST ACCEL *pAccel)
48 {
49 BOOL bFound = FALSE;
50 UINT Mask = 0, nPos;
51 HWND hWnd;
52 HMENU hMenu, hSubMenu;
53 PMENU_OBJECT MenuObject, SubMenu;
54 PMENU_ITEM MenuItem;
55
56 ASSERT_REFS_CO(Window);
57
58 hWnd = Window->head.h;
59
60 TRACE("IntTranslateAccelerator(hwnd %p, message %x, wParam %x, lParam %x, fVirt 0x%x, key %x, cmd %x)\n",
61 hWnd, pMsg->message, pMsg->wParam, pMsg->lParam, pAccel->fVirt, pAccel->key, pAccel->cmd);
62
63 if (UserGetKeyState(VK_CONTROL) & 0x8000) Mask |= FCONTROL;
64 if (UserGetKeyState(VK_MENU) & 0x8000) Mask |= FALT; // FIXME: VK_LMENU (msg winetest!)
65 if (UserGetKeyState(VK_SHIFT) & 0x8000) Mask |= FSHIFT;
66 TRACE("Mask 0x%x\n", Mask);
67
68 if (pAccel->fVirt & FVIRTKEY)
69 {
70 /* This is a virtual key. Process WM_(SYS)KEYDOWN messages. */
71 if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN)
72 {
73 /* Check virtual key and SHIFT, CTRL, LALT state */
74 if (pMsg->wParam == pAccel->key && Mask == (pAccel->fVirt & (FSHIFT | FCONTROL | FALT)))
75 {
76 bFound = TRUE;
77 }
78 }
79 }
80 else
81 {
82 /* This is a char code. Process WM_(SYS)CHAR messages. */
83 if (pMsg->message == WM_CHAR || pMsg->message == WM_SYSCHAR)
84 {
85 /* Check char code and LALT state only */
86 if (pMsg->wParam == pAccel->key && (Mask & FALT) == (pAccel->fVirt & FALT))
87 {
88 bFound = TRUE;
89 }
90 }
91 }
92
93 if (!bFound)
94 {
95 /* Don't translate this msg */
96 TRACE("IntTranslateAccelerator returns FALSE\n");
97 return FALSE;
98 }
99
100 /* Check if accelerator is associated with menu command */
101 hMenu = (Window->style & WS_CHILD) ? 0 : (HMENU)Window->IDMenu;
102 hSubMenu = NULL;
103 MenuObject = IntGetMenuObject(hMenu);
104 if (MenuObject)
105 {
106 nPos = IntGetMenuItemByFlag(MenuObject,
107 pAccel->cmd,
108 MF_BYCOMMAND,
109 &SubMenu,
110 &MenuItem,
111 NULL);
112 if (nPos != (UINT) - 1)
113 hSubMenu = SubMenu->head.h;
114 else
115 hMenu = NULL;
116 }
117 if (!hMenu)
118 {
119 /* Check system menu now */
120 hMenu = Window->SystemMenu;
121 hSubMenu = hMenu; /* system menu is a popup menu */
122 MenuObject = IntGetMenuObject(hMenu);
123 if (MenuObject)
124 {
125 nPos = IntGetMenuItemByFlag(MenuObject,
126 pAccel->cmd,
127 MF_BYCOMMAND,
128 &SubMenu,
129 &MenuItem,
130 NULL);
131 if (nPos != (UINT) - 1)
132 hSubMenu = SubMenu->head.h;
133 else
134 hMenu = NULL;
135 }
136 }
137
138 /* If this is a menu item, there is no capturing enabled and
139 window is not disabled, send WM_INITMENU */
140 if (hMenu && !IntGetCaptureWindow())
141 {
142 co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
143 if (hSubMenu)
144 {
145 nPos = IntFindSubMenu(&hMenu, hSubMenu);
146 TRACE("hSysMenu = %p, hSubMenu = %p, nPos = %u\n", hMenu, hSubMenu, nPos);
147 co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
148 }
149 }
150
151 /* Don't send any message if:
152 - window is disabled
153 - menu item is disabled
154 - this is window menu and window is minimized */
155 if (!(Window->style & WS_DISABLED) &&
156 !(hMenu && IntGetMenuState(hMenu, pAccel->cmd, MF_BYCOMMAND) & (MF_DISABLED | MF_GRAYED)) &&
157 !(hMenu && hMenu == (HMENU)Window->IDMenu && (Window->style & WS_MINIMIZED)))
158 {
159 /* If this is system menu item, send WM_SYSCOMMAND, otherwise send WM_COMMAND */
160 if (hMenu && hMenu == Window->SystemMenu)
161 {
162 TRACE("Sending WM_SYSCOMMAND, wParam=%0x\n", pAccel->cmd);
163 co_IntSendMessage(hWnd, WM_SYSCOMMAND, pAccel->cmd, 0x00010000L);
164 }
165 else
166 {
167 TRACE("Sending WM_COMMAND, wParam=%0x\n", 0x10000 | pAccel->cmd);
168 co_IntSendMessage(hWnd, WM_COMMAND, 0x10000 | pAccel->cmd, 0L);
169 }
170 }
171
172 TRACE("IntTranslateAccelerator returns TRUE\n");
173 return TRUE;
174 }
175
176
177 /* SYSCALLS *****************************************************************/
178
179
180 ULONG
181 APIENTRY
182 NtUserCopyAcceleratorTable(
183 HACCEL hAccel,
184 LPACCEL Entries,
185 ULONG EntriesCount)
186 {
187 PACCELERATOR_TABLE Accel;
188 ULONG Ret;
189 DECLARE_RETURN(int);
190
191 TRACE("Enter NtUserCopyAcceleratorTable\n");
192 UserEnterShared();
193
194 Accel = UserGetAccelObject(hAccel);
195 if (!Accel)
196 {
197 RETURN(0);
198 }
199
200 /* If Entries is NULL return table size */
201 if (!Entries)
202 {
203 RETURN(Accel->Count);
204 }
205
206 /* Don't overrun */
207 if (Accel->Count < EntriesCount)
208 EntriesCount = Accel->Count;
209
210 Ret = 0;
211
212 _SEH2_TRY
213 {
214 ProbeForWrite(Entries, EntriesCount*sizeof(Entries[0]), 4);
215
216 for (Ret = 0; Ret < EntriesCount; Ret++)
217 {
218 Entries[Ret].fVirt = Accel->Table[Ret].fVirt;
219 Entries[Ret].key = Accel->Table[Ret].key;
220 Entries[Ret].cmd = Accel->Table[Ret].cmd;
221 }
222 }
223 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
224 {
225 SetLastNtError(_SEH2_GetExceptionCode());
226 Ret = 0;
227 }
228 _SEH2_END;
229
230 RETURN(Ret);
231
232 CLEANUP:
233 TRACE("Leave NtUserCopyAcceleratorTable, ret=%i\n", _ret_);
234 UserLeave();
235 END_CLEANUP;
236 }
237
238 HACCEL
239 APIENTRY
240 NtUserCreateAcceleratorTable(
241 LPACCEL Entries,
242 ULONG EntriesCount)
243 {
244 PACCELERATOR_TABLE Accel;
245 HACCEL hAccel;
246 ULONG Index;
247 NTSTATUS Status = STATUS_SUCCESS;
248 DECLARE_RETURN(HACCEL);
249
250 TRACE("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u)\n",
251 Entries, EntriesCount);
252 UserEnterExclusive();
253
254 if (!Entries || EntriesCount <= 0)
255 {
256 SetLastNtError(STATUS_INVALID_PARAMETER);
257 RETURN( (HACCEL) NULL );
258 }
259
260 Accel = UserCreateObject(gHandleTable, NULL, NULL, (PHANDLE)&hAccel, TYPE_ACCELTABLE, sizeof(ACCELERATOR_TABLE));
261
262 if (Accel == NULL)
263 {
264 SetLastNtError(STATUS_NO_MEMORY);
265 RETURN( (HACCEL) NULL );
266 }
267
268 Accel->Count = EntriesCount;
269 Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), USERTAG_ACCEL);
270 if (Accel->Table == NULL)
271 {
272 UserDereferenceObject(Accel);
273 UserDeleteObject(hAccel, TYPE_ACCELTABLE);
274 SetLastNtError(STATUS_NO_MEMORY);
275 RETURN( (HACCEL) NULL);
276 }
277
278 _SEH2_TRY
279 {
280 ProbeForRead(Entries, EntriesCount * sizeof(ACCEL), 4);
281
282 for (Index = 0; Index < EntriesCount; Index++)
283 {
284 Accel->Table[Index].fVirt = Entries[Index].fVirt & FVIRT_MASK;
285 if(Accel->Table[Index].fVirt & FVIRTKEY)
286 {
287 Accel->Table[Index].key = Entries[Index].key;
288 }
289 else
290 {
291 RtlMultiByteToUnicodeN(&Accel->Table[Index].key,
292 sizeof(WCHAR),
293 NULL,
294 (PCSTR)&Entries[Index].key,
295 sizeof(CHAR));
296 }
297
298 Accel->Table[Index].cmd = Entries[Index].cmd;
299 }
300 }
301 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
302 {
303 Status = _SEH2_GetExceptionCode();
304 }
305 _SEH2_END;
306
307 if (!NT_SUCCESS(Status))
308 {
309 ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
310 UserDereferenceObject(Accel);
311 UserDeleteObject(hAccel, TYPE_ACCELTABLE);
312 SetLastNtError(Status);
313 RETURN( (HACCEL) NULL);
314 }
315
316 /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
317
318 RETURN(hAccel);
319
320 CLEANUP:
321 TRACE("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u) = %p\n",
322 Entries, EntriesCount, _ret_);
323 UserLeave();
324 END_CLEANUP;
325 }
326
327 BOOLEAN
328 APIENTRY
329 NtUserDestroyAcceleratorTable(
330 HACCEL hAccel)
331 {
332 PACCELERATOR_TABLE Accel;
333 DECLARE_RETURN(BOOLEAN);
334
335 /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's
336 usage count (and return TRUE).
337 FIXME: Destroy only tables created using CreateAcceleratorTable.
338 */
339
340 TRACE("NtUserDestroyAcceleratorTable(Table %p)\n", hAccel);
341 UserEnterExclusive();
342
343 if (!(Accel = UserGetAccelObject(hAccel)))
344 {
345 RETURN( FALSE);
346 }
347
348 if (Accel->Table != NULL)
349 {
350 ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
351 Accel->Table = NULL;
352 }
353
354 UserDeleteObject(hAccel, TYPE_ACCELTABLE);
355
356 RETURN( TRUE);
357
358 CLEANUP:
359 TRACE("Leave NtUserDestroyAcceleratorTable(Table %p) = %u\n", hAccel, _ret_);
360 UserLeave();
361 END_CLEANUP;
362 }
363
364 int
365 APIENTRY
366 NtUserTranslateAccelerator(
367 HWND hWnd,
368 HACCEL hAccel,
369 LPMSG pUnsafeMessage)
370 {
371 PWND Window = NULL;
372 PACCELERATOR_TABLE Accel = NULL;
373 ULONG i;
374 MSG Message;
375 USER_REFERENCE_ENTRY AccelRef, WindowRef;
376 DECLARE_RETURN(int);
377
378 TRACE("NtUserTranslateAccelerator(hWnd %p, hAccel %p, Message %p)\n",
379 hWnd, hAccel, pUnsafeMessage);
380 UserEnterShared();
381
382 if (hWnd == NULL)
383 {
384 RETURN( 0);
385 }
386
387 _SEH2_TRY
388 {
389 ProbeForRead(pUnsafeMessage, sizeof(MSG), 4);
390 RtlCopyMemory(&Message, pUnsafeMessage, sizeof(MSG));
391 }
392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
393 {
394 SetLastNtError(_SEH2_GetExceptionCode());
395 _SEH2_YIELD(RETURN( 0));
396 }
397 _SEH2_END;
398
399 if ((Message.message != WM_KEYDOWN) &&
400 (Message.message != WM_SYSKEYDOWN) &&
401 (Message.message != WM_SYSCHAR) &&
402 (Message.message != WM_CHAR))
403 {
404 RETURN( 0);
405 }
406
407 Accel = UserGetAccelObject(hAccel);
408 if (!Accel)
409 {
410 RETURN( 0);
411 }
412
413 UserRefObjectCo(Accel, &AccelRef);
414
415 Window = UserGetWindowObject(hWnd);
416 if (!Window)
417 {
418 RETURN( 0);
419 }
420
421 UserRefObjectCo(Window, &WindowRef);
422
423 /* FIXME: Associate AcceleratorTable with the current thread */
424
425 for (i = 0; i < Accel->Count; i++)
426 {
427 if (co_IntTranslateAccelerator(Window, &Message, &Accel->Table[i]))
428 {
429 RETURN( 1);
430 }
431
432 /* Undocumented feature... */
433 if (Accel->Table[i].fVirt & FVIRT_TBL_END)
434 break;
435 }
436
437 RETURN( 0);
438
439 CLEANUP:
440 if (Window) UserDerefObjectCo(Window);
441 if (Accel) UserDerefObjectCo(Accel);
442
443 TRACE("NtUserTranslateAccelerator returns %d\n", _ret_);
444 UserLeave();
445 END_CLEANUP;
446 }