1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window accelerator
5 * FILE: win32ss/user/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
UserGetAccelObject(HACCEL hAccel)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
co_IntTranslateAccelerator(PWND Window,CONST MSG * pMsg,CONST ACCEL * pAccel)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 = UserHMGetHandle(Window);
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 = UserHMGetHandle(MenuObject);
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 = UserHMGetHandle(MenuObject);
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
NtUserCopyAcceleratorTable(HACCEL hAccel,LPACCEL Entries,ULONG EntriesCount)171 NtUserCopyAcceleratorTable(
172 HACCEL hAccel,
173 LPACCEL Entries,
174 ULONG EntriesCount)
175 {
176 PACCELERATOR_TABLE Accel;
177 ULONG Ret = 0;
178
179 TRACE("Enter NtUserCopyAcceleratorTable\n");
180 UserEnterShared();
181
182 Accel = UserGetAccelObject(hAccel);
183 if (!Accel)
184 {
185 goto Exit; // Return 0
186 }
187
188 /* If Entries is NULL return table size */
189 if (!Entries)
190 {
191 Ret = Accel->Count;
192 goto Exit;
193 }
194
195 /* Don't overrun */
196 if (Accel->Count < EntriesCount)
197 EntriesCount = Accel->Count;
198
199 _SEH2_TRY
200 {
201 ProbeForWrite(Entries, EntriesCount*sizeof(Entries[0]), 4);
202
203 for (Ret = 0; Ret < EntriesCount; Ret++)
204 {
205 Entries[Ret].fVirt = Accel->Table[Ret].fVirt;
206 Entries[Ret].key = Accel->Table[Ret].key;
207 Entries[Ret].cmd = Accel->Table[Ret].cmd;
208 }
209 }
210 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
211 {
212 SetLastNtError(_SEH2_GetExceptionCode());
213 Ret = 0;
214 }
215 _SEH2_END;
216
217 Exit:
218 TRACE("Leave NtUserCopyAcceleratorTable, ret=%lu\n", Ret);
219 UserLeave();
220 return Ret;
221 }
222
223 HACCEL
224 APIENTRY
NtUserCreateAcceleratorTable(LPACCEL Entries,ULONG EntriesCount)225 NtUserCreateAcceleratorTable(
226 LPACCEL Entries,
227 ULONG EntriesCount)
228 {
229 PACCELERATOR_TABLE Accel;
230 HACCEL hAccel;
231 ULONG Index;
232 NTSTATUS Status = STATUS_SUCCESS;
233 HACCEL Ret = NULL;
234 PTHREADINFO pti;
235
236 TRACE("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u)\n",
237 Entries, EntriesCount);
238 UserEnterExclusive();
239
240 if (!Entries || EntriesCount <= 0)
241 {
242 SetLastNtError(STATUS_INVALID_PARAMETER);
243 goto Exit; // Return NULL
244 }
245
246 pti = PsGetCurrentThreadWin32Thread();
247
248 Accel = UserCreateObject(gHandleTable,
249 pti->rpdesk,
250 pti,
251 (PHANDLE)&hAccel,
252 TYPE_ACCELTABLE,
253 sizeof(ACCELERATOR_TABLE));
254
255 if (Accel == NULL)
256 {
257 SetLastNtError(STATUS_NO_MEMORY);
258 goto Exit; // Return NULL
259 }
260
261 Accel->Count = EntriesCount;
262 Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), USERTAG_ACCEL);
263 if (Accel->Table == NULL)
264 {
265 UserDereferenceObject(Accel);
266 UserDeleteObject(hAccel, TYPE_ACCELTABLE);
267 SetLastNtError(STATUS_NO_MEMORY);
268 goto Exit; // Return NULL
269 }
270
271 _SEH2_TRY
272 {
273 ProbeForRead(Entries, EntriesCount * sizeof(ACCEL), 4);
274
275 for (Index = 0; Index < EntriesCount; Index++)
276 {
277 Accel->Table[Index].fVirt = Entries[Index].fVirt & FVIRT_MASK;
278 if(Accel->Table[Index].fVirt & FVIRTKEY)
279 {
280 Accel->Table[Index].key = Entries[Index].key;
281 }
282 else
283 {
284 RtlMultiByteToUnicodeN(&Accel->Table[Index].key,
285 sizeof(WCHAR),
286 NULL,
287 (PCSTR)&Entries[Index].key,
288 sizeof(CHAR));
289 }
290
291 Accel->Table[Index].cmd = Entries[Index].cmd;
292 }
293 }
294 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
295 {
296 Status = _SEH2_GetExceptionCode();
297 }
298 _SEH2_END;
299
300 if (!NT_SUCCESS(Status))
301 {
302 ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
303 UserDereferenceObject(Accel);
304 UserDeleteObject(hAccel, TYPE_ACCELTABLE);
305 SetLastNtError(Status);
306 goto Exit; // Return NULL
307 }
308
309 /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
310
311 /* Release the extra reference (UserCreateObject added 2 references) */
312 UserDereferenceObject(Accel);
313
314 Ret = hAccel;
315
316 Exit:
317 TRACE("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u) = %p\n",
318 Entries, EntriesCount, Ret);
319 UserLeave();
320 return Ret;
321 }
322
323 BOOLEAN
UserDestroyAccelTable(PVOID Object)324 UserDestroyAccelTable(PVOID Object)
325 {
326 PACCELERATOR_TABLE Accel = Object;
327
328 if (Accel->Table != NULL)
329 {
330 ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
331 Accel->Table = NULL;
332 }
333
334 UserDeleteObject(UserHMGetHandle(Accel), TYPE_ACCELTABLE);
335 return TRUE;
336 }
337
338 BOOLEAN
339 APIENTRY
NtUserDestroyAcceleratorTable(HACCEL hAccel)340 NtUserDestroyAcceleratorTable(
341 HACCEL hAccel)
342 {
343 PACCELERATOR_TABLE Accel;
344 BOOLEAN Ret = FALSE;
345
346 /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's
347 usage count (and return TRUE).
348 FIXME: Destroy only tables created using CreateAcceleratorTable.
349 */
350
351 TRACE("NtUserDestroyAcceleratorTable(Table %p)\n", hAccel);
352 UserEnterExclusive();
353
354 Accel = UserGetAccelObject(hAccel);
355 if (Accel)
356 {
357 Ret = UserDestroyAccelTable(Accel);
358 }
359
360 TRACE("Leave NtUserDestroyAcceleratorTable(Table %p) = %u\n", hAccel, Ret);
361 UserLeave();
362 return Ret;
363 }
364
365 int
366 APIENTRY
NtUserTranslateAccelerator(HWND hWnd,HACCEL hAccel,LPMSG pUnsafeMessage)367 NtUserTranslateAccelerator(
368 HWND hWnd,
369 HACCEL hAccel,
370 LPMSG pUnsafeMessage)
371 {
372 PWND Window = NULL;
373 PACCELERATOR_TABLE Accel = NULL;
374 ULONG i;
375 MSG Message;
376 USER_REFERENCE_ENTRY AccelRef, WindowRef;
377 int Ret = 0;
378
379 TRACE("NtUserTranslateAccelerator(hWnd %p, hAccel %p, Message %p)\n",
380 hWnd, hAccel, pUnsafeMessage);
381 UserEnterShared();
382
383 if (hWnd == NULL)
384 {
385 goto Cleanup; // Return 0
386 }
387
388 _SEH2_TRY
389 {
390 ProbeForRead(pUnsafeMessage, sizeof(MSG), 4);
391 RtlCopyMemory(&Message, pUnsafeMessage, sizeof(MSG));
392 }
393 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
394 {
395 SetLastNtError(_SEH2_GetExceptionCode());
396 _SEH2_YIELD(goto Cleanup); // Return 0
397 }
398 _SEH2_END;
399
400 if ((Message.message != WM_KEYDOWN) &&
401 (Message.message != WM_SYSKEYDOWN) &&
402 (Message.message != WM_SYSCHAR) &&
403 (Message.message != WM_CHAR))
404 {
405 goto Cleanup; // Return 0
406 }
407
408 Accel = UserGetAccelObject(hAccel);
409 if (!Accel)
410 {
411 goto Cleanup; // Return 0
412 }
413
414 UserRefObjectCo(Accel, &AccelRef);
415
416 Window = UserGetWindowObject(hWnd);
417 if (!Window)
418 {
419 goto Cleanup; // Return 0
420 }
421
422 UserRefObjectCo(Window, &WindowRef);
423
424 /* FIXME: Associate AcceleratorTable with the current thread */
425
426 for (i = 0; i < Accel->Count; i++)
427 {
428 if (co_IntTranslateAccelerator(Window, &Message, &Accel->Table[i]))
429 {
430 Ret = 1;
431 break;
432 }
433
434 /* Undocumented feature... */
435 if (Accel->Table[i].fVirt & FVIRT_TBL_END)
436 break;
437 }
438
439 Cleanup:
440 if (Window) UserDerefObjectCo(Window);
441 if (Accel) UserDerefObjectCo(Accel);
442
443 TRACE("NtUserTranslateAccelerator returns %d\n", Ret);
444 UserLeave();
445 return Ret;
446 }
447