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